from __future__ import annotations

import json
import tkinter as tk
from tkinter import ttk

from gui.adapters import format_timestamp, metadata_summary
from gui.charts import DonutChart, ThreatGauge, TimelineChart
from gui.theme import COLORS
from gui.widgets import Card, DataTable, Page


class OverviewPage(Page):
    def __init__(self, parent, action_callback=None):
        super().__init__(parent, "Security Overview", "Live defensive monitoring posture")
        self.action_callback = action_callback
        self.auto_log = tk.BooleanVar(value=True)
        self._build_status_row()
        self._build_metric_row()
        self._build_middle_row()
        self._build_bottom_row()
        self.rowconfigure(4, weight=3)
        self.rowconfigure(5, weight=2)

    def _build_status_row(self):
        frame = ttk.Frame(self); frame.grid(row=2, column=0, sticky="ew")
        for column in range(4): frame.columnconfigure(column, weight=1, uniform="status")
        self.status_cards = {}
        for column, name in enumerate(("ProcessMonitor Driver", "Telemetry Relay", "Monitoring Health", "Last Update")):
            card = Card(frame, name, compact=True); card.grid(row=0, column=column, sticky="nsew", padx=4, pady=3)
            self.status_cards[name] = card

    def _build_metric_row(self):
        frame = ttk.Frame(self); frame.grid(row=3, column=0, sticky="ew", pady=(8, 0))
        for column in range(6): frame.columnconfigure(column, weight=1, uniform="metrics")
        self.metric_cards = {}
        for column, name in enumerate(("Total Events", "Active Processes", "Total Alerts", "High-Risk Alerts", "DB Event Count")):
            card = Card(frame, name, compact=True); card.grid(row=0, column=column, sticky="nsew", padx=4)
            self.metric_cards[name] = card
        self.gauge = ThreatGauge(frame); self.gauge.grid(row=0, column=5, sticky="nsew", padx=4)

    def _build_middle_row(self):
        frame = ttk.Frame(self); frame.grid(row=4, column=0, sticky="nsew", pady=(10, 0))
        frame.columnconfigure(0, weight=5, uniform="middle"); frame.columnconfigure(1, weight=3, uniform="middle")
        frame.columnconfigure(2, weight=5, uniform="middle"); frame.rowconfigure(1, weight=1)
        self._section_title(frame, "RECENT EVENTS", 0)
        self.events_table = DataTable(frame, [("timestamp", "Time", 90), ("event", "Event", 120), ("pid", "PID", 55),
                                              ("process", "Process", 115), ("severity", "Severity", 70), ("source", "Source", 80)], 6)
        self.events_table.grid(row=1, column=0, sticky="nsew", padx=(0, 5))
        self.donut = DonutChart(frame); self.donut.grid(row=0, column=1, rowspan=2, sticky="nsew", padx=5)
        self.timeline = TimelineChart(frame); self.timeline.grid(row=0, column=2, rowspan=2, sticky="nsew", padx=(5, 0))

    def _build_bottom_row(self):
        frame = ttk.Frame(self); frame.grid(row=5, column=0, sticky="nsew", pady=(10, 0))
        frame.columnconfigure(0, weight=4, uniform="bottom"); frame.columnconfigure(1, weight=3, uniform="bottom")
        frame.columnconfigure(2, weight=5, uniform="bottom"); frame.rowconfigure(1, weight=1)
        self._section_title(frame, "ALERTS", 0)
        self.alerts_table = DataTable(frame, [("severity", "Severity", 70), ("timestamp", "Time", 80),
                                              ("process", "Process", 105), ("rule", "Rule", 90), ("reason", "Reason", 150)], 4)
        self.alerts_table.grid(row=1, column=0, sticky="nsew", padx=(0, 5))
        relay = ttk.Frame(frame, style="Panel.TFrame", padding=12); relay.grid(row=0, column=1, rowspan=2, sticky="nsew", padx=5)
        ttk.Label(relay, text="DRIVER & RELAY STATUS", style="Panel.TLabel", font=("Segoe UI Semibold", 10)).grid(row=0, column=0, columnspan=2, sticky="w", pady=(0, 7))
        relay.columnconfigure(1, weight=1); self.relay_values = {}
        for row, label in enumerate(("Driver", "Relay", "PID", "Poll", "Output", "Database"), 1):
            ttk.Label(relay, text=label, style="PanelMuted.TLabel").grid(row=row, column=0, sticky="nw", padx=(0, 8), pady=2)
            value = ttk.Label(relay, text="N/A", style="Panel.TLabel", wraplength=230); value.grid(row=row, column=1, sticky="w", pady=2)
            self.relay_values[label] = value
        if self.action_callback:
            controls = ttk.Frame(relay, style="Panel.TFrame"); controls.grid(row=7, column=0, columnspan=2, sticky="w", pady=(7, 0))
            ttk.Button(controls, text="Start", style="Success.TButton", command=lambda: self.action_callback("start")).pack(side="left")
            ttk.Button(controls, text="Stop", style="Danger.TButton", command=lambda: self.action_callback("stop")).pack(side="left", padx=5)
            ttk.Button(controls, text="Refresh", command=lambda: self.action_callback("refresh")).pack(side="left")
        log_panel = ttk.Frame(frame, style="Panel.TFrame", padding=(10, 8)); log_panel.grid(row=0, column=2, rowspan=2, sticky="nsew", padx=(5, 0))
        log_panel.columnconfigure(0, weight=1); log_panel.rowconfigure(1, weight=1)
        heading = ttk.Frame(log_panel, style="Panel.TFrame"); heading.grid(row=0, column=0, sticky="ew", pady=(0, 5)); heading.columnconfigure(0, weight=1)
        ttk.Label(heading, text="LIVE LOG", style="Panel.TLabel", font=("Segoe UI Semibold", 10)).grid(row=0, column=0, sticky="w")
        ttk.Checkbutton(heading, text="Auto-scroll", variable=self.auto_log, style="Panel.TCheckbutton").grid(row=0, column=1, sticky="e")
        self.live_log = tk.Text(log_panel, height=5, background="#030914", foreground=COLORS["text"], insertbackground=COLORS["text"],
                                relief="flat", borderwidth=0, font=("Cascadia Mono", 8), state="disabled", wrap="none")
        self.live_log.grid(row=1, column=0, sticky="nsew")
        for severity, color in (("info", COLORS["good"]), ("low", COLORS["good"]), ("medium", COLORS["warn"]),
                                ("high", COLORS["high"]), ("critical", COLORS["critical"])):
            self.live_log.tag_configure(severity, foreground=color)

    @staticmethod
    def _section_title(parent, text, column):
        ttk.Label(parent, text=text, foreground=COLORS["text"], font=("Segoe UI Semibold", 10)).grid(row=0, column=column, sticky="w", padx=8, pady=(0, 5))

    def refresh(self, snapshot):
        metrics, relay, driver = snapshot["metrics"], snapshot["relay"], snapshot["driver"]
        health = "HEALTHY" if driver.get("running") and relay.get("running") and snapshot["jsonl_ok"] else "DEGRADED"
        self.status_cards["ProcessMonitor Driver"].set(str(driver["state"]).upper(), self._state_color(driver.get("running"), driver.get("error")))
        self.status_cards["Telemetry Relay"].set("RUNNING" if relay["running"] else "STOPPED", self._state_color(relay.get("running"), relay.get("error")))
        self.status_cards["Monitoring Health"].set(health, COLORS["good"] if health == "HEALTHY" else COLORS["warn"])
        self.status_cards["Last Update"].set(format_timestamp(metrics["latest_event_timestamp"]), COLORS["accent"])
        for name, value in {"Total Events": metrics["event_count"], "Active Processes": metrics["active_process_count"],
                            "Total Alerts": metrics["alert_count"], "High-Risk Alerts": metrics["high_risk_count"],
                            "DB Event Count": metrics["event_count"]}.items(): self.metric_cards[name].set(value)
        event_rows = [{"timestamp": self._time(item.get("timestamp")), "event": item.get("event_type", "Unknown"),
                       "pid": item.get("pid") or "N/A", "process": item.get("process_name") or "Unknown",
                       "severity": item.get("severity") or "info", "source": item.get("source") or "Unknown"} for item in snapshot["events"][:10]]
        self.events_table.set_rows(event_rows)
        alert_rows = [{"severity": item.get("severity") or "info", "timestamp": self._time(item.get("timestamp")),
                       "process": item.get("process_name") or item.get("title") or "Unknown",
                       "rule": item.get("rule_id") or "N/A", "reason": item.get("description") or item.get("event_type") or "Observed"} for item in snapshot["alerts"][:8]]
        self.alerts_table.set_rows(alert_rows)
        classes = self._classifications(snapshot); self.donut.set_values(classes)
        self.timeline.set_data(snapshot["events"], snapshot["alerts"])
        self.gauge.set_result(snapshot.get("threat", {"score": 0, "level": "LOW", "factors": []}))
        values = {"Driver": driver.get("state") or "Unknown", "Relay": "Running" if relay["running"] else "Stopped",
                  "PID": relay.get("pid") or "N/A", "Poll": f"{relay['poll_interval']} seconds",
                  "Output": relay.get("output_path") or "N/A", "Database": snapshot.get("database_path") or "N/A"}
        for key, value in values.items(): self.relay_values[key].configure(text=value)
        self._update_log(snapshot["jsonl_events"])

    def _update_log(self, events):
        self.live_log.configure(state="normal"); self.live_log.delete("1.0", "end")
        for item in reversed(events[:8]):
            severity = str(item.get("severity") or "info").lower(); summary = item.get("event") or "event"
            process = item.get("image") or item.get("process_name"); pid = item.get("pid")
            suffix = f" | {process}" if process else ""; suffix += f" | PID {pid}" if pid else ""
            self.live_log.insert("end", f"[{self._time(item.get('timestamp'))}] ")
            self.live_log.insert("end", f"{severity.upper():8}", severity if severity in {"info", "low", "medium", "high", "critical"} else "info")
            self.live_log.insert("end", f" {summary}{suffix}\n")
        if self.auto_log.get(): self.live_log.see("end")
        self.live_log.configure(state="disabled")

    def _classifications(self, snapshot):
        result = {"Trusted": 0, "Normal": 0, "Watchlisted": 0, "High risk": 0, "Unknown": 0}
        for item in snapshot["processes"]:
            category = str(item.get("classification") or "UNKNOWN").upper()
            label = {"TRUSTED": "Trusted", "NORMAL": "Normal", "WATCHLISTED": "Watchlisted",
                     "HIGH_RISK": "High risk", "UNKNOWN": "Unknown"}.get(category, "Unknown")
            result[label] += 1
        return result

    @staticmethod
    def _time(value):
        rendered = format_timestamp(value); return rendered.rsplit(" ", 1)[-1] if rendered != "N/A" else rendered

    @staticmethod
    def _state_color(running, error):
        if error: return COLORS["critical"]
        return COLORS["good"] if running else COLORS["muted"]
