"""Dark-themed usage popup window."""
from __future__ import annotations

import sys
import tkinter as tk
import webbrowser
from tkinter import font as tkfont
from typing import Callable

from api import (
    UsageSnapshot,
    days_until_reset,
    format_app_title,
    format_dollars,
    format_last_updated,
    format_reset_line,
    format_tokens,
)
from colors import percent_color
from icons import activity_photoimage
from positioning import (
    clamp_window_geometry,
    get_bottom_right_position,
    margins_from_window,
    max_window_height,
)

# Colors approximating the mockup
BG = "#15171a"
CARD = "#1c1f24"
BORDER = "#2a2f36"
TEXT = "#e8eaed"
MUTED = "#9aa0a6"
ACCENT = "#5b9cf5"
BAR_BG = "#2f3540"
LINK = "#5b9cf5"
WINDOW_WIDTH = 430
WINDOW_MIN_HEIGHT = 400
DEFAULT_MARGIN = 10
OUTER_PADX = 16
THERMO_TITLE_COL = 42
THERMO_VALUE_COL = 62
THERMO_COL_GAP = 8
THERMO_BAR_WIDTH = (
    WINDOW_WIDTH - OUTER_PADX * 2 - THERMO_TITLE_COL - THERMO_VALUE_COL - THERMO_COL_GAP
)


class ThermometerGauge(tk.Frame):
    """Horizontální teploměr — plní se zleva doprava."""

    _BAR_H = 10

    def __init__(
        self,
        master: tk.Misc,
        title: str,
        title_font: tkfont.Font,
        value_font: tkfont.Font,
        **kwargs,
    ):
        super().__init__(master, bg=BG, **kwargs)
        self._percent = 0.0
        self._fill_color = percent_color(0)

        self.columnconfigure(0, minsize=THERMO_TITLE_COL)
        self.columnconfigure(1, minsize=THERMO_VALUE_COL)

        self._title_label = tk.Label(
            self,
            text=title,
            fg=MUTED,
            bg=BG,
            font=title_font,
            anchor="e",
        )
        self._title_label.grid(row=0, column=0, sticky="e", padx=(0, THERMO_COL_GAP))

        self._value_label = tk.Label(
            self,
            text="—%",
            fg=self._fill_color,
            bg=BG,
            font=value_font,
            anchor="w",
        )
        self._value_label.grid(row=0, column=1, sticky="w", padx=(0, THERMO_COL_GAP))

        self._canvas = tk.Canvas(
            self,
            width=THERMO_BAR_WIDTH,
            height=self._BAR_H,
            bg=BG,
            highlightthickness=0,
        )
        self._canvas.grid(row=0, column=2, sticky="w")
        self._canvas.bind("<Configure>", lambda _e: self._draw())

    def set_percent(self, percent: float) -> None:
        self._percent = max(0.0, percent)
        self._fill_color = percent_color(min(self._percent, 100.0))
        self._value_label.configure(
            text=f"{self._percent:.1f}%",
            fg=self._fill_color,
        )
        self._draw()

    def _draw(self) -> None:
        self._canvas.delete("all")
        width = THERMO_BAR_WIDTH
        height = self._BAR_H

        self._canvas.create_rectangle(
            0, 0, width, height, fill=BAR_BG, outline=BORDER, width=1
        )
        ratio = min(self._percent, 100.0) / 100.0
        fill_w = max(0, int(width * ratio))
        if fill_w > 0:
            self._canvas.create_rectangle(
                1, 1, fill_w - 1, height - 1, fill=self._fill_color, outline=""
            )


