"""Adapter for Figma / Tokens Studio DTCG exports.

Transforms the flat token structure that Figma, Tokens Studio, and
Style Dictionary produce into the internal payload format expected by
``build_document_ir()``.

Usage::

    from tokenmoulds.dtcg.parser import parse_tokens
    from tokenmoulds.dtcg.figma_adapter import adapt_figma_tokens
    from tokenmoulds.ir import build_document_ir

    tf = parse_tokens(Path("figma-export.tokens.json"))
    payload = adapt_figma_tokens(tf)
    ir = build_document_ir(payload, "acme", "US")
"""

from __future__ import annotations

from typing import Any, Dict, Optional

from tokenmoulds.dtcg.types import (
    ColorValue,
    DimensionValue,
    TokenFile,
    TokenGroup,
    TypographyValue,
)


def adapt_figma_tokens(token_file: TokenFile) -> Dict[str, Any]:
    """Convert a parsed Figma/Tokens Studio DTCG file to internal payload.

    The returned dict has the shape ``build_document_ir()`` expects:
    ``colors``, ``typography``, ``styles``, ``spacing``, etc.
    """
    result: Dict[str, Any] = {}

    # --- colors -----------------------------------------------------------
    color_group = token_file.groups.get("color")
    if color_group:
        result["colors"] = _adapt_colors(color_group)

    # --- typography -------------------------------------------------------
    typo_group = token_file.groups.get("typography")
    if typo_group:
        fonts, styles_from_typo = _adapt_typography(typo_group)
        result["typography"] = fonts
        # Merge typography-derived styles into styles
        result.setdefault("styles", {}).update(styles_from_typo)

    # --- spacing ----------------------------------------------------------
    spacing_group = token_file.groups.get("spacing")
    if spacing_group:
        result["spacing"] = _adapt_spacing(spacing_group)

    # --- fill in required defaults ----------------------------------------
    _ensure_required_keys(result)

    return result


# ---------------------------------------------------------------------------
# Colors
# ---------------------------------------------------------------------------


def _adapt_colors(group: TokenGroup) -> Dict[str, Any]:
    """Map Figma color groups to internal palette + theme_mapping."""
    palette: Dict[str, Any] = {}
    theme_mapping: Dict[str, str] = {}

    for name, subgroup in group.groups.items():
        # Each subgroup is a color scale (primary, secondary, neutral, ...)
        scale: Dict[str, str] = {}
        for token_name, token in subgroup.tokens.items():
            hex_val = _color_to_hex(token.value)
            if hex_val:
                scale[token_name] = hex_val
        if scale:
            palette[name] = scale

    # Also pick up any top-level color tokens
    for token_name, token in group.tokens.items():
        hex_val = _color_to_hex(token.value)
        if hex_val:
            palette[token_name] = hex_val

    # Derive OOXML theme mapping from palette
    primary = palette.get("primary", {})
    secondary = palette.get("secondary", {})
    neutral = palette.get("neutral", {})

    from tokenmoulds.design.color_defaults import DEFAULT_THEME_MAPPING

    theme_mapping = {
        "dk1": DEFAULT_THEME_MAPPING["dk1"],
        "lt1": DEFAULT_THEME_MAPPING["lt1"],
        "dk2": neutral.get("800", neutral.get("900", DEFAULT_THEME_MAPPING["dk2"])),
        "lt2": neutral.get("50", neutral.get("100", DEFAULT_THEME_MAPPING["lt2"])),
        "accent1": primary.get(
            "600", primary.get("500", DEFAULT_THEME_MAPPING["accent1"])
        ),
        "accent2": secondary.get(
            "600", secondary.get("500", DEFAULT_THEME_MAPPING["accent2"])
        ),
        "accent3": primary.get(
            "400", primary.get("300", DEFAULT_THEME_MAPPING["accent3"])
        ),
        "accent4": secondary.get(
            "400", secondary.get("300", DEFAULT_THEME_MAPPING["accent4"])
        ),
        "accent5": primary.get(
            "800", primary.get("700", DEFAULT_THEME_MAPPING["accent5"])
        ),
        "accent6": secondary.get(
            "800", secondary.get("700", DEFAULT_THEME_MAPPING["accent6"])
        ),
        "hlink": primary.get("600", primary.get("500", DEFAULT_THEME_MAPPING["hlink"])),
        "folHlink": secondary.get(
            "700", secondary.get("600", DEFAULT_THEME_MAPPING["folHlink"])
        ),
    }

    return {"palette": palette, "theme_mapping": theme_mapping}


def _color_to_hex(value: Any) -> Optional[str]:
    if isinstance(value, ColorValue) and value.hex:
        return value.hex if value.hex.startswith("#") else f"#{value.hex}"
    if isinstance(value, str) and value.startswith("#"):
        return value
    return None


# ---------------------------------------------------------------------------
# Typography
# ---------------------------------------------------------------------------


