from __future__ import annotations

import tkinter as tk
from datetime import datetime, timezone
from tkinter import messagebox
from typing import Dict

from gui.theme import COLORS


class Chart(tk.Canvas):
    def __init__(self, parent, height=150):
        super().__init__(parent, height=height, background=COLORS["panel"], highlightthickness=1,
                         highlightbackground=COLORS["border"])
        self._resize_job = None
        self.bind("<Configure>", self._queue_redraw)

    def _queue_redraw(self, _event=None):
        if self._resize_job is not None:
            self.after_cancel(self._resize_job)
        self._resize_job = self.after(40, self._resize_redraw)

    def _resize_redraw(self):
        self._resize_job = None
        self.redraw()

    def title(self, text):
        self.create_text(14, 12, anchor="nw", text=text.upper(), fill=COLORS["text"], font=("Segoe UI Semibold", 10))

    def redraw(self): pass


class ThreatGauge(Chart):
    def __init__(self, parent):
        self.score = 0
        self.level = "LOW"
        self.factors = []
        super().__init__(parent, 118)
        self.bind("<Button-1>", self._show_factors)

    def set_result(self, result):
        self.score = max(0, min(100, int(result.get("score", 0))))
        self.level = str(result.get("level") or "LOW")
        self.factors = list(result.get("factors") or [])
        self.redraw()

    def redraw(self):
        self.delete("all"); self.title("Threat level")
        width, height = max(180, self.winfo_width()), max(100, self.winfo_height())
        cx, cy = width / 2, 34 + (height - 34) / 2
        radius = max(24, min(53, width / 3.3, (height - 42) / 2))
        bbox = (cx-radius, cy-radius, cx+radius, cy+radius)
        for start, extent, color in ((0, 45, COLORS["critical"]), (45, 45, COLORS["high"]),
                                     (90, 45, COLORS["warn"]), (135, 45, COLORS["good"])):
            self.create_arc(*bbox, start=start, extent=extent, style="arc", width=12, outline=color)
        color = COLORS["good"] if self.score < 20 else COLORS["accent"] if self.score < 40 else COLORS["warn"] if self.score < 60 else COLORS["high"] if self.score < 80 else COLORS["critical"]
        self.create_text(cx, cy-10, text=self.level, fill=color, font=("Segoe UI Semibold", 14))
        self.create_text(cx, cy+11, text=f"{self.score} / 100", fill=COLORS["muted"], font=("Segoe UI", 9))

    def _show_factors(self, _event=None):
        if not self.factors:
            messagebox.showinfo("Threat score factors", "No actionable findings are contributing to the current score.")
            return
        lines = [f"{item['contribution']} pts | {item['rule_id']} | {item['process']}\n{item['reason']}" for item in self.factors]
        messagebox.showinfo("Threat score factors", "\n\n".join(lines[:12]))


class DonutChart(Chart):
    def __init__(self, parent):
        self.values: Dict[str, int] = {}
        super().__init__(parent, 175)

    def set_values(self, values): self.values = dict(values); self.redraw()

    def redraw(self):
        self.delete("all"); self.title("Process classification")
        colors = {"Trusted": COLORS["good"], "Normal": COLORS["accent"], "Watchlisted": COLORS["warn"],
                  "High risk": COLORS["high"], "Unknown": COLORS["muted"]}
        width, height = max(240, self.winfo_width()), max(150, self.winfo_height()); total = sum(self.values.values())
        compact = width < 250
        cx, cy = (47 if compact else min(76, width * .27)), 34 + (height - 34) / 2
        radius = max(30, min(48, width * .18, (height - 48) / 2)); start = 90
        if total:
            for name, count in self.values.items():
                extent = -(count / total * 360)
                self.create_arc(cx-radius, cy-radius, cx+radius, cy+radius, start=start, extent=extent,
                                fill=colors.get(name, COLORS["muted"]), outline=COLORS["panel"])
                start += extent
            self.create_oval(cx-28, cy-28, cx+28, cy+28, fill=COLORS["panel"], outline=COLORS["panel"])
        else:
            self.create_oval(cx-radius, cy-radius, cx+radius, cy+radius, outline=COLORS["border"], width=10)
        self.create_text(cx, cy-5, text=str(total), fill=COLORS["text"], font=("Segoe UI Semibold", 16))
        self.create_text(cx, cy+14, text="ACTIVE", fill=COLORS["muted"], font=("Segoe UI", 8))
        legend_x, y = (91 if compact else max(138, width * .52)), max(42, cy - 48)
        for name in ("Trusted", "Normal", "Watchlisted", "High risk", "Unknown"):
            count = self.values.get(name, 0); pct = int(count / total * 100) if total else 0
            self.create_rectangle(legend_x, y, legend_x+10, y+10, fill=colors[name], outline="")
            label = f"{name}: {count}" if compact else f"{name}: {count} ({pct}%)"
            self.create_text(legend_x+16, y+5, anchor="w", text=label,
                             fill=COLORS["text"], font=("Segoe UI", 7 if compact else 8)); y += 21