class ProgressBar(tk.Canvas):
    def __init__(self, master: tk.Misc, **kwargs):
        super().__init__(
            master,
            height=6,
            bg=CARD,
            highlightthickness=0,
            **kwargs,
        )
        self._ratio = 0.0
        self._fill_color = percent_color(0)
        self.bind("<Configure>", lambda _e: self._draw())

    def set_ratio(self, ratio: float) -> None:
        self._ratio = max(ratio, 0.0)
        self._fill_color = percent_color(min(self._ratio * 100, 100.0))
        self._draw()

    def _draw(self) -> None:
        self.delete("all")
        width = self.winfo_width() or 200
        fill_width = min(width, width * min(self._ratio, 1.0))
        self.create_rectangle(0, 0, width, 6, fill=BAR_BG, outline="")
        if fill_width > 0:
            self.create_rectangle(0, 0, fill_width, 6, fill=self._fill_color, outline="")


class UsageWindow:
    def __init__(
        self,
        root: tk.Tk,
        on_refresh: Callable[[], None],
        margin_right: int = DEFAULT_MARGIN,
        margin_above_taskbar: int = DEFAULT_MARGIN,
        legacy_position: tuple[int, int] | None = None,
        on_margins_saved: Callable[[int, int], None] | None = None,
        plan_label: str | None = None,
        user_label: str | None = None,
        refresh_interval_secs: int = 60,
    ):
        self._on_refresh = on_refresh
        self._margin_right = margin_right
        self._margin_above_taskbar = margin_above_taskbar
        self._legacy_position = legacy_position
        self._on_margins_saved = on_margins_saved
        self._plan_label = plan_label
        self._user_label = user_label
        self._refresh_interval_secs = refresh_interval_secs
        self._seconds_until_refresh = refresh_interval_secs
        self._countdown_job: str | None = None
        self._root = root
        self._popup: tk.Toplevel | None = None
        self._visible = False
        self._body_labels: list[tk.Label] = []
        self._footer_label: tk.Label | None = None
        self._user_footer_label: tk.Label | None = None
        self._included_bar: ProgressBar | None = None
        self._ondemand_bar: ProgressBar | None = None
        self._included_used: tk.Label | None = None
        self._included_suffix: tk.Label | None = None
        self._ondemand_used: tk.Label | None = None
        self._ondemand_suffix: tk.Label | None = None
        self._last_updated_label: tk.Label | None = None
        self._last_updated_text: str | None = None
        self._next_refresh_label: tk.Label | None = None
        self._status_label: tk.Label | None = None
        self._title_label: tk.Label | None = None
        self._thermometers: dict[str, ThermometerGauge] = {}
        self._legacy_migrated = False

    def set_plan_label(self, plan: str | None) -> None:
        self._plan_label = plan
        title = format_app_title(plan)
        if self._title_label:
            self._title_label.configure(text=title)
        if self._popup:
            self._popup.title(title)

    def reset_refresh_countdown(self) -> None:
        self._seconds_until_refresh = self._refresh_interval_secs
        self._update_next_refresh_label()

    def _update_next_refresh_label(self) -> None:
        if self._next_refresh_label:
            self._next_refresh_label.configure(
                text=f" · next in {self._seconds_until_refresh}s"
            )

    def _tick_refresh_countdown(self) -> None:
        if self._seconds_until_refresh > 0:
            self._seconds_until_refresh -= 1
        self._update_next_refresh_label()
        self._countdown_job = self._root.after(1000, self._tick_refresh_countdown)

    def _start_refresh_countdown(self) -> None:
        if self._countdown_job:
            self._root.after_cancel(self._countdown_job)
        self._tick_refresh_countdown()

    def _ensure_window(self) -> tk.Toplevel:
        if self._popup is not None:
            return self._popup

        root = tk.Toplevel(self._root)
        root.title(format_app_title(self._plan_label))
        root.configure(bg=BG)
        root.resizable(False, False)
        root.overrideredirect(True)
        root.attributes("-topmost", True)

        # Allow dragging by header
        self._drag = {"x": 0, "y": 0}

        def start_drag(event):
            self._drag["x"] = event.x
            self._drag["y"] = event.y

        def do_drag(event):
            x = root.winfo_x() + event.x - self._drag["x"]
            y = root.winfo_y() + event.y - self._drag["y"]
            root.geometry(f"+{x}+{y}")

        def end_drag(_event):
            self._finalize_drag_position(root)

        def bind_drag(widget: tk.Misc) -> None:
            widget.bind("<Button-1>", start_drag)
            widget.bind("<B1-Motion>", do_drag)
            widget.bind("<ButtonRelease-1>", end_drag)

        outer = tk.Frame(root, bg=BG, padx=OUTER_PADX, pady=14)
        outer.pack(fill="both", expand=True)

        self._card_title_font = tkfont.Font(family="Segoe UI", size=9)
        self._card_value_font = tkfont.Font(family="Segoe UI", size=12, weight="bold")

        header = tk.Frame(outer, bg=BG)
        header.pack(fill="x", pady=(0, 12))
        bind_drag(header)

        title_font = tkfont.Font(family="Segoe UI", size=13, weight="bold")
        icon_size = title_font.metrics("ascent")
        self._header_icon = activity_photoimage(
            root, size=icon_size, color=TEXT, gap=8
        )
        self._title_label = tk.Label(
            header,
            text=format_app_title(self._plan_label),
            image=self._header_icon,
            compound="left",
            fg=TEXT,
            bg=BG,
            font=title_font,
            anchor="w",
        )
        self._title_label.pack(side="left")
        bind_drag(self._title_label)

        close_btn = tk.Label(
            header,
            text="✕",
            fg=MUTED,
            bg=BG,
            font=tkfont.Font(family="Segoe UI", size=11),
            cursor="hand2",
        )
        close_btn.pack(side="right")
        close_btn.bind("<Button-1>", lambda _e: self.hide())

        thermo_box = tk.Frame(outer, bg=BG)
        thermo_box.pack(fill="x", pady=(0, 12))
        for key, title in (("total", "Total"), ("auto", "Auto"), ("api", "API")):
            gauge = ThermometerGauge(
                thermo_box,
                title,
                title_font=self._card_title_font,
                value_font=self._card_value_font,
            )
            gauge.pack(fill="x", pady=3)
            self._thermometers[key] = gauge

        cards = tk.Frame(outer, bg=BG)
        cards.pack(fill="x", pady=(0, 14))
        cards.columnconfigure(0, weight=1, uniform="summary")
        cards.columnconfigure(1, weight=1, uniform="summary")

        self._included_used, self._included_suffix, self._included_bar = self._make_summary_card(
            cards, "Included", column=0
        )
        self._ondemand_used, self._ondemand_suffix, self._ondemand_bar = self._make_summary_card(
            cards, "On-demand", column=1
        )

        section = tk.Frame(outer, bg=BG)
        section.pack(fill="x")

        section_head = tk.Frame(section, bg=BG)
        section_head.pack(fill="x", pady=(0, 8))
        tk.Label(
            section_head,
            text="Usage by Model",
            fg=TEXT,
            bg=BG,
            font=tkfont.Font(family="Segoe UI", size=10, weight="bold"),
        ).pack(side="left")
        tk.Label(
            section_head,
            text=" (Current Billing Cycle)",
            fg=MUTED,
            bg=BG,
            font=tkfont.Font(family="Segoe UI", size=9),
        ).pack(side="left")

        table_wrap = tk.Frame(section, bg=CARD, highlightbackground=BORDER, highlightthickness=1)
        table_wrap.pack(fill="x")

        header_font = tkfont.Font(family="Segoe UI", size=9, weight="bold")
        body_font = tkfont.Font(family="Segoe UI", size=9)
        grid = tk.Frame(table_wrap, bg=CARD, padx=12, pady=8)
        grid.pack(fill="x")
        grid.columnconfigure(0, weight=4)
        grid.columnconfigure(1, weight=1)
        grid.columnconfigure(2, weight=1)
        grid.columnconfigure(3, weight=1)

        for col, text in enumerate(("Model", "Requests", "Tokens", "Spend")):
            tk.Label(
                grid,
                text=text,
                fg=MUTED,
                bg=CARD,
                font=header_font,
                anchor="w" if col == 0 else "e",
            ).grid(row=0, column=col, sticky="ew", pady=(0, 6), padx=(0, 0 if col < 3 else 2))

        self._table_grid = grid
        self._body_font = body_font
        self._body_labels = []

        meta_font = tkfont.Font(family="Segoe UI", size=9)
        meta_row = tk.Frame(section, bg=BG)
        meta_row.pack(fill="x", pady=(12, 6))

        self._footer_label = tk.Label(
            meta_row,
            text="",
            fg=MUTED,
            bg=BG,
            font=meta_font,
            anchor="w",
        )
        self._footer_label.pack(side="left", anchor="w")

        self._user_footer_label = tk.Label(
            meta_row,
            text="",
            fg=MUTED,
            bg=BG,
            font=meta_font,
            anchor="e",
        )
        self._user_footer_label.pack(side="right", anchor="e")

        footer = tk.Frame(outer, bg=BG)
        footer.pack(fill="x")

        self._status_label = tk.Label(
            footer,
            text="",
            fg=MUTED,
            bg=BG,
            font=tkfont.Font(family="Segoe UI", size=9),
            anchor="w",
        )
        self._status_label.pack(fill="x", anchor="w", pady=(0, 6))

        links = tk.Frame(footer, bg=BG)
        links.pack(fill="x", anchor="w", pady=(0, 8))
        self._make_link(links, "Open Dashboard", self._open_dashboard).pack(side="left")
        tk.Label(links, text=" | ", fg=MUTED, bg=BG).pack(side="left")
        self._make_link(links, "Refresh", self._refresh).pack(side="left")
        self._next_refresh_label = tk.Label(
            links,
            text="",
            fg=MUTED,
            bg=BG,
            font=tkfont.Font(family="Segoe UI", size=9),
            anchor="w",
        )
        self._next_refresh_label.pack(side="left")
        self._last_updated_label = tk.Label(
            links,
            text="",
            fg=MUTED,
            bg=BG,
            font=tkfont.Font(family="Segoe UI", size=9),
            anchor="w",
        )
        self._last_updated_label.pack(side="left")

        self.reset_refresh_countdown()
        self._start_refresh_countdown()

        root.update_idletasks()
        root.withdraw()
        root.protocol("WM_DELETE_WINDOW", self.hide)
        self._popup = root
        return root

    def _make_summary_card(
        self, parent: tk.Frame, title: str, column: int
    ) -> tuple[tk.Label, tk.Label, ProgressBar]:
        card = tk.Frame(
            parent,
            bg=CARD,
            highlightbackground=BORDER,
            highlightthickness=1,
            padx=12,
            pady=10,
        )
        card.grid(row=0, column=column, sticky="nsew", padx=(0, 6) if column == 0 else (6, 0))

        tk.Label(
            card,
            text=title,
            fg=MUTED,
            bg=CARD,
            font=self._card_title_font,
            anchor="w",
        ).pack(fill="x")

        value_row = tk.Frame(card, bg=CARD)
        value_row.pack(fill="x", pady=(2, 8))

        used = tk.Label(
            value_row,
            text="—",
            fg=percent_color(0),
            bg=CARD,
            font=self._card_value_font,
            anchor="w",
        )
        used.pack(side="left")

        suffix = tk.Label(
            value_row,
            text="",
            fg=TEXT,
            bg=CARD,
            font=self._card_value_font,
            anchor="w",
        )
        suffix.pack(side="left")

        bar = ProgressBar(card)
        bar.pack(fill="x")
        return used, suffix, bar

    def _update_summary_card(
        self,
        used_label: tk.Label | None,
        suffix_label: tk.Label | None,
        bar: ProgressBar | None,
        used_text: str,
        suffix_text: str,
        ratio: float,
    ) -> None:
        color = percent_color(min(ratio * 100, 100.0))
        if used_label:
            used_label.configure(text=used_text, fg=color)
        if suffix_label:
            suffix_label.configure(text=suffix_text)
        if bar:
            bar.set_ratio(ratio)

    def _make_link(self, parent: tk.Frame, text: str, command: Callable[[], None]) -> tk.Label:
        label = tk.Label(
            parent,
            text=text,
            fg=LINK,
            bg=BG,
            font=tkfont.Font(family="Segoe UI", size=9, underline=False),
            cursor="hand2",
        )
        label.bind("<Button-1>", lambda _e: command())
        label.bind("<Enter>", lambda _e: label.configure(font=tkfont.Font(family="Segoe UI", size=9, underline=True)))
        label.bind("<Leave>", lambda _e: label.configure(font=tkfont.Font(family="Segoe UI", size=9, underline=False)))
        return label

    def _open_dashboard(self) -> None:
        webbrowser.open("https://cursor.com/dashboard/usage")

    def _set_last_updated_text(self, text: str) -> None:
        self._last_updated_text = text
        if self._last_updated_label:
            self._last_updated_label.configure(text=text)

    def _refresh(self) -> None:
        if self._status_label:
            self._status_label.configure(text="Refreshing…")
        if self._last_updated_label:
            self._last_updated_label.configure(text=" · Refreshing…")
        self._on_refresh()

    def _window_height(self, root: tk.Toplevel) -> int:
        root.update_idletasks()
        return max(root.winfo_reqheight(), WINDOW_MIN_HEIGHT)

    def _finalize_drag_position(self, root: tk.Toplevel) -> None:
        """Keep the window where the user dropped it; persist as margins."""
        root.update_idletasks()
        sw = root.winfo_screenwidth()
        sh = root.winfo_screenheight()
        x, y, height = clamp_window_geometry(
            root.winfo_x(),
            root.winfo_y(),
            WINDOW_WIDTH,
            root.winfo_height(),
            screen_width=sw,
            screen_height=sh,
        )
        root.geometry(f"{WINDOW_WIDTH}x{height}+{x}+{y}")

        margin_right, margin_above = margins_from_window(
            x, y, WINDOW_WIDTH, height, sw, sh
        )
        self._margin_right = margin_right
        self._margin_above_taskbar = margin_above
        if self._on_margins_saved:
            self._on_margins_saved(margin_right, margin_above)

    def _place_window(self, root: tk.Toplevel) -> None:
        req_height = self._window_height(root)
        sw = root.winfo_screenwidth()
        sh = root.winfo_screenheight()
        height = min(req_height, max_window_height(sw, sh))

        if self._legacy_position and not self._legacy_migrated:
            x, y = self._legacy_position
            self._margin_right, self._margin_above_taskbar = margins_from_window(
                x, y, WINDOW_WIDTH, height, sw, sh
            )
            self._legacy_migrated = True
            if self._on_margins_saved:
                self._on_margins_saved(self._margin_right, self._margin_above_taskbar)
            self._legacy_position = None

        x, y = get_bottom_right_position(
            WINDOW_WIDTH,
            height,
            margin_right=self._margin_right,
            margin_above_taskbar=self._margin_above_taskbar,
            screen_width=sw,
            screen_height=sh,
        )

        x, y, height = clamp_window_geometry(
            x,
            y,
            WINDOW_WIDTH,
            height,
            screen_width=sw,
            screen_height=sh,
        )
        root.geometry(f"{WINDOW_WIDTH}x{height}+{x}+{y}")

    def show(self) -> None:
        root = self._ensure_window()
        self._place_window(root)
        root.deiconify()
        root.lift()
        if sys.platform == "linux":
            try:
                root.attributes("-type", "dialog")
            except tk.TclError:
                pass
            root.update_idletasks()
            root.focus_force()
        self._visible = True

    def hide(self) -> None:
        if self._popup:
            self._popup.withdraw()
        self._visible = False

    def toggle(self) -> None:
        if self._visible:
            self.hide()
        else:
            self.show()

    def render(self, snapshot: UsageSnapshot) -> None:
        self._ensure_window()

        for key, value in (
            ("total", snapshot.total_percent_used),
            ("auto", snapshot.auto_percent_used),
            ("api", snapshot.api_percent_used),
        ):
            if key in self._thermometers:
                self._thermometers[key].set_percent(value)

        if snapshot.included_unit == "dollars":
            included_used = format_dollars(snapshot.included_used)
            included_suffix = f" / {format_dollars(snapshot.included_limit)}"
            included_ratio = (
                snapshot.included_used / snapshot.included_limit
                if snapshot.included_limit
                else 0
            )
        else:
            included_used = str(int(snapshot.included_used))
            included_suffix = f" / {int(snapshot.included_limit)}"
            included_ratio = (
                snapshot.included_used / snapshot.included_limit
                if snapshot.included_limit
                else 0
            )

        self._update_summary_card(
            self._included_used,
            self._included_suffix,
            self._included_bar,
            included_used,
            included_suffix,
            included_ratio,
        )

        if snapshot.on_demand_enabled and snapshot.on_demand_limit:
            ondemand_used = format_dollars(snapshot.on_demand_used)
            ondemand_suffix = f" / {format_dollars(snapshot.on_demand_limit)}"
            ondemand_ratio = snapshot.on_demand_used / snapshot.on_demand_limit
        else:
            ondemand_used = format_dollars(snapshot.on_demand_used)
            ondemand_suffix = " / —"
            ondemand_ratio = 0

        self._update_summary_card(
            self._ondemand_used,
            self._ondemand_suffix,
            self._ondemand_bar,
            ondemand_used,
            ondemand_suffix,
            ondemand_ratio,
        )

        for label in self._body_labels:
            label.destroy()
        self._body_labels.clear()

        models = snapshot.models[:8]
        if not models:
            lbl = tk.Label(
                self._table_grid,
                text="No model usage in current billing cycle.",
                fg=MUTED,
                bg=CARD,
                font=self._body_font,
                anchor="w",
            )
            lbl.grid(row=1, column=0, columnspan=4, sticky="w", pady=4)
            self._body_labels.append(lbl)
        else:
            for row_idx, model in enumerate(models, start=1):
                values = [
                    model.model,
                    str(model.requests),
                    format_tokens(model.tokens),
                    format_dollars(model.spend_cents),
                ]
                anchors = ("w", "e", "e", "e")
                for col, (text, anchor) in enumerate(zip(values, anchors)):
                    lbl = tk.Label(
                        self._table_grid,
                        text=text,
                        fg=TEXT,
                        bg=CARD,
                        font=self._body_font,
                        anchor=anchor,
                    )
                    lbl.grid(
                        row=row_idx,
                        column=col,
                        sticky="ew",
                        pady=2,
                        padx=(0, 0 if col < 3 else 2),
                    )
                    self._body_labels.append(lbl)

        days = days_until_reset(snapshot.billing_cycle_end)
        if self._footer_label:
            self._footer_label.configure(
                text=format_reset_line(days, snapshot.billing_cycle_end)
            )
        if self._user_footer_label:
            self._user_footer_label.configure(
                text=f"User: {self._user_label}" if self._user_label else ""
            )
        if self._status_label:
            self._status_label.configure(text=snapshot.display_message or "")
        if self._last_updated_label:
            self._set_last_updated_text(f" · Updated {format_last_updated()}")

        if self._popup and self._visible:
            self._place_window(self._popup)

    def render_error(self, message: str) -> None:
        self._ensure_window()
        if self._status_label:
            self._status_label.configure(text=f"Error: {message}")
        if self._last_updated_label:
            if self._last_updated_text:
                self._last_updated_label.configure(text=self._last_updated_text)
            else:
                self._last_updated_label.configure(text=f" · Error: {message}")
