"""Couche logique pure de l'éditeur de masques (testable sans display Tk).

Sépare la logique métier (rectangles par page, conversions écran↔PDF, hit-test,
sérialisation de templates) de la fenêtre Tk `MaskEditorWindow`. Le format de
template reste strictement compatible avec `pdf_mask_designer.Template` (et donc
avec le moteur d'anonymisation) : on réutilise `MaskRect`/`Template`.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Optional

from pdf_mask_designer import MaskRect, Template, clamp, rect_norm

# Bornes d'affichage de l'éditeur.
ZOOM_MIN = 0.25
ZOOM_MAX = 4.0

# Côté minimal (en points PDF) d'un rectangle dessiné ; en dessous, on ignore
# (évite les masques fantômes créés par un simple clic).
MIN_RECT_SIZE = 3.0

_DEFAULT_PAGE_SIZE = (595.0, 842.0)


def clamp_zoom(zoom: float) -> float:
    return clamp(float(zoom), ZOOM_MIN, ZOOM_MAX)


def clamp_page(index: int, page_count: int) -> int:
    if page_count <= 0:
        return 0
    return int(clamp(int(index), 0, page_count - 1))


def screen_to_pdf(cx: float, cy: float, zoom: float) -> tuple[float, float]:
    """Coordonnées canvas (espace scrollregion, origine 0,0) -> points PDF."""
    z = zoom or 1.0
    return (cx / z, cy / z)


def pdf_to_screen(px: float, py: float, zoom: float) -> tuple[float, float]:
    return (px * zoom, py * zoom)


def fit_width_zoom(page_width_pt: float, viewport_px: float, padding: float = 24.0) -> float:
    if page_width_pt <= 0:
        return 1.0
    usable = max(1.0, viewport_px - padding)
    return usable / page_width_pt


def fit_page_zoom(
    page_width_pt: float,
    page_height_pt: float,
    viewport_w_px: float,
    viewport_h_px: float,
    padding: float = 24.0,
) -> float:
    if page_width_pt <= 0 or page_height_pt <= 0:
        return 1.0
    zw = max(1.0, viewport_w_px - padding) / page_width_pt
    zh = max(1.0, viewport_h_px - padding) / page_height_pt
    return min(zw, zh)


@dataclass
class MaskEditorModel:
    """État pur de l'éditeur : rectangles (toutes pages) + position courante."""

    name: str = "template_masques"
    page_index: int = 0
    page_count: int = 0
    page_sizes: list[tuple[float, float]] = field(default_factory=list)
    masks: list[MaskRect] = field(default_factory=list)

    # --- rectangles ---------------------------------------------------------
    def add_rect(
        self,
        page: int,
        x0: float,
        y0: float,
        x1: float,
        y1: float,
        label: str = "MASK",
    ) -> Optional[MaskRect]:
        nx0, ny0, nx1, ny1 = rect_norm(float(x0), float(y0), float(x1), float(y1))
        if (nx1 - nx0) < MIN_RECT_SIZE or (ny1 - ny0) < MIN_RECT_SIZE:
            return None
        rect = MaskRect(page=int(page), x0=nx0, y0=ny0, x1=nx1, y1=ny1, label=label)
        self.masks.append(rect)
        return rect

    def masks_for_page(self, page: int) -> list[MaskRect]:
        return [m for m in self.masks if m.page == page]

    def rect_at(self, page: int, px: float, py: float) -> Optional[MaskRect]:
        """Rectangle le plus en avant (dernier dessiné) contenant le point."""
        for rect in reversed(self.masks):
            if rect.page != page:
                continue
            if rect.x0 <= px <= rect.x1 and rect.y0 <= py <= rect.y1:
                return rect
        return None

    def delete_rect(self, rect: MaskRect) -> bool:
        for idx, existing in enumerate(self.masks):
            if existing is rect:
                del self.masks[idx]
                return True
        return False

    def clear_page(self, page: int) -> int:
        before = len(self.masks)
        self.masks = [m for m in self.masks if m.page != page]
        return before - len(self.masks)

    def clear_all(self) -> int:
        count = len(self.masks)
        self.masks.clear()
        return count

    def count_page(self, page: int) -> int:
        return sum(1 for m in self.masks if m.page == page)

    def count_total(self) -> int:
        return len(self.masks)

    # --- pages --------------------------------------------------------------
    def set_page(self, index: int) -> int:
        self.page_index = clamp_page(index, self.page_count)
        return self.page_index

    def current_page_size(self) -> tuple[float, float]:
        if 0 <= self.page_index < len(self.page_sizes):
            return self.page_sizes[self.page_index]
        return _DEFAULT_PAGE_SIZE

    def reference_page_size(self) -> tuple[float, float]:
        if self.page_sizes:
            return self.page_sizes[0]
        return _DEFAULT_PAGE_SIZE

    # --- templates (compat pdf_mask_designer.Template) ----------------------
    def to_payload(self) -> dict[str, Any]:
        width, height = self.reference_page_size()
        return {
            "version": 1,
            "name": (self.name or "template_masques").strip() or "template_masques",
            "page_size": {"width": float(width), "height": float(height)},
            "masks": [
                {
                    "page": int(m.page),
                    "x0": round(float(m.x0), 2),
                    "y0": round(float(m.y0), 2),
                    "x1": round(float(m.x1), 2),
                    "y1": round(float(m.y1), 2),
                    "label": str(m.label),
                }
                for m in self.masks
            ],
        }

    @classmethod
    def from_payload(cls, payload: dict[str, Any]) -> "MaskEditorModel":
        tpl = Template.from_dict(payload or {})
        return cls(
            name=tpl.name,
            page_sizes=[tpl.page_size],
            masks=list(tpl.masks or []),
        )
