"""Aspect ratio definitions for presentations and themes.

Provides EMU-precise dimensions for standard and custom aspect ratios
used in PowerPoint presentations, SuperThemes, and print layouts.

Standard presentation ratios:
- 16:9 (widescreen) - Modern default
- 16:10 (widescreen) - Mac displays
- 4:3 (standard) - Legacy/projection

Print/document ratios:
- A4 landscape (297mm × 210mm)
- A4 portrait (210mm × 297mm)
- Letter landscape (11" × 8.5")
- Letter portrait (8.5" × 11")

All dimensions are in EMU (English Metric Units):
- 914400 EMU = 1 inch
- 914400 EMU = 72 points
- 914400 EMU ≈ 25.4mm
"""

from __future__ import annotations

from dataclasses import dataclass
from enum import Enum
from typing import Dict, Tuple

from tokenmoulds.design.units import EMU_PER_INCH


# Conversion constants
EMU_PER_MM = EMU_PER_INCH / 25.4  # ~36000 EMU per mm


class AspectRatioCategory(Enum):
    """Category of aspect ratio usage."""

    PRESENTATION = "presentation"  # Screen-based ratios
    PRINT = "print"  # Paper-based ratios
    CUSTOM = "custom"  # User-defined


@dataclass(frozen=True, slots=True)
class AspectRatio:
    """Aspect ratio definition with EMU dimensions.

    Attributes:
        name: Human-readable name
        key: Machine identifier (e.g., "16_9", "a4_landscape")
        width_emu: Width in EMU
        height_emu: Height in EMU
        category: Usage category
        description: Optional description
    """

    name: str
    key: str
    width_emu: int
    height_emu: int
    category: AspectRatioCategory = AspectRatioCategory.PRESENTATION
    description: str = ""

    @property
    def ratio(self) -> float:
        """Aspect ratio as width/height."""
        return self.width_emu / self.height_emu if self.height_emu else 0

    @property
    def ratio_string(self) -> str:
        """Human-readable ratio string."""
        # Common ratios
        r = self.ratio
        if abs(r - 16 / 9) < 0.01:
            return "16:9"
        elif abs(r - 16 / 10) < 0.01:
            return "16:10"
        elif abs(r - 4 / 3) < 0.01:
            return "4:3"
        elif abs(r - 297 / 210) < 0.01:
            return "A4 landscape"
        elif abs(r - 210 / 297) < 0.01:
            return "A4 portrait"
        elif abs(r - 11 / 8.5) < 0.01:
            return "Letter landscape"
        elif abs(r - 8.5 / 11) < 0.01:
            return "Letter portrait"
        else:
            return f"{r:.3f}"

    @property
    def width_inches(self) -> float:
        """Width in inches."""
        return self.width_emu / EMU_PER_INCH

    @property
    def height_inches(self) -> float:
        """Height in inches."""
        return self.height_emu / EMU_PER_INCH

    @property
    def width_mm(self) -> float:
        """Width in millimeters."""
        return self.width_emu / EMU_PER_MM

    @property
    def height_mm(self) -> float:
        """Height in millimeters."""
        return self.height_emu / EMU_PER_MM

    @property
    def dimensions_emu(self) -> Tuple[int, int]:
        """Width and height as EMU tuple."""
        return (self.width_emu, self.height_emu)

    @classmethod
    def from_inches(
        cls,
        name: str,
        key: str,
        width_inches: float,
        height_inches: float,
        category: AspectRatioCategory = AspectRatioCategory.PRESENTATION,
        description: str = "",
    ) -> AspectRatio:
        """Create aspect ratio from inch dimensions."""
        return cls(
            name=name,
            key=key,
            width_emu=int(round(width_inches * EMU_PER_INCH)),
            height_emu=int(round(height_inches * EMU_PER_INCH)),
            category=category,
            description=description,
        )

    @classmethod
    def from_mm(
        cls,
        name: str,
        key: str,
        width_mm: float,
        height_mm: float,
        category: AspectRatioCategory = AspectRatioCategory.PRINT,
        description: str = "",
    ) -> AspectRatio:
        """Create aspect ratio from millimeter dimensions."""
        return cls(
            name=name,
            key=key,
            width_emu=int(round(width_mm * EMU_PER_MM)),
            height_emu=int(round(height_mm * EMU_PER_MM)),
            category=category,
            description=description,
        )

    @classmethod
    def from_ratio(
        cls,
        name: str,
        key: str,
        width_ratio: int,
        height_ratio: int,
        base_width_emu: int = 12192000,  # PowerPoint default ~13.33"
        category: AspectRatioCategory = AspectRatioCategory.PRESENTATION,
        description: str = "",
    ) -> AspectRatio:
        """Create aspect ratio from ratio numbers (e.g., 16:9).

        Uses base_width_emu and calculates height to maintain ratio.
        """
        height_emu = int(round(base_width_emu * height_ratio / width_ratio))
        return cls(
            name=name,
            key=key,
            width_emu=base_width_emu,
            height_emu=height_emu,
            category=category,
            description=description,
        )

    def rotated(self) -> AspectRatio:
        """Return a rotated (swapped width/height) version."""
        # Determine new key and name
        if "landscape" in self.key:
            new_key = self.key.replace("landscape", "portrait")
            new_name = self.name.replace("Landscape", "Portrait")
        elif "portrait" in self.key:
            new_key = self.key.replace("portrait", "landscape")
            new_name = self.name.replace("Portrait", "Landscape")
        else:
            new_key = f"{self.key}_rotated"
            new_name = f"{self.name} (rotated)"

        return AspectRatio(
            name=new_name,
            key=new_key,
            width_emu=self.height_emu,
            height_emu=self.width_emu,
            category=self.category,
            description=f"Rotated: {self.description}" if self.description else "",
        )


