"""Advanced token engine and generator for TokenMoulds.

Delegates typographic calculations to ``DesignResolutionEngine``.
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any, Dict, Mapping, Optional

from tokenmoulds.design.engine import (
    DesignResolutionEngine,
    LineHeightMode,
    ResolvedDesign,
)
from tokenmoulds.fonts.superfamilies import resolve_monospace
from tokenmoulds.dtcg.locale_data import LOCALE_DATA as _LOCALE_DATA
from tokenmoulds.dtcg.font_metrics import (
    BrandAdapter as _BrandAdapter,
    FontConfigAdapter as _FontConfigAdapter,
    FontMetricsAdapter as _FontMetricsAdapter,
    extract_font_metrics,
    select_scale_ratio as _select_scale_ratio,
)
from tokenmoulds.dtcg.token_calculators import (
    calculate_colors,
    calculate_components,
    calculate_kerning,
    calculate_layout,
    calculate_ooxml_mappings,
    calculate_spacing,
    calculate_typography,
)
from tokenmoulds.dtcg.token_builders import (
    build_color_tokens,
    build_grid_tokens,
    build_spacing_tokens,
    build_style_tokens,
    build_typography_tokens,
)


@dataclass(frozen=True)
class EngineInputs:
    font_pair: Dict[str, str]
    base_colors: Dict[str, str]
    locale: str
    brand_tone: float
    content_density: float
    accessibility_level: str
    base_grid_pt: float
    font_metrics_cache: Optional[str]
    fonts_dir: Optional[str]


class AdvancedTokenEngine:
    """Font-metric-aware formulaic engine (ported from design-tokens)."""

    def __init__(
        self,
        *,
        font_pair: Mapping[str, str],
        base_colors: Mapping[str, str],
        locale: str,
        brand_tone: float = 0.6,
        content_density: float = 0.4,
        accessibility_level: str = "AAA",
        base_grid_pt: float = 11.0,
        font_metrics_cache: Optional[str] = None,
        fonts_dir: Optional[str] = None,
    ) -> None:
        self.font_pair = dict(font_pair)
        self.base_colors = dict(base_colors)
        self.locale = locale
        self.brand_tone = brand_tone
        self.content_density = content_density
        self.accessibility_level = accessibility_level
        self.base_grid_pt = base_grid_pt
        self.font_metrics_cache = font_metrics_cache
        self.fonts_dir = fonts_dir

        self._mono_font = self._resolve_mono_font()
        self.font_metrics = extract_font_metrics(
            font_pair=self.font_pair,
            font_metrics_cache=self.font_metrics_cache,
            fonts_dir=self.fonts_dir,
        )
        self.resolved_design = self._resolve_design()
        self.baseline_emu = self.resolved_design.baseline.grid_emu
        self.tokens = self._calculate_all_tokens()

    def get_tokens(self) -> Dict[str, Any]:
        return self.tokens

    def _resolve_mono_font(self) -> str:
        """Resolve monospace font from font_pair or superfamily lookup."""
        body = self.font_pair.get("minor") or self.font_pair.get("sans") or "Inter"
        heading = self.font_pair.get("major") or self.font_pair.get("serif") or body
        return resolve_monospace(body, heading, self.font_pair.get("mono"))

    def _resolve_design(self) -> ResolvedDesign:
        """Create a DesignResolutionEngine and resolve the full design system."""
        primary_metrics = self.font_metrics["primary"]
        serif_metrics = self.font_metrics.get("serif", primary_metrics)

        body_adapter = _FontMetricsAdapter(
            x_height=primary_metrics["x_height"],
            units_per_em=primary_metrics["units_per_em"],
            cap_height=primary_metrics["cap_height"],
            ascender=primary_metrics["ascender"],
            descender=primary_metrics["descender"],
        )
        heading_adapter = _FontMetricsAdapter(
            x_height=serif_metrics["x_height"],
            units_per_em=serif_metrics["units_per_em"],
            cap_height=serif_metrics["cap_height"],
            ascender=serif_metrics["ascender"],
            descender=serif_metrics["descender"],
        )

        body_family = (
            self.font_pair.get("minor") or self.font_pair.get("sans") or "Inter"
        )
        heading_family = (
            self.font_pair.get("major") or self.font_pair.get("serif") or body_family
        )

        font_config = _FontConfigAdapter(
            body=body_adapter,
            heading=heading_adapter,
            _body_family=body_family,
            _heading_family=heading_family,
            _mono_family=self._mono_font,
        )
        brand = _BrandAdapter(tone=self.brand_tone, density=self.content_density)
        scale_ratio = _select_scale_ratio(self.brand_tone)

        engine = DesignResolutionEngine(
            fonts=font_config,
            brand=brand,
            locale=self.locale,
            colors={},
            base_font_size_pt=self.base_grid_pt,
            scale_ratio=scale_ratio,
            line_height_mode=LineHeightMode.SNAPPED,
        )
        return engine.resolve()

    def _calculate_all_tokens(self) -> Dict[str, Any]:
        tokens: Dict[str, Any] = {}
        typography = calculate_typography(
            resolved_design=self.resolved_design,
            font_pair=self.font_pair,
            mono_font=self._mono_font,
        )
        colors = calculate_colors(base_colors=self.base_colors)
        spacing = calculate_spacing(resolved_design=self.resolved_design)
        kerning = calculate_kerning(
            resolved_design=self.resolved_design,
            baseline_emu=self.baseline_emu,
        )
        locale = self._calculate_locale_settings()
        layout = calculate_layout(
            baseline_emu=self.baseline_emu,
            locale_settings=locale,
            brand_tone=self.brand_tone,
        )

        tokens["typography"] = typography
        tokens["colors"] = colors
        tokens["spacing"] = spacing
        tokens["kerning"] = kerning
        tokens["layout"] = layout
        tokens["locale"] = locale
        tokens["resolved_design"] = self.resolved_design
        tokens["components"] = calculate_components(
            typography=typography,
            colors=colors,
            spacing=spacing,
            layout=layout,
            locale=locale,
            baseline_emu=self.baseline_emu,
            base_colors=self.base_colors,
        )
        tokens["ooxml"] = calculate_ooxml_mappings(
            typography=typography,
            colors=colors,
            locale=locale,
            locale_code=self.locale,
            font_pair=self.font_pair,
            base_colors=self.base_colors,
        )
        return tokens

    def _calculate_locale_settings(self) -> Dict[str, Any]:
        return _LOCALE_DATA.get(self.locale, _LOCALE_DATA["US"])


class TokenGenerator:
    """Generate canonical tokens using the advanced engine."""

    def generate(self, inputs: Mapping[str, Any]) -> Dict[str, Any]:
        normalized = normalize_inputs(inputs)
        engine = AdvancedTokenEngine(
            font_pair=normalized.font_pair,
            base_colors=normalized.base_colors,
            locale=normalized.locale,
            brand_tone=normalized.brand_tone,
            content_density=normalized.content_density,
            accessibility_level=normalized.accessibility_level,
            base_grid_pt=normalized.base_grid_pt,
            font_metrics_cache=normalized.font_metrics_cache,
            fonts_dir=normalized.fonts_dir,
        )
        raw = engine.get_tokens()
        baseline_emu = engine.baseline_emu
        resolved_design: Optional[ResolvedDesign] = raw.get("resolved_design")
        colors = build_color_tokens(raw, normalized.base_colors)
        typography = build_typography_tokens(raw, normalized.font_pair, baseline_emu)
        spacing = build_spacing_tokens(baseline_emu)
        grid = build_grid_tokens(baseline_emu)
        styles = build_style_tokens(
            colors,
            typography,
            spacing,
            brand_tone=normalized.brand_tone,
            resolved_design=resolved_design,
        )
        return {
            "inputs": inputs_for_output(normalized),
            "grid": grid,
            "colors": colors,
            "typography": typography,
            "spacing": spacing,
            "styles": styles,
            "kerning": raw.get("kerning", {}),
            "layout": raw.get("layout", {}),
            "locale": raw.get("locale", {}),
            "components": raw.get("components", {}),
            "ooxml": raw.get("ooxml", {}),
        }


def normalize_inputs(raw: Mapping[str, Any]) -> EngineInputs:
    font_pair_raw = raw.get("font_pair", {})
    major = str(font_pair_raw.get("major") or font_pair_raw.get("sans") or "Inter")
    minor = str(font_pair_raw.get("minor") or font_pair_raw.get("sans") or major)
    serif = str(font_pair_raw.get("serif") or "Roboto Slab")
    font_pair: dict[str, str] = {
        "sans": minor,
        "serif": serif,
        "major": major,
        "minor": minor,
    }
    mono_raw = font_pair_raw.get("mono")
    if mono_raw:
        font_pair["mono"] = str(mono_raw)

    base_colors_raw = raw.get("base_colors", {})
    base_colors = {
        "primary": str(base_colors_raw.get("primary") or "#2563EB"),
        "secondary": str(base_colors_raw.get("secondary") or "#E11D48"),
    }

    locale = str(raw.get("locale") or "US")
    _brand_tone = raw.get("brand_tone")
    brand_tone = float(_brand_tone) if _brand_tone is not None else 0.6
    _content_density = raw.get("content_density")
    content_density = float(_content_density) if _content_density is not None else 0.4
    accessibility_level = str(raw.get("accessibility_level") or "AA")
    _base_grid_pt = raw.get("base_grid_pt")
    base_grid_pt = float(_base_grid_pt) if _base_grid_pt is not None else 11
    font_metrics_cache = raw.get("font_metrics_cache")
    font_metrics_cache = str(font_metrics_cache) if font_metrics_cache else None
    fonts_dir = raw.get("fonts_dir")
    fonts_dir = str(fonts_dir) if fonts_dir else None

    return EngineInputs(
        font_pair=font_pair,
        base_colors=base_colors,
        locale=locale,
        brand_tone=brand_tone,
        content_density=content_density,
        accessibility_level=accessibility_level,
        base_grid_pt=base_grid_pt,
        font_metrics_cache=font_metrics_cache,
        fonts_dir=fonts_dir,
    )


def inputs_for_output(inputs: EngineInputs) -> Dict[str, Any]:
    payload = {
        "font_pair": inputs.font_pair,
        "base_colors": inputs.base_colors,
        "locale": inputs.locale,
        "brand_tone": inputs.brand_tone,
        "content_density": inputs.content_density,
        "accessibility_level": inputs.accessibility_level,
        "base_grid_pt": inputs.base_grid_pt,
    }
    if inputs.font_metrics_cache:
        payload["font_metrics_cache"] = inputs.font_metrics_cache
    if inputs.fonts_dir:
        payload["fonts_dir"] = inputs.fonts_dir
    return payload
