"""
new_world.py — New world creation screen for Powernet.

The player sets:
  • World name  (TextInput)
  • Difficulty  (Easy | Hard — described card-style)
  • World icon  (small "?" button → modal dialog with all icons)

Returns from update():
  "back"               → return to world manager
  "created:<world_id>" → world saved, open world manager
"""

from __future__ import annotations

import re
from typing import Optional

import pygame

from helpers.gui import GUIHandler, Label, TextInput, theme
from helpers.i18n import t
from helpers.icons import get_icon, ICO_BACK
from helpers.world_data import WorldMeta, WORLD_ICONS, TEX_BASE

_ICON_SIZE  = 52   # displayed size per icon cell in the dialog grid
_ICON_COLS  = 5    # columns in the dialog grid

_DIFF_EASY = "easy"
_DIFF_HARD = "hard"


def _load_icon_surf(icon_id: int, size: int = _ICON_SIZE) -> pygame.Surface:
    info = next((ic for ic in WORLD_ICONS if ic["id"] == icon_id), WORLD_ICONS[0])
    path = TEX_BASE / info["path"]
    try:
        raw = pygame.image.load(str(path)).convert_alpha()
        scaled = pygame.transform.scale(raw, (size, size))
        return scaled
    except Exception:
        # Fallback: colored square
        surf = pygame.Surface((size, size))
        surf.fill((80, 80, 120))
        return surf


def _lerp3(a: tuple, b: tuple, t: float) -> tuple:
    return tuple(int(a[i] + (b[i] - a[i]) * t) for i in range(3))


# ---------------------------------------------------------------------------
# Difficulty card
# ---------------------------------------------------------------------------


class _DiffCard:
    """Selectable difficulty card drawn manually."""

    def __init__(self, x: int, y: int, w: int, h: int, diff: str) -> None:
        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.diff = diff
        self._hover = 0.0

    @property
    def rect(self) -> pygame.Rect:
        return pygame.Rect(self.x, self.y, self.w, self.h)

    def update(self, dt: float) -> None:
        target = 1.0 if self.rect.collidepoint(pygame.mouse.get_pos()) else 0.0
        self._hover += (target - self._hover) * min(1.0, 10.0 * dt)

    def draw(self, surface: pygame.Surface, selected: bool) -> None:
        r = self.rect
        is_hard = self.diff == _DIFF_HARD
        accent = (180, 55, 25) if is_hard else (35, 130, 75)

        alpha = 220 if selected else int(140 + 60 * self._hover)
        bg_surf = pygame.Surface((r.w, r.h), pygame.SRCALPHA)
        if selected:
            bg_surf.fill((*accent, 45))
        else:
            bg_surf.fill((*theme.PANEL, alpha))
        surface.blit(bg_surf, r.topleft)

        bw = 2 if selected else 1
        bc = accent if (selected or self._hover > 0.1) else theme.BORDER
        pygame.draw.rect(surface, bc, r, width=bw, border_radius=4)

        if selected:
            pygame.draw.rect(surface, accent, pygame.Rect(r.x, r.y, r.w, 3), border_radius=2)

        label = t("nw_hard") if is_hard else t("nw_easy")
        label_col = accent if selected else _lerp3(theme.TEXT_DIM, accent, self._hover)
        title_s = theme.font(22).render(label, True, label_col)
        surface.blit(title_s, (r.x + 16, r.y + 14))

        badge_text = t("nw_hard_badge") if is_hard else t("nw_easy_badge")
        badge_col = (*accent, 180) if selected else (*theme.TEXT_DIM, 100)
        badge_s = theme.font(12).render(badge_text, True, badge_col[:3])
        surface.blit(badge_s, (r.x + r.w - badge_s.get_width() - 12, r.y + 18))

        desc_keys = (
            ["nw_hard_1", "nw_hard_2", "nw_hard_3", "nw_hard_4", "nw_hard_5"]
            if is_hard
            else ["nw_easy_1", "nw_easy_2", "nw_easy_3"]
        )
        line_col = theme.TEXT if selected else theme.TEXT_DIM
        for i, key in enumerate(desc_keys):
            ls = theme.font(13).render(t(key), True, line_col)
            surface.blit(ls, (r.x + 16, r.y + 50 + i * 20))