# =============================================================================
# Standard Presentation Aspect Ratios
# =============================================================================

# PowerPoint default dimensions (these match PowerPoint's internal values)
# 16:9 widescreen: 13.333" × 7.5" (12192000 × 6858000 EMU)
WIDESCREEN_16_9 = AspectRatio(
    name="Widescreen 16:9",
    key="16_9",
    width_emu=12192000,
    height_emu=6858000,
    category=AspectRatioCategory.PRESENTATION,
    description="Modern widescreen default, HD/4K displays",
)

# 16:10 widescreen: 13.333" × 8.333" (12192000 × 7620000 EMU)
WIDESCREEN_16_10 = AspectRatio(
    name="Widescreen 16:10",
    key="16_10",
    width_emu=12192000,
    height_emu=7620000,
    category=AspectRatioCategory.PRESENTATION,
    description="Mac displays, WUXGA monitors",
)

# 4:3 standard: 10" × 7.5" (9144000 × 6858000 EMU)
STANDARD_4_3 = AspectRatio(
    name="Standard 4:3",
    key="4_3",
    width_emu=9144000,
    height_emu=6858000,
    category=AspectRatioCategory.PRESENTATION,
    description="Legacy projectors, older displays",
)


# =============================================================================
# Print/Document Aspect Ratios
# =============================================================================

# A4: 297mm × 210mm (ISO 216)
A4_LANDSCAPE = AspectRatio.from_mm(
    name="A4 Landscape",
    key="a4_landscape",
    width_mm=297.0,
    height_mm=210.0,
    category=AspectRatioCategory.PRINT,
    description="ISO A4 paper, landscape orientation (297×210mm)",
)

A4_PORTRAIT = AspectRatio.from_mm(
    name="A4 Portrait",
    key="a4_portrait",
    width_mm=210.0,
    height_mm=297.0,
    category=AspectRatioCategory.PRINT,
    description="ISO A4 paper, portrait orientation (210×297mm)",
)

# US Letter: 11" × 8.5"
LETTER_LANDSCAPE = AspectRatio.from_inches(
    name="Letter Landscape",
    key="letter_landscape",
    width_inches=11.0,
    height_inches=8.5,
    category=AspectRatioCategory.PRINT,
    description="US Letter paper, landscape orientation (11×8.5 inches)",
)

LETTER_PORTRAIT = AspectRatio.from_inches(
    name="Letter Portrait",
    key="letter_portrait",
    width_inches=8.5,
    height_inches=11.0,
    category=AspectRatioCategory.PRINT,
    description="US Letter paper, portrait orientation (8.5×11 inches)",
)

# A3: 420mm × 297mm (ISO 216) - for posters/large format
A3_LANDSCAPE = AspectRatio.from_mm(
    name="A3 Landscape",
    key="a3_landscape",
    width_mm=420.0,
    height_mm=297.0,
    category=AspectRatioCategory.PRINT,
    description="ISO A3 paper, landscape orientation (420×297mm)",
)

