"""
Modal dialogs — S7 Confirm, S8 Progress, S9 Error.

A modal renders its underlay screen first, then a scrim, then a centered
card. It's pushed onto the same screen stack; B (or an action) pops it.
S12 Toast is handled by the App itself (non-blocking, no scrim).
"""

from __future__ import annotations

import pygame

from ..design import tokens as t
from ..shared import widgets as w
from ..shared import fonts, icons


class ConfirmDialog:
    """S7 — binary confirm. focus defaults to confirm (or Cancel if danger)."""
    name = "confirm"

    def __init__(self, app, underlay, *, title, body,
                 confirm_label="Install", on_confirm=None, danger=False,
                 body_max_chars=42, card_width=380):
        self.app = app
        self.underlay = underlay
        self.title = title
        self.body = body
        self.confirm_label = confirm_label
        self.on_confirm = on_confirm
        self.danger = danger
        self.body_max_chars = body_max_chars
        self.card_width = card_width
        # focus: 0=Cancel 1=Confirm. danger → default Cancel
        self.focus = 0 if danger else 1

    def on_action(self, action):
        if action in ("left", "right"):
            self.focus ^= 1
        elif action == "b":
            self.app.pop()
        elif action == "a":
            if self.focus == 1:
                self.app.pop()
                if self.on_confirm:
                    self.on_confirm()
            else:
                self.app.pop()

    def update(self, dt_ms):
        pass

    def draw(self, surf):
        w.draw_underlay(surf, self.underlay)
        w.scrim(surf)
        # mask the underlay's own footer so it doesn't bleed under ours
        surf.fill(t.Color.BG_DEEP, (0, 448, 640, t.Space.FOOTER_HINT_HEIGHT))
        body_lines = w.wrap_chars(self.body, self.body_max_chars)
        sample = fonts.render(t.Type.BODY, "Ag", t.Color.TEXT_SECONDARY)
        body_h = len(body_lines) * (sample.get_height() + 3)
        cw = max(380, min(520, self.card_width))
        ch = max(200, min(318, 156 + body_h))
        rect = pygame.Rect((640 - cw) // 2, (480 - ch) // 2, cw, ch)
        w.modal_card(surf, rect)

        title = fonts.render(t.Type.TITLE, self.title, t.Color.TEXT_PRIMARY)
        surf.blit(title, (rect.centerx - title.get_width() // 2, rect.y + 22))

        # body — wraps and grows the modal for longer safety copy.
        by = rect.y + 58
        for line in body_lines:
            s = fonts.render(t.Type.BODY, line, t.Color.TEXT_SECONDARY)
            surf.blit(s, (rect.centerx - s.get_width() // 2, by))
            by += s.get_height() + 3

        # two buttons
        bw, bh, gap = 150, 42, 16
        bx = rect.centerx - bw - gap // 2
        cy = rect.bottom - 64
        w.button(surf, pygame.Rect(bx, cy, bw, bh), "Cancel", focused=(self.focus == 0))
        w.button(surf, pygame.Rect(rect.centerx + gap // 2, cy, bw, bh),
                 self.confirm_label, focused=(self.focus == 1))

        # consistent footer hint (own actions, overrides the underlay's)
        w.footer_hints(surf, [("A", self.confirm_label.lower()), ("B", "cancel")])


class ProgressDialog:
    """S8 — conic progress ring. Driven by set_progress(); no buttons."""
    name = "progress"

    def __init__(self, app, *, title="Installing…", status="", frac=0.0):
        self.app = app
        self.title = title
        self.status = status
        self.frac = frac

    def set_progress(self, frac, status=None):
        self.frac = max(0.0, min(frac, 1.0))
        if status is not None:
            self.status = status

    def on_action(self, action):
        pass  # progress is non-interactive

    def update(self, dt_ms):
        pass

    def draw(self, surf):
        # full background (no underlay; this is a takeover)
        w.status_bar(surf, title=self.title, back=False)
        cx, cy, r = 320, 240, 60
        w.conic_ring(surf, cx, cy, r, self.frac, thickness=10)
        pct = fonts.render(t.Type.TITLE, f"{int(self.frac*100)}%", t.Color.TEXT_PRIMARY)
        surf.blit(pct, (cx - pct.get_width() // 2, cy - pct.get_height() // 2))
        if self.status:
            s = fonts.render(t.Type.CAPTION, self.status, t.Color.TEXT_SECONDARY)
            surf.blit(s, (cx - s.get_width() // 2, cy + r + 18))

        w.footer_hints(surf, [("B", "cancel")])


class ErrorDialog:
    """S9 — error modal. Show details / Close. error never blocks recovery."""
    name = "error"

    def __init__(self, app, underlay, *, title="Install failed", body="",
                 trace="", on_details=None):
        self.app = app
        self.underlay = underlay
        self.title = title
        self.body = body
        self.trace = trace
        self.on_details = on_details
        self.focus = 1   # Close

    def on_action(self, action):
        if action in ("left", "right"):
            self.focus ^= 1
        elif action == "b":
            self.app.pop()
        elif action == "a":
            if self.focus == 0 and self.on_details:
                self.app.pop()
                self.on_details()
            else:
                self.app.pop()

    def update(self, dt_ms):
        pass

    def draw(self, surf):
        w.draw_underlay(surf, self.underlay)
        w.scrim(surf)
        # mask the underlay's own footer so it doesn't bleed under ours
        surf.fill(t.Color.BG_DEEP, (0, 448, 640, t.Space.FOOTER_HINT_HEIGHT))
        cw, ch = 420, 240
        rect = pygame.Rect((640 - cw) // 2, (480 - ch) // 2, cw, ch)
        w.modal_card(surf, rect)

        ic = icons.icon("ph-warning-circle", 36, t.Color.ERROR)
        surf.blit(ic, (rect.centerx - 18, rect.y + 18))
        title = fonts.render(t.Type.TITLE, self.title, t.Color.ERROR)
        surf.blit(title, (rect.centerx - title.get_width() // 2, rect.y + 60))
        by = rect.y + 92
        for line in w.wrap_chars(self.body, 46):
            s = fonts.render(t.Type.BODY, line, t.Color.TEXT_SECONDARY)
            surf.blit(s, (rect.centerx - s.get_width() // 2, by))
            by += s.get_height() + 3
        if self.trace:
            tr = fonts.render(t.Type.MONO_TECH, self.trace[:54], t.Color.TEXT_MUTED)
            surf.blit(tr, (rect.centerx - tr.get_width() // 2, by + 4))

        bw, bh, gap = 170, 42, 16
        cyy = rect.bottom - 60
        w.button(surf, pygame.Rect(rect.centerx - bw - gap // 2, cyy, bw, bh),
                 "Show details", focused=(self.focus == 0))
        w.button(surf, pygame.Rect(rect.centerx + gap // 2, cyy, bw, bh),
                 "Close", focused=(self.focus == 1))

        w.footer_hints(surf, [("A", "select"), ("B", "back")])