def _adapt_typography(group: TokenGroup) -> tuple[Dict[str, Any], Dict[str, Any]]:
    """Extract fonts dict and styles dict from Figma typography tokens."""
    fonts: Dict[str, Any] = {}
    styles: Dict[str, Any] = {}

    heading_group = group.groups.get("heading")
    body_group = group.groups.get("body")

    # Extract font families
    heading_font = _extract_font_family(heading_group) or "Inter"
    body_font = _extract_font_family(body_group) or heading_font

    fonts["fonts"] = {
        "minor": body_font,  # body
        "major": heading_font,  # heading
        "sans": body_font,
        "serif": heading_font,
    }

    # Build heading styles
    if heading_group:
        heading_levels: Dict[str, Any] = {}
        heading_color = "#111827"
        for token_name, token in heading_group.tokens.items():
            if not isinstance(token.value, TypographyValue):
                continue
            if not token_name.startswith("h"):
                continue
            tv = token.value
            size_pt = _dim_to_pt(tv.font_size) if tv.font_size else 24.0
            size_twips = size_pt * 20
            level = int(token_name.lstrip("h"))
            heading_levels[token_name] = {
                "font_size_pt": size_pt,
                "font_size_twips": size_twips,
                "outline_level": level - 1,
                "paragraph_spacing": {
                    "before_twips": int(size_pt * 10),  # ~0.5x font size
                    "after_twips": int(size_pt * 5),  # ~0.25x font size
                },
            }

        styles["heading"] = {
            "base": {
                "font_family": heading_font,
                "color": heading_color,
            },
            "levels": heading_levels,
        }

    # Build body styles
    body_size_pt = 11.0
    body_color = "#374151"
    if body_group:
        for token_name, token in body_group.tokens.items():
            if isinstance(token.value, TypographyValue) and token_name == "regular":
                body_size_pt = (
                    _dim_to_pt(token.value.font_size) if token.value.font_size else 11.0
                )
                break

    styles["body"] = {
        "base": {
            "font_family": body_font,
            "font_size_pt": body_size_pt,
            "color": body_color,
            "paragraph_spacing": {
                "before_twips": 0,
                "after_twips": int(body_size_pt * 8),  # ~0.4x line height
            },
        }
    }

    # Build scale_pt from heading levels
    scale_pt: Dict[str, float] = {"body": body_size_pt}
    if "heading" in styles:
        for level_name, level_data in styles["heading"]["levels"].items():
            scale_pt[level_name] = level_data["font_size_pt"]
    fonts["scale_pt"] = scale_pt

    return fonts, styles


def _extract_font_family(group: Optional[TokenGroup]) -> Optional[str]:
    if group is None:
        return None
    ff_token = group.tokens.get("fontFamily")
    if ff_token and isinstance(ff_token.value, str):
        return ff_token.value
    # Try to get from first typography token
    for token in group.tokens.values():
        if isinstance(token.value, TypographyValue):
            ff = token.value.font_family
            if isinstance(ff, list):
                return ff[0] if ff else None
            return ff
    return None


def _dim_to_pt(value: Any) -> float:
    if isinstance(value, DimensionValue):
        return value.to_pt()
    if isinstance(value, (int, float)):
        return float(value)
    return 12.0


# ---------------------------------------------------------------------------
# Spacing
# ---------------------------------------------------------------------------


def _adapt_spacing(group: TokenGroup) -> Dict[str, Any]:
    scale_pt: Dict[str, float] = {}
    for name, token in group.tokens.items():
        if isinstance(token.value, DimensionValue):
            scale_pt[name] = token.value.to_pt()
    return {
        "scale_pt": scale_pt,
        "base_pt": min(scale_pt.values()) if scale_pt else 4.0,
    }


# ---------------------------------------------------------------------------
# Defaults
# ---------------------------------------------------------------------------


def _ensure_required_keys(payload: Dict[str, Any]) -> None:
    """Fill in keys that build_document_ir() requires but Figma doesn't export."""
    styles = payload.setdefault("styles", {})

    if "body" not in styles:
        styles["body"] = {
            "base": {
                "font_family": "Inter",
                "font_size_pt": 11.0,
                "color": "#374151",
                "paragraph_spacing": {"before_twips": 0, "after_twips": 160},
            }
        }

    if "heading" not in styles:
        styles["heading"] = {
            "base": {"font_family": "Inter", "color": "#111827"},
            "levels": {
                "h1": {
                    "font_size_pt": 28.0,
                    "font_size_twips": 560,
                    "outline_level": 0,
                    "paragraph_spacing": {"before_twips": 280, "after_twips": 140},
                },
                "h2": {
                    "font_size_pt": 22.0,
                    "font_size_twips": 440,
                    "outline_level": 1,
                    "paragraph_spacing": {"before_twips": 220, "after_twips": 110},
                },
                "h3": {
                    "font_size_pt": 16.0,
                    "font_size_twips": 320,
                    "outline_level": 2,
                    "paragraph_spacing": {"before_twips": 160, "after_twips": 80},
                },
            },
        }

    if "list" not in styles:
        from tokenmoulds.design.layout_defaults import (
            list_hanging_twips,
            list_indent_twips,
        )

        styles["list"] = {
            "base": {
                "indent_twips": list_indent_twips(),
                "hanging_twips": list_hanging_twips(),
            }
        }

    if "quote" not in styles:
        from tokenmoulds.design.layout_defaults import (
            quote_border_width_twips,
            quote_indent_twips,
        )

        body = styles["body"]["base"]
        styles["quote"] = {
            "base": {
                "font_family": body["font_family"],
                "font_size_pt": body["font_size_pt"],
                "color": "#4B5563",
                "italic": True,
                "indent_twips": quote_indent_twips(),
                "border_color": payload.get("colors", {})
                .get("theme_mapping", {})
                .get("accent1", "#2563EB"),
                "border_width_twips": quote_border_width_twips(),
                "paragraph_spacing": {"before_twips": 160, "after_twips": 160},
            }
        }

    if "table" not in styles:
        from tokenmoulds.design.layout_defaults import table_cell_padding_twips

        body = styles["body"]["base"]
        styles["table"] = {
            "base": {
                "font_family": body["font_family"],
                "font_size_pt": body["font_size_pt"] * 0.85,
                "border_width_twips": 15,
                "border_color": "#E5E7EB",
                "header_fill": "#F3F4F6",
                "body_fill": "#FFFFFF",
                "cell_padding_twips": table_cell_padding_twips(),
            }
        }