class TimelineChart(Chart):
    def __init__(self, parent):
        self.events, self.alerts = [], []
        super().__init__(parent, 175)

    def set_data(self, events, alerts):
        self.events, self.alerts = list(events), list(alerts)
        self.redraw()

    def redraw(self):
        self.delete("all"); self.title("Activity timeline - last 30 minutes")
        width, height = max(280, self.winfo_width()), max(150, self.winfo_height())
        left, top, right, bottom = 36, 39, width-14, height-24; now = datetime.now(timezone.utc)
        events, alerts = self._buckets(self.events, now), self._buckets(self.alerts, now)
        maximum = max(5, max(events, default=0), max(alerts, default=0))
        for index in range(5):
            y = top + (bottom-top) * index / 4
            self.create_line(left, y, right, y, fill="#203049")
            self.create_text(left-6, y, anchor="e", text=str(round(maximum*(4-index)/4)), fill=COLORS["muted"], font=("Segoe UI", 7))
        self._series(events, maximum, left, top, right, bottom, COLORS["good"], True)
        if any(alerts): self._series(alerts, maximum, left, top, right, bottom, COLORS["critical"], False)
        self.create_text(left, bottom+12, anchor="w", text="-30m", fill=COLORS["muted"], font=("Segoe UI", 7))
        self.create_text(right, bottom+12, anchor="e", text="now", fill=COLORS["muted"], font=("Segoe UI", 7))
        self.create_line(right-98, 18, right-86, 18, fill=COLORS["good"], width=2)
        self.create_text(right-80, 18, anchor="w", text="Events", fill=COLORS["muted"], font=("Segoe UI", 7))
        if any(alerts):
            self.create_line(right-43, 18, right-31, 18, fill=COLORS["critical"], width=2)
            self.create_text(right-27, 18, anchor="w", text="Alerts", fill=COLORS["muted"], font=("Segoe UI", 7))

    @staticmethod
    def _buckets(items, now):
        buckets = [0] * 30
        for item in items:
            try:
                stamp = datetime.fromisoformat(str(item.get("timestamp", "")).replace("Z", "+00:00"))
                age = int((now-stamp.astimezone(timezone.utc)).total_seconds() // 60)
                if 0 <= age < 30: buckets[29-age] += 1
            except (ValueError, TypeError): pass
        return buckets

    def _series(self, buckets, maximum, left, top, right, bottom, color, area):
        points = []
        for index, value in enumerate(buckets):
            x = left + (right-left) * index / 29; y = bottom - (bottom-top) * value / maximum
            points.extend((x, y))
        if area: self.create_polygon(*([left, bottom] + points + [right, bottom]), fill="#063a31", outline="")
        self.create_line(*points, fill=color, width=2, smooth=True)
        for index in range(0, len(points), 10):
            self.create_oval(points[index]-2, points[index+1]-2, points[index]+2, points[index+1]+2, fill=color, outline="")