# ---------------------------------------------------------------------------
# Icon dialog cell (inside the modal)
# ---------------------------------------------------------------------------


class _IconCell:
    """A single icon cell inside the icon picker dialog."""

    _CELL_SZ = _ICON_SIZE + 10

    def __init__(self, x: int, y: int, icon_id: int, surf: pygame.Surface) -> None:
        self.x = x
        self.y = y
        self.icon_id = icon_id
        self.surf = surf
        self._hover = 0.0

    @property
    def rect(self) -> pygame.Rect:
        return pygame.Rect(self.x, self.y, self._CELL_SZ, self._CELL_SZ)

    def update(self, dt: float) -> None:
        target = 1.0 if self.rect.collidepoint(pygame.mouse.get_pos()) else 0.0
        self._hover += (target - self._hover) * min(1.0, 10.0 * dt)

    def draw(self, surface: pygame.Surface, selected: bool) -> None:
        r = self.rect
        if selected:
            bg = pygame.Surface((r.w, r.h), pygame.SRCALPHA)
            bg.fill((*theme.ACCENT, 60))
            surface.blit(bg, r.topleft)
            pygame.draw.rect(surface, theme.ACCENT, r, width=2, border_radius=3)
        elif self._hover > 0.05:
            bg = pygame.Surface((r.w, r.h), pygame.SRCALPHA)
            bg.fill((*theme.BORDER_HOV, int(80 * self._hover)))
            surface.blit(bg, r.topleft)
            pygame.draw.rect(surface, (*theme.BORDER_HOV, int(180 * self._hover)),
                             r, width=1, border_radius=3)
        pad = (r.w - _ICON_SIZE) // 2
        surface.blit(self.surf, (r.x + pad, r.y + pad))


# ---------------------------------------------------------------------------
# Icon dialog (modal overlay)
# ---------------------------------------------------------------------------