A3_PORTRAIT = AspectRatio.from_mm(
    name="A3 Portrait",
    key="a3_portrait",
    width_mm=297.0,
    height_mm=420.0,
    category=AspectRatioCategory.PRINT,
    description="ISO A3 paper, portrait orientation (297×420mm)",
)


# =============================================================================
# Aspect Ratio Registry
# =============================================================================

ASPECT_RATIOS: Dict[str, AspectRatio] = {
    # Presentation ratios
    "16_9": WIDESCREEN_16_9,
    "16_10": WIDESCREEN_16_10,
    "4_3": STANDARD_4_3,
    # Print ratios - A series
    "a4_landscape": A4_LANDSCAPE,
    "a4_portrait": A4_PORTRAIT,
    "a3_landscape": A3_LANDSCAPE,
    "a3_portrait": A3_PORTRAIT,
    # Print ratios - US
    "letter_landscape": LETTER_LANDSCAPE,
    "letter_portrait": LETTER_PORTRAIT,
}

# Aliases for convenience
ASPECT_RATIOS["widescreen"] = WIDESCREEN_16_9
ASPECT_RATIOS["standard"] = STANDARD_4_3
ASPECT_RATIOS["a4"] = A4_LANDSCAPE  # Default A4 is landscape for presentations
ASPECT_RATIOS["letter"] = LETTER_LANDSCAPE


def get_aspect_ratio(key: str) -> AspectRatio:
    """Get aspect ratio by key.

    Args:
        key: Aspect ratio key (e.g., "16_9", "a4_landscape")

    Returns:
        AspectRatio instance

    Raises:
        KeyError: If key not found
    """
    if key not in ASPECT_RATIOS:
        available = ", ".join(sorted(ASPECT_RATIOS.keys()))
        raise KeyError(f"Unknown aspect ratio '{key}'. Available: {available}")
    return ASPECT_RATIOS[key]


def list_aspect_ratios(
    category: AspectRatioCategory | None = None,
) -> list[AspectRatio]:
    """List all aspect ratios, optionally filtered by category.

    Args:
        category: Optional category filter

    Returns:
        List of AspectRatio instances
    """
    ratios = list(ASPECT_RATIOS.values())

    # Remove aliases (duplicates)
    seen = set()
    unique = []
    for r in ratios:
        if r.key not in seen:
            seen.add(r.key)
            unique.append(r)

    if category:
        unique = [r for r in unique if r.category == category]

    return sorted(unique, key=lambda r: r.key)


def create_custom_aspect_ratio(
    name: str,
    key: str,
    width: float,
    height: float,
    unit: str = "emu",
) -> AspectRatio:
    """Create a custom aspect ratio.

    Args:
        name: Human-readable name
        key: Machine identifier
        width: Width value
        height: Height value
        unit: Unit type - "emu", "inches", "mm", or "ratio"

    Returns:
        New AspectRatio instance
    """
    if unit == "emu":
        return AspectRatio(
            name=name,
            key=key,
            width_emu=int(width),
            height_emu=int(height),
            category=AspectRatioCategory.CUSTOM,
        )
    elif unit == "inches":
        return AspectRatio.from_inches(
            name=name,
            key=key,
            width_inches=width,
            height_inches=height,
            category=AspectRatioCategory.CUSTOM,
        )
    elif unit == "mm":
        return AspectRatio.from_mm(
            name=name,
            key=key,
            width_mm=width,
            height_mm=height,
            category=AspectRatioCategory.CUSTOM,
        )
    elif unit == "ratio":
        return AspectRatio.from_ratio(
            name=name,
            key=key,
            width_ratio=int(width),
            height_ratio=int(height),
            category=AspectRatioCategory.CUSTOM,
        )
    else:
        raise ValueError(f"Unknown unit: {unit}. Use 'emu', 'inches', 'mm', or 'ratio'")


__all__ = [
    "AspectRatio",
    "AspectRatioCategory",
    "ASPECT_RATIOS",
    "get_aspect_ratio",
    "list_aspect_ratios",
    "create_custom_aspect_ratio",
    # Standard ratios
    "WIDESCREEN_16_9",
    "WIDESCREEN_16_10",
    "STANDARD_4_3",
    # Print ratios
    "A4_LANDSCAPE",
    "A4_PORTRAIT",
    "A3_LANDSCAPE",
    "A3_PORTRAIT",
    "LETTER_LANDSCAPE",
    "LETTER_PORTRAIT",
]
