"""Color palette generation with canonical naming.

This module bridges DrawingML (semantic + transforms) and ODF (pre-computed hex).

DrawingML stores: accent1 + <a:tint val="30000"/>
ODF stores: fo:background-color="#5F78BF" with style name "Accent1.Tint30"

The palette generator computes ALL variations once, enabling:
- DrawingML emitters to use theme refs + transforms
- ODF emitters to use pre-computed hex values
- Consistent visual output across all office suites
"""

from __future__ import annotations

from dataclasses import dataclass, field
from enum import Enum
from typing import Dict, List, Optional

from .models import Color
from .parser import parse_color


class ThemeSlot(str, Enum):
    """OOXML theme color semantic slots."""

    DK1 = "dk1"
    LT1 = "lt1"
    DK2 = "dk2"
    LT2 = "lt2"
    ACCENT1 = "accent1"
    ACCENT2 = "accent2"
    ACCENT3 = "accent3"
    ACCENT4 = "accent4"
    ACCENT5 = "accent5"
    ACCENT6 = "accent6"
    HLINK = "hlink"
    FOL_HLINK = "folHlink"


class TransformType(str, Enum):
    """Color transform types."""

    NONE = "none"
    TINT = "tint"  # Lighten (mix with white)
    SHADE = "shade"  # Darken (mix with black)


@dataclass(frozen=True)
class ColorVariant:
    """A single color variant with both DrawingML and ODF representations.

    Attributes:
        name: Canonical name (e.g., "Accent1.Tint30")
        slot: Theme slot reference for DrawingML
        transform: Transform type (tint/shade/none)
        strength: Transform strength in percent (0-100)
        hex: Pre-computed hex color for ODF
        color: Color object for further calculations
    """

    name: str
    slot: ThemeSlot
    transform: TransformType
    strength: int  # 0-100 percent
    hex: str
    color: Color


@dataclass
class ThemePalette:
    """Complete color palette with all variants.

    Holds:
    - 12 base theme colors (dk1, lt1, accent1-6, etc.)
    - All tint/shade variants with canonical names
    - Lookup by name for ODF emission
    - Lookup by slot+transform for DrawingML emission
    """

    # Base colors (the 12 theme slots)
    bases: Dict[ThemeSlot, ColorVariant] = field(default_factory=dict)

    # All variants indexed by canonical name
    by_name: Dict[str, ColorVariant] = field(default_factory=dict)

    # Variants indexed by (slot, transform, strength)
    by_transform: Dict[tuple[ThemeSlot, TransformType, int], ColorVariant] = field(
        default_factory=dict
    )

    def get(self, name: str) -> Optional[ColorVariant]:
        """Get variant by canonical name."""
        return self.by_name.get(name)

    def get_base(self, slot: ThemeSlot) -> Optional[ColorVariant]:
        """Get base color for a slot."""
        return self.bases.get(slot)

    def get_variant(
        self, slot: ThemeSlot, transform: TransformType, strength: int
    ) -> Optional[ColorVariant]:
        """Get variant by slot and transform."""
        return self.by_transform.get((slot, transform, strength))


def _slot_to_name_prefix(slot: ThemeSlot) -> str:
    """Convert slot to canonical name prefix."""
    mapping = {
        ThemeSlot.DK1: "Dark1",
        ThemeSlot.LT1: "Light1",
        ThemeSlot.DK2: "Dark2",
        ThemeSlot.LT2: "Light2",
        ThemeSlot.ACCENT1: "Accent1",
        ThemeSlot.ACCENT2: "Accent2",
        ThemeSlot.ACCENT3: "Accent3",
        ThemeSlot.ACCENT4: "Accent4",
        ThemeSlot.ACCENT5: "Accent5",
        ThemeSlot.ACCENT6: "Accent6",
        ThemeSlot.HLINK: "Hyperlink",
        ThemeSlot.FOL_HLINK: "FollowedHyperlink",
    }
    return mapping[slot]


def _compute_tint(color: Color, strength: int) -> Color:
    """Compute tint (mix with white) using OKLab.

    Args:
        color: Base color
        strength: Tint strength 0-100 (0=no change, 100=white)

    Returns:
        Tinted color
    """
    # OKLab lightness adjustment for perceptual uniformity
    delta = (strength / 100.0) * 0.5  # Max 0.5 lightness increase
    return color.lighten(delta)


def _compute_shade(color: Color, strength: int) -> Color:
    """Compute shade (mix with black) using OKLab.

    Args:
        color: Base color
        strength: Shade strength 0-100 (0=no change, 100=black)

    Returns:
        Shaded color
    """
    delta = (strength / 100.0) * 0.5  # Max 0.5 lightness decrease
    return color.lighten(-delta)


def generate_palette(
    theme_colors: Dict[str, str],
    tint_levels: List[int] | None = None,
    shade_levels: List[int] | None = None,
) -> ThemePalette:
    """Generate complete color palette from theme colors.

    Args:
        theme_colors: Dict mapping slot names to hex colors
                      e.g., {"accent1": "#4472C4", "dk1": "#000000"}
        tint_levels: List of tint percentages to generate (default: [20, 40, 60, 80])
        shade_levels: List of shade percentages to generate (default: [20, 40])

    Returns:
        ThemePalette with all base colors and variants
    """
    if tint_levels is None:
        tint_levels = [20, 40, 60, 80]
    if shade_levels is None:
        shade_levels = [20, 40]

    palette = ThemePalette()

    for slot in ThemeSlot:
        hex_color = theme_colors.get(slot.value)
        if not hex_color:
            continue

        # Parse base color
        color = parse_color(hex_color)
        if color is None:
            continue
        name_prefix = _slot_to_name_prefix(slot)

        # Create base variant
        base_name = f"{name_prefix}.Base"
        base_variant = ColorVariant(
            name=base_name,
            slot=slot,
            transform=TransformType.NONE,
            strength=0,
            hex=color.to_hex(),
            color=color,
        )
        palette.bases[slot] = base_variant
        palette.by_name[base_name] = base_variant
        palette.by_transform[(slot, TransformType.NONE, 0)] = base_variant

        # Generate tints
        for level in tint_levels:
            tinted = _compute_tint(color, level)
            tint_name = f"{name_prefix}.Tint{level}"
            variant = ColorVariant(
                name=tint_name,
                slot=slot,
                transform=TransformType.TINT,
                strength=level,
                hex=tinted.to_hex(),
                color=tinted,
            )
            palette.by_name[tint_name] = variant
            palette.by_transform[(slot, TransformType.TINT, level)] = variant

        # Generate shades
        for level in shade_levels:
            shaded = _compute_shade(color, level)
            shade_name = f"{name_prefix}.Shade{level}"
            variant = ColorVariant(
                name=shade_name,
                slot=slot,
                transform=TransformType.SHADE,
                strength=level,
                hex=shaded.to_hex(),
                color=shaded,
            )
            palette.by_name[shade_name] = variant
            palette.by_transform[(slot, TransformType.SHADE, level)] = variant

    return palette


__all__ = [
    "ThemeSlot",
    "TransformType",
    "ColorVariant",
    "ThemePalette",
    "generate_palette",
]