class _IconDialog:
    """
    Modal overlay that shows all world icons in a grid.

    Call open() to show; read .done and .selected_id when closed.
    """

    _CELL_SZ  = _ICON_SIZE + 10
    _CELL_GAP = 6

    def __init__(
        self,
        screen_w: int,
        screen_h: int,
    ) -> None:
        self._cells: list[_IconCell] = []
        self._open = False
        self.done = False
        self.selected_id: int = 0
        self._current_id: int = 0
        self._screen_w = screen_w
        self._screen_h = screen_h

    def open(self, current_icon_id: int) -> None:
        self._open = True
        self.done = False
        self._current_id = current_icon_id
        self.selected_id = current_icon_id
        self._build_cells()

    def _build_cells(self) -> None:
        n = len(WORLD_ICONS)
        cols = _ICON_COLS
        rows = (n + cols - 1) // cols
        box_w = cols * (self._CELL_SZ + self._CELL_GAP) - self._CELL_GAP + 60
        box_h = rows * (self._CELL_SZ + self._CELL_GAP) - self._CELL_GAP + 140
        bx = self._screen_w // 2 - box_w // 2
        by = self._screen_h // 2 - box_h // 2
        grid_x = bx + 30
        grid_y = by + 70

        self._cells = []
        for idx, info in enumerate(WORLD_ICONS):
            col_idx = idx % cols
            row_idx = idx // cols
            cx = grid_x + col_idx * (self._CELL_SZ + self._CELL_GAP)
            cy = grid_y + row_idx * (self._CELL_SZ + self._CELL_GAP)
            surf = _load_icon_surf(info["id"])
            self._cells.append(_IconCell(cx, cy, info["id"], surf))

        self._box = pygame.Rect(bx, by, box_w, box_h)

    def _ok_rect(self) -> pygame.Rect:
        return pygame.Rect(
            self._box.centerx - 90, self._box.bottom - 50, 80, 34
        )

    def _cancel_rect(self) -> pygame.Rect:
        return pygame.Rect(
            self._box.centerx + 10, self._box.bottom - 50, 80, 34
        )

    def handle_event(self, event: pygame.event.Event) -> bool:
        if not self._open:
            return False
        if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            self._open = False
            self.done = True
            # cancelled — keep old selection
            self.selected_id = self._current_id
            return True
        if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
            for cell in self._cells:
                if cell.rect.collidepoint(event.pos):
                    self.selected_id = cell.icon_id
                    return True
            if self._ok_rect().collidepoint(event.pos):
                self._open = False
                self.done = True
                return True
            if self._cancel_rect().collidepoint(event.pos):
                self._open = False
                self.done = True
                self.selected_id = self._current_id
                return True
        return False

    def update(self, dt: float) -> None:
        if not self._open:
            return
        for cell in self._cells:
            cell.update(dt)

    def draw(self, surface: pygame.Surface) -> None:
        if not self._open:
            return

        # Dim background
        dim = pygame.Surface(surface.get_size(), pygame.SRCALPHA)
        dim.fill((0, 0, 0, 150))
        surface.blit(dim, (0, 0))

        # Box
        box_s = pygame.Surface((self._box.w, self._box.h), pygame.SRCALPHA)
        box_s.fill((*theme.PANEL, 248))
        surface.blit(box_s, self._box.topleft)
        pygame.draw.rect(surface, theme.ACCENT, self._box, width=2, border_radius=5)
        pygame.draw.rect(surface, theme.ACCENT,
                         pygame.Rect(self._box.x, self._box.y, self._box.w, 3),
                         border_radius=2)

        # Title
        title_s = theme.font(20).render(t("nw_icon_dialog"), True, theme.TEXT)
        surface.blit(title_s, (self._box.centerx - title_s.get_width() // 2, self._box.y + 16))

        # Icon cells
        for cell in self._cells:
            cell.draw(surface, selected=(cell.icon_id == self.selected_id))

        # Selected label
        info = next((ic for ic in WORLD_ICONS if ic["id"] == self.selected_id), WORLD_ICONS[0])
        sel_s = theme.font(14).render(t("nw_icon_selected", name=info["label"]),
                                      True, theme.ACCENT)
        surface.blit(sel_s, (self._box.centerx - sel_s.get_width() // 2,
                              self._box.bottom - 70))

        # OK / Cancel buttons
        mp = pygame.mouse.get_pos()
        for rect, label in [(self._ok_rect(), t("confirm")), (self._cancel_rect(), t("cancel"))]:
            hover = rect.collidepoint(mp)
            col = theme.BTN_HOVER_BAR if hover else theme.BTN_NORMAL_BAR
            bs = pygame.Surface((rect.w, rect.h), pygame.SRCALPHA)
            bs.fill((*col, 200 if hover else 140))
            surface.blit(bs, rect.topleft)
            pygame.draw.rect(surface, col, rect, width=1, border_radius=3)
            ls = theme.font(16).render(label, True, theme.TEXT)
            surface.blit(ls, (rect.centerx - ls.get_width() // 2,
                               rect.centery - ls.get_height() // 2))


# ---------------------------------------------------------------------------
# NewWorld scene
# ---------------------------------------------------------------------------


class NewWorld:
    """
    Scene: Create a new world.

    update() return values
    ----------------------
    "back"                → return to world manager
    "created:<world_id>"  → world saved, switch to world manager
    None                  → no transition
    """

    # Size of the icon preview square ("?" button)
    _ICON_BTN_SZ = 52

    def __init__(self, screen: pygame.Surface) -> None:
        self._pending: Optional[str] = None
        self._difficulty: str = _DIFF_EASY
        self._icon_id: int = 0

        self._gui = GUIHandler()
        self._name_input: Optional[TextInput] = None
        self._save_as_label: Optional[Label] = None
        self._diff_cards: list[_DiffCard] = []
        self._last_size = (0, 0)

        # Icon dialog (lazily built when opened)
        self._icon_dialog: Optional[_IconDialog] = None

        # Preview button state (hover)
        self._icon_btn_rect: pygame.Rect = pygame.Rect(0, 0, self._ICON_BTN_SZ, self._ICON_BTN_SZ)
        self._icon_btn_hover: float = 0.0

        self._build(screen.get_size())

    # ------------------------------------------------------------------
    # Build
    # ------------------------------------------------------------------

    def _build(self, size: tuple[int, int]) -> None:
        w = size[0]
        self._last_size = size
        cx = w // 2
        pad = 20

        self._gui.clear()

        # Header
        self._gui.panel(cx, 0, w, 62, color=theme.PANEL, alpha=220,
                        border_color=None, anchor="midtop")
        self._gui.panel(cx, 62, w, 2, color=theme.ACCENT, alpha=180,
                        border_color=None, border_width=0, anchor="midtop")
        self._gui.label(cx, 31, t("nw_title"),
                        font_size=26, color=theme.TEXT, anchor="center")
        self._gui.small_button(
            pad, 31, t("back"),
            font_size=16, anchor="midleft",
            color=theme.TEXT_DIM, hover_color=theme.ACCENT,
            icon_surf=get_icon(ICO_BACK, 16),
            on_click=self._on_back,
        )

        form_x = cx - 380
        form_w = 760
        y = 90

        # ---- World Name ----
        self._gui.label(form_x, y, t("nw_name"), font_size=17, color=theme.TEXT_DIM)
        y += 28
        ti = TextInput(
            form_x, y, form_w, 50,
            placeholder=t("nw_name_ph"),
            max_length=40, font_size=24,
        )
        if self._name_input:
            ti.text = self._name_input.text
        self._name_input = ti
        self._gui.add(ti, name="name_input")
        y += 56

        # "Save as: safe_name_xxxx" preview
        save_as_lbl = Label(form_x, y, "", font_size=13, color=theme.TEXT_DIM)
        self._save_as_label = save_as_lbl
        self._gui.add(save_as_lbl)
        y += 22

        # ---- Difficulty ----
        self._gui.label(form_x, y, t("nw_diff"), font_size=17, color=theme.TEXT_DIM)
        y += 28
        card_gap = 14
        card_w = (form_w - card_gap) // 2
        card_h = 145
        self._diff_cards = [
            _DiffCard(form_x,                    y, card_w, card_h, _DIFF_EASY),
            _DiffCard(form_x + card_w + card_gap, y, card_w, card_h, _DIFF_HARD),
        ]
        y += card_h + 22

        # ---- World Icon ----
        self._gui.label(form_x, y, t("nw_icon"), font_size=17, color=theme.TEXT_DIM)
        y += 28
        # The "?" button is drawn manually (not a GUI element) at form_x, y
        self._icon_btn_rect = pygame.Rect(form_x, y, self._ICON_BTN_SZ, self._ICON_BTN_SZ)
        y += self._ICON_BTN_SZ + 22

        # ---- Create button ----
        self._gui.button(
            cx, y, 260, 54, t("nw_create"),
            font_size=24, anchor="midtop",
            on_click=self._on_create,
            name="btn_create",
        )

        # Ensure dialog size is updated
        sw, sh = size
        if self._icon_dialog is None:
            self._icon_dialog = _IconDialog(sw, sh)
        else:
            # Recreate with new screen size
            self._icon_dialog = _IconDialog(sw, sh)

    # ------------------------------------------------------------------
    # Callbacks
    # ------------------------------------------------------------------

    def _on_back(self) -> None:
        self._pending = "back"

    def _on_create(self) -> None:
        import random as _random
        name = self._name_input.text.strip() if self._name_input else ""
        if not name:
            name = "Unnamed World"
        seed = _random.randint(1, 2**31 - 1)
        world = WorldMeta(
            name=name,
            difficulty=self._difficulty,
            icon_id=self._icon_id,
            seed=seed,
        )
        world.save()
        self._pending = f"created:{world.world_id}"

    # ------------------------------------------------------------------
    # Scene interface
    # ------------------------------------------------------------------

    def update(self, dt: float, events: list[pygame.event.Event]) -> Optional[str]:
        if self._last_size != pygame.display.get_surface().get_size():
            self._build(pygame.display.get_surface().get_size())

        dialog_open = self._icon_dialog is not None and self._icon_dialog._open

        for event in events:
            if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
                if dialog_open:
                    pass  # let dialog handle
                else:
                    self._pending = "back"

            if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
                if not dialog_open:
                    # Difficulty card clicks
                    for card in self._diff_cards:
                        if card.rect.collidepoint(event.pos):
                            self._difficulty = card.diff
                    # Icon button click — open dialog
                    if self._icon_btn_rect.collidepoint(event.pos):
                        if self._icon_dialog is not None:
                            self._icon_dialog.open(self._icon_id)

        # Let dialog consume events first when open
        if self._icon_dialog is not None:
            for event in events:
                self._icon_dialog.handle_event(event)
            if self._icon_dialog.done:
                self._icon_id = self._icon_dialog.selected_id
                self._icon_dialog.done = False

        if not dialog_open:
            self._gui.handle_events(events)

        self._gui.update(dt)

        for card in self._diff_cards:
            card.update(dt)

        if self._icon_dialog is not None:
            self._icon_dialog.update(dt)

        # Icon button hover
        hov = self._icon_btn_rect.collidepoint(pygame.mouse.get_pos()) and not dialog_open
        self._icon_btn_hover += (1.0 if hov else 0.0 - self._icon_btn_hover) * min(1.0, 10.0 * dt)

        # Update "Save as:" preview label
        if self._save_as_label is not None and self._name_input is not None:
            raw = self._name_input.text.strip() or "Unnamed World"
            safe = re.sub(r"[^\w\-]", "_", raw)[:30] or "world"
            self._save_as_label.text = t("nw_save_as", name=f"{safe}_...")

        result, self._pending = self._pending, None
        return result

    def draw(self, surface: pygame.Surface) -> None:
        if surface.get_size() != self._last_size:
            self._build(surface.get_size())

        surface.fill(theme.BG)

        # Background dot grid
        step = 48
        for gx in range(0, surface.get_width(), step):
            for gy in range(0, surface.get_height(), step):
                pygame.draw.circle(surface, (18, 32, 52), (gx, gy), 1)

        # Difficulty cards
        for card in self._diff_cards:
            card.draw(surface, selected=(card.diff == self._difficulty))

        # Icon preview button ("?" or current icon)
        self._draw_icon_button(surface)

        # GUI (header + buttons)
        self._gui.draw(surface)

        # Icon dialog (on top of everything)
        if self._icon_dialog is not None:
            self._icon_dialog.draw(surface)

    def _draw_icon_button(self, surface: pygame.Surface) -> None:
        r = self._icon_btn_rect
        h = self._icon_btn_hover

        # Background
        bg_col = (
            int(theme.PANEL[0] + (theme.BTN_HOVER_BG[0] - theme.PANEL[0]) * h),
            int(theme.PANEL[1] + (theme.BTN_HOVER_BG[1] - theme.PANEL[1]) * h),
            int(theme.PANEL[2] + (theme.BTN_HOVER_BG[2] - theme.PANEL[2]) * h),
        )
        bg = pygame.Surface((r.w, r.h), pygame.SRCALPHA)
        bg.fill((*bg_col, 220))
        surface.blit(bg, r.topleft)

        border_col = theme.ACCENT if h > 0.5 else theme.BORDER
        pygame.draw.rect(surface, border_col, r, width=1, border_radius=4)

        # Show current icon
        icon_surf = _load_icon_surf(self._icon_id, _ICON_SIZE)
        pad = (r.w - _ICON_SIZE) // 2
        surface.blit(icon_surf, (r.x + pad, r.y + pad))

