"""
att_monitor.py
--------------
GUI monitor for the ATT Query Runner.
Launches att_worker.py as a subprocess and monitors its progress
by reading the results file it writes.

HOW TO RUN:
  python "C:\\Users\\public\\att_monitor.py"
  Keep window visible for best performance.
"""

import os
import sys
import json
import subprocess
from pathlib import Path
from datetime import datetime
from time import perf_counter

import tkinter as tk
from tkinter import ttk

# ─────────────────────────────────────────────
# CONFIG — must match att_worker.py
# ─────────────────────────────────────────────

OUTPUT_ROOT  = r"D:\KinderMorgan\complete"
RESULTS_FILE = r"D:\KinderMorgan\complete\_results.jsonl"
STATUS_FILE  = r"D:\KinderMorgan\complete\_status.json"
GUI_REFRESH  = 2000

WORKER_SCRIPT = str(Path(__file__).parent / "km_worker.py")

# ─────────────────────────────────────────────
# GUI
# ─────────────────────────────────────────────

class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Kinder Morgan Query Tool")
        self.configure(bg="#1e1e2e")
        self.geometry("1000x700")
        self.resizable(True, True)

        self.total         = 0
        self.done          = 0
        self.success       = 0
        self.failed        = 0
        self.active        = {}
        self.path_ids_done = set()
        self.timing        = {}
        self._results_pos  = 0
        self._run_start    = perf_counter()

        self._build_ui()
        self._start_worker()
        self.after(GUI_REFRESH, self._refresh)

    def _build_ui(self):
        BG   = "#1e1e2e"
        HDR  = "#313244"
        FG   = "#cdd6f4"
        GRN  = "#a6e3a1"
        RED  = "#f38ba8"
        YLW  = "#f9e2af"
        MONO = ("Consolas", 10)
        BOLD = ("Consolas", 11, "bold")
        BIG  = ("Consolas", 14, "bold")

        tk.Label(self, text="Kinder Morgan Query Tool",
                 bg=BG, fg=FG, font=BIG).pack(pady=(12,0))

        pf = tk.Frame(self, bg=BG)
        pf.pack(fill="x", padx=20, pady=(8,0))

        self.status_label = tk.Label(pf, text="Starting worker...",
                                     bg=BG, fg=YLW, font=MONO)
        self.status_label.pack(anchor="w")

        style = ttk.Style(self)
        style.theme_use("clam")
        style.configure("TProgressbar", troughcolor=HDR,
                        background=GRN, thickness=18)

        tk.Label(pf, text="Queries", bg=BG, fg=FG,
                 font=MONO).pack(anchor="w")
        self.pb_q = ttk.Progressbar(pf, maximum=1,
                                    mode="determinate", length=960)
        self.pb_q.pack(fill="x", pady=(2,0))

        tk.Label(pf, text="Path IDs Complete", bg=BG, fg=FG,
                 font=MONO).pack(anchor="w", pady=(6,0))
        self.pb_p = ttk.Progressbar(pf, maximum=1,
                                    mode="determinate", length=960)
        self.pb_p.pack(fill="x", pady=(2,0))

        tk.Label(self, text="Active Queries",
                 bg=BG, fg=FG, font=BOLD).pack(
                 anchor="w", padx=20, pady=(14,2))

        cols = ("Path ID", "Query", "Elapsed")
        self.tree = ttk.Treeview(self, columns=cols, show="headings",
                                 height=8, selectmode="none")
        style.configure("Treeview", background=HDR, foreground=FG,
                        fieldbackground=HDR, rowheight=24, font=MONO)
        style.configure("Treeview.Heading", background=BG,
                        foreground=YLW, font=("Consolas",10,"bold"))
        style.map("Treeview", background=[("selected", HDR)])
        for c, w in [("Path ID",420),("Query",100),("Elapsed",120)]:
            self.tree.heading(c, text=c)
            self.tree.column(c, width=w, anchor="w")
        self.tree.pack(fill="x", padx=20)

        tk.Label(self, text="Completed",
                 bg=BG, fg=FG, font=BOLD).pack(
                 anchor="w", padx=20, pady=(14,2))

        lf = tk.Frame(self, bg=HDR)
        lf.pack(fill="both", expand=True, padx=20, pady=(0,8))
        self.log = tk.Text(lf, bg=HDR, fg=GRN, font=MONO,
                           state="disabled", wrap="none",
                           relief="flat", borderwidth=0)
        sb = tk.Scrollbar(lf, command=self.log.yview)
        self.log.configure(yscrollcommand=sb.set)
        sb.pack(side="right", fill="y")
        self.log.pack(fill="both", expand=True, padx=6, pady=6)
        self.log.tag_config("ok",  foreground=GRN)
        self.log.tag_config("err", foreground=RED)
        self.log.tag_config("sep", foreground=YLW)

        tk.Button(self, text="Stop",
                  command=self._stop,
                  bg=RED, fg="#1e1e2e",
                  font=("Consolas",10,"bold"),
                  relief="flat", padx=12, pady=6).pack(pady=(0,12))

    def _log(self, text, tag="ok"):
        self.log.config(state="normal")
        self.log.insert("end", text+"\n", tag)
        self.log.see("end")
        self.log.config(state="disabled")

    def _start_worker(self):
        os.makedirs(OUTPUT_ROOT, exist_ok=True)
        for f in [RESULTS_FILE, STATUS_FILE]:
            try: Path(f).unlink()
            except: pass

        self._worker_proc = subprocess.Popen(
            [sys.executable, WORKER_SCRIPT],
            creationflags=subprocess.CREATE_NO_WINDOW
            if sys.platform == "win32" else 0,
        )

    def _read_results(self):
        try:
            with open(RESULTS_FILE, "r", encoding="utf-8") as f:
                f.seek(self._results_pos)
                new_lines = f.readlines()
                self._results_pos = f.tell()
            return [json.loads(l) for l in new_lines if l.strip()]
        except Exception:
            return []

    def _refresh(self):
        now = perf_counter()

        for msg in self._read_results():
            if msg.get("started"):
                key = f"{msg['path_id']}/{msg['query_name']}"
                self.active[key] = perf_counter()

            elif msg.get("all_done"):
                self._on_done(msg.get("timing_summary",""))
                return

            else:
                pid   = msg["path_id"]
                qn    = msg["query_name"]
                key   = f"{pid}/{qn}"
                el    = msg["elapsed"]
                err   = msg.get("error")
                rows  = msg.get("rows", 0)
                total = msg.get("total", self.total)

                self.total = total
                self.active.pop(key, None)
                self.timing[(pid,qn)] = el if not err else None

                if err:
                    self.failed += 1
                    self._log(f"X  {key:<40}  {_fmt(el)}  {err}", "err")
                else:
                    self.success += 1
                    self._log(f"   {key:<40}  {_fmt(el)}  ({rows} rows)")

                self.done += 1

                all_q = {"A_TER","A_INT","B_TER","B_INT"}
                done_q = {q for (p,q) in self.timing if p==pid}
                if all_q.issubset(done_q):
                    self.path_ids_done.add(pid)
                    wall = _fmt(perf_counter() - self._run_start)
                    self._log(f"== {pid:<40}  COMPLETE  {wall}", "sep")

        if self.total > 0:
            self.pb_q["maximum"] = self.total
            self.pb_q["value"]   = self.done

        try:
            with open(STATUS_FILE) as f:
                st = json.load(f)
            n_pids = len(st.get("path_ids",[]))
            if n_pids > 0:
                self.pb_p["maximum"] = n_pids
                self.pb_p["value"]   = len(self.path_ids_done)
        except Exception:
            pass

        self.status_label.config(
            text=f"Queries: {self.done}/{self.total}  |  "
                 f"Path IDs: {len(self.path_ids_done)}  |  "
                 f"OK: {self.success}  Errors: {self.failed}"
        )

        for row in self.tree.get_children():
            self.tree.delete(row)
        for key, t0 in self.active.items():
            pid, qn = key.split("/", 1)
            self.tree.insert("", "end",
                             values=(pid, qn, _fmt(now - t0)))

        self.after(GUI_REFRESH, self._refresh)

    def _on_done(self, timing_summary):
        self.status_label.config(text="Done!")
        self._log(f"== All queries complete. Timing: {timing_summary}", "sep")

    def _stop(self):
        try:
            import psutil
            parent = psutil.Process(self._worker_proc.pid)
            for child in parent.children(recursive=True):
                child.kill()
            parent.kill()
        except Exception:
            try: self._worker_proc.kill()
            except: pass
        self.destroy()


# ─────────────────────────────────────────────
# HELPERS
# ─────────────────────────────────────────────

def _fmt(seconds):
    if seconds is None: return "ERROR"
    s = int(seconds)
    hrs, rem = divmod(s, 3600)
    mins, secs = divmod(rem, 60)
    return f"{hrs:02}:{mins:02}:{secs:02}"


# ─────────────────────────────────────────────
# MAIN
# ─────────────────────────────────────────────

if __name__ == "__main__":
    app = App()
    app.mainloop()
