"""Convert TokenMoulds internal format to/from DTCG format.

This module bridges the gap between the internal TokenMoulds token
format and the W3C DTCG standard format.
"""

from __future__ import annotations

from typing import Any, Dict, Optional

from tokenmoulds.design.units import EMU_PER_POINT
from tokenmoulds.dtcg.types import (
    ColorValue,
    DimensionValue,
    Token,
    TokenExtensions,
    TokenFile,
    TokenGroup,
    TokenType,
    TypographyValue,
)


def convert_to_dtcg(
    token_payload: Dict[str, Any],
    metadata: Optional[Dict[str, Any]] = None,
) -> TokenFile:
    """Convert TokenMoulds internal token format to DTCG format.

    Args:
        token_payload: The internal token payload from TokenGenerator
        metadata: Optional metadata to include

    Returns:
        TokenFile in DTCG format
    """
    token_file = TokenFile()

    # Convert colors
    if "colors" in token_payload:
        token_file.groups["color"] = _convert_colors(token_payload["colors"])

    # Convert typography
    if "typography" in token_payload:
        token_file.groups["typography"] = _convert_typography(
            token_payload["typography"]
        )

    # Convert spacing
    if "spacing" in token_payload:
        token_file.groups["spacing"] = _convert_spacing(token_payload["spacing"])

    # Convert styles to typography tokens
    if "styles" in token_payload:
        token_file.groups["style"] = _convert_styles(token_payload["styles"])

    return token_file


def _convert_colors(colors: Dict[str, Any]) -> TokenGroup:
    """Convert colors section to DTCG format."""
    group = TokenGroup(name="color", type=TokenType.COLOR)

    # Convert palette (handles nested structure like primary.base, primary.light, etc.)
    if "palette" in colors:
        palette_group = TokenGroup(name="palette", type=TokenType.COLOR)
        for color_name, color_value in colors["palette"].items():
            if isinstance(color_value, dict):
                # Nested structure (e.g., primary: {base, light, dark, contrast})
                nested_group = TokenGroup(name=color_name, type=TokenType.COLOR)
                for variant_name, hex_value in color_value.items():
                    nested_group.tokens[variant_name] = Token(
                        name=variant_name,
                        value=ColorValue.from_hex(hex_value),
                        type=TokenType.COLOR,
                    )
                palette_group.groups[color_name] = nested_group
            else:
                # Simple hex string
                palette_group.tokens[color_name] = Token(
                    name=color_name,
                    value=ColorValue.from_hex(color_value),
                    type=TokenType.COLOR,
                )
        group.groups["palette"] = palette_group

    # Convert theme mapping (with OOXML extensions)
    if "theme_mapping" in colors:
        theme_group = TokenGroup(
            name="theme",
            type=TokenType.COLOR,
            description="Office theme color slots",
        )
        for slot_name, hex_value in colors["theme_mapping"].items():
            theme_group.tokens[slot_name] = Token(
                name=slot_name,
                value=ColorValue.from_hex(hex_value),
                type=TokenType.COLOR,
                extensions=TokenExtensions(
                    ooxml_theme_slot=slot_name,
                    derivation_source="theme_mapping",
                ),
            )
        group.groups["theme"] = theme_group

    # Convert text colors
    if "text" in colors:
        text_group = TokenGroup(name="text", type=TokenType.COLOR)
        for name, hex_value in colors["text"].items():
            text_group.tokens[name] = Token(
                name=name,
                value=ColorValue.from_hex(hex_value),
                type=TokenType.COLOR,
            )
        group.groups["text"] = text_group

    return group


def _convert_typography(typography: Dict[str, Any]) -> TokenGroup:
    """Convert typography section to DTCG format."""
    group = TokenGroup(name="typography")

    # Convert fonts
    if "fonts" in typography:
        font_group = TokenGroup(name="font", type=TokenType.FONT_FAMILY)
        fonts = typography["fonts"]
        for role, font_name in fonts.items():
            font_group.tokens[role] = Token(
                name=role,
                value=font_name,
                type=TokenType.FONT_FAMILY,
                description=f"Font for {role} text",
            )
        group.groups["font"] = font_group

    # Convert scale to dimension tokens
    if "scale_emu" in typography:
        scale_group = TokenGroup(
            name="scale",
            type=TokenType.DIMENSION,
            description="Typography scale in EMU",
        )
        scale_emu = typography["scale_emu"]
        # Handle both dict and list formats
        if isinstance(scale_emu, dict):
            for name, emu_value in scale_emu.items():
                pt_value = emu_value / EMU_PER_POINT
                scale_group.tokens[name] = Token(
                    name=name,
                    value=DimensionValue(value=round(pt_value, 2), unit="pt"),
                    type=TokenType.DIMENSION,
                    extensions=TokenExtensions(
                        other={"com.tokenmoulds": {"emu": emu_value}},
                    ),
                )
        else:
            # Legacy list format
            for i, emu_value in enumerate(scale_emu):
                pt_value = emu_value / EMU_PER_POINT
                scale_group.tokens[f"level{i}"] = Token(
                    name=f"level{i}",
                    value=DimensionValue(value=round(pt_value, 2), unit="pt"),
                    type=TokenType.DIMENSION,
                    extensions=TokenExtensions(
                        other={"com.tokenmoulds": {"emu": emu_value}},
                    ),
                )
        group.groups["scale"] = scale_group

    # Add baseline grid
    if "baseline_emu" in typography:
        baseline_emu = typography["baseline_emu"]
        group.tokens["baseline"] = Token(
            name="baseline",
            value=DimensionValue(value=baseline_emu / EMU_PER_POINT, unit="pt"),
            type=TokenType.DIMENSION,
            description="Baseline grid unit",
            extensions=TokenExtensions(
                other={"com.tokenmoulds": {"emu": baseline_emu}},
            ),
        )

    return group


def _convert_spacing(spacing: Dict[str, Any]) -> TokenGroup:
    """Convert spacing section to DTCG format."""
    group = TokenGroup(name="spacing", type=TokenType.DIMENSION)

    # Convert scale
    if "scale_emu" in spacing:
        scale_emu = spacing["scale_emu"]
        # Handle both dict and list formats
        if isinstance(scale_emu, dict):
            for name, emu_value in scale_emu.items():
                pt_value = emu_value / EMU_PER_POINT
                group.tokens[name] = Token(
                    name=name,
                    value=DimensionValue(value=round(pt_value, 2), unit="pt"),
                    type=TokenType.DIMENSION,
                    extensions=TokenExtensions(
                        other={"com.tokenmoulds": {"emu": emu_value}},
                    ),
                )
        else:
            # Legacy list format
            scale_names = ["xs", "sm", "md", "lg", "xl", "2xl", "3xl", "4xl"]
            for i, emu_value in enumerate(scale_emu):
                name = scale_names[i] if i < len(scale_names) else f"level{i}"
                pt_value = emu_value / EMU_PER_POINT
                group.tokens[name] = Token(
                    name=name,
                    value=DimensionValue(value=round(pt_value, 2), unit="pt"),
                    type=TokenType.DIMENSION,
                    extensions=TokenExtensions(
                        other={"com.tokenmoulds": {"emu": emu_value}},
                    ),
                )

    # Add paragraph spacing if present
    if "paragraph_emu" in spacing:
        para = spacing["paragraph_emu"]
        if "before" in para:
            group.tokens["paragraphBefore"] = Token(
                name="paragraphBefore",
                value=DimensionValue(value=para["before"] / EMU_PER_POINT, unit="pt"),
                type=TokenType.DIMENSION,
            )
        if "after" in para:
            group.tokens["paragraphAfter"] = Token(
                name="paragraphAfter",
                value=DimensionValue(value=para["after"] / EMU_PER_POINT, unit="pt"),
                type=TokenType.DIMENSION,
            )

    return group


def _convert_styles(styles: Dict[str, Any]) -> TokenGroup:
    """Convert styles section to DTCG typography tokens."""
    group = TokenGroup(name="style")

    # Convert heading styles
    if "heading" in styles:
        heading_group = TokenGroup(name="heading", type=TokenType.TYPOGRAPHY)
        heading = styles["heading"]

        # Get base heading properties
        base = heading.get("base", {})
        base_font = base.get("font_family", "Arial")
        base_weight = base.get("font_weight", 700)

        if "levels" in heading:
            levels = heading["levels"]
            # Handle both dict and list formats
            if isinstance(levels, dict):
                for level_name, level_def in levels.items():
                    heading_group.tokens[level_name] = _create_typography_token(
                        level_name,
                        level_def,
                        base_font=base_font,
                        base_weight=base_weight,
                        description=f"{level_name.upper()} heading style",
                    )
            else:
                for i, level_def in enumerate(levels, start=1):
                    heading_group.tokens[f"h{i}"] = _create_typography_token(
                        f"h{i}",
                        level_def,
                        base_font=base_font,
                        base_weight=base_weight,
                        description=f"Heading {i} style",
                    )

        group.groups["heading"] = heading_group

    # Convert body styles
    if "body" in styles:
        body_group = TokenGroup(name="body", type=TokenType.TYPOGRAPHY)
        body = styles["body"]

        for style_name, style_def in body.items():
            if isinstance(style_def, dict) and "font_family" in style_def:
                body_group.tokens[style_name] = _create_typography_token(
                    style_name,
                    style_def,
                )

        group.groups["body"] = body_group

    return group


def _create_typography_token(
    name: str,
    style_def: Dict[str, Any],
    base_font: str = "Arial",
    base_weight: int = 400,
    description: Optional[str] = None,
) -> Token:
    """Create a typography token from a style definition."""
    # Extract font info - use new field names
    font_family = style_def.get("font_family", base_font)

    # Get font size in points (prefer direct pt value)
    size_pt = style_def.get("font_size_pt", 12.0)
    size_twips = style_def.get("font_size_twips")

    # Get other properties
    font_weight = style_def.get("font_weight", base_weight)
    line_height = style_def.get("line_height_multiplier", 1.2)

    # Letter spacing (kerning) - in em
    kerning_em = style_def.get("kerning_em", 0)
    letter_spacing_pt = kerning_em * size_pt if kerning_em else 0

    extensions_data = {}
    if size_twips:
        extensions_data["size_twips"] = size_twips
    if "outline_level" in style_def:
        extensions_data["outline_level"] = style_def["outline_level"]

    return Token(
        name=name,
        value=TypographyValue(
            font_family=font_family,
            font_size=DimensionValue(value=round(size_pt, 2), unit="pt"),
            font_weight=font_weight,
            letter_spacing=DimensionValue(value=round(letter_spacing_pt, 3), unit="pt"),
            line_height=line_height,
        ),
        type=TokenType.TYPOGRAPHY,
        description=description,
        extensions=TokenExtensions(
            other={"com.tokenmoulds": extensions_data},
        )
        if extensions_data
        else None,
    )


def convert_from_dtcg(token_file: TokenFile) -> Dict[str, Any]:
    """Convert DTCG token file to TokenMoulds internal format.

    This is useful for importing tokens from Figma or other tools
    that export DTCG format.

    Args:
        token_file: DTCG TokenFile to convert

    Returns:
        Dictionary in TokenMoulds internal format
    """
    result: Dict[str, Any] = {}

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

    # Convert typography
    typo_group = token_file.groups.get("typography")
    if typo_group:
        result["typography"] = _convert_typography_from_dtcg(typo_group)

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

    return result


def _convert_colors_from_dtcg(group: TokenGroup) -> Dict[str, Any]:
    """Convert DTCG color group to internal format."""
    result: Dict[str, Any] = {}

    # Convert palette
    palette_group = group.groups.get("palette")
    if palette_group:
        result["palette"] = {}
        for name, token in palette_group.tokens.items():
            if isinstance(token.value, ColorValue):
                result["palette"][name] = token.value.to_hex()
            elif isinstance(token.value, str) and token.value.startswith("#"):
                result["palette"][name] = token.value

    # Convert theme mapping
    theme_group = group.groups.get("theme")
    if theme_group:
        result["theme_mapping"] = {}
        for name, token in theme_group.tokens.items():
            if isinstance(token.value, ColorValue):
                result["theme_mapping"][name] = token.value.to_hex()

    # Convert text colors
    text_group = group.groups.get("text")
    if text_group:
        result["text"] = {}
        for name, token in text_group.tokens.items():
            if isinstance(token.value, ColorValue):
                result["text"][name] = token.value.to_hex()

    return result


def _convert_typography_from_dtcg(group: TokenGroup) -> Dict[str, Any]:
    """Convert DTCG typography group to internal format."""
    result: Dict[str, Any] = {}

    # Convert fonts
    font_group = group.groups.get("font")
    if font_group:
        result["fonts"] = {}
        for name, token in font_group.tokens.items():
            if isinstance(token.value, str):
                result["fonts"][name] = token.value

    # Convert scale
    scale_group = group.groups.get("scale")
    if scale_group:
        scale_emu = []
        for name, token in sorted(scale_group.tokens.items()):
            if isinstance(token.value, DimensionValue):
                # Convert back to EMU
                emu = int(token.value.to_pt() * EMU_PER_POINT)
                # Check for stored EMU value in extensions
                if token.extensions and token.extensions.other:
                    tm_ext = token.extensions.other.get("com.tokenmoulds", {})
                    if "emu" in tm_ext:
                        emu = tm_ext["emu"]
                scale_emu.append(emu)
        result["scale_emu"] = scale_emu

    # Get baseline
    baseline_token = group.tokens.get("baseline")
    if baseline_token and isinstance(baseline_token.value, DimensionValue):
        emu = int(baseline_token.value.to_pt() * EMU_PER_POINT)
        if baseline_token.extensions and baseline_token.extensions.other:
            tm_ext = baseline_token.extensions.other.get("com.tokenmoulds", {})
            if "emu" in tm_ext:
                emu = tm_ext["emu"]
        result["baseline_emu"] = emu

    return result


def _convert_spacing_from_dtcg(group: TokenGroup) -> Dict[str, Any]:
    """Convert DTCG spacing group to internal format."""
    result: Dict[str, Any] = {}

    scale_emu = []
    for name, token in group.tokens.items():
        if name in ("paragraphBefore", "paragraphAfter"):
            continue
        if isinstance(token.value, DimensionValue):
            emu = int(token.value.to_pt() * EMU_PER_POINT)
            if token.extensions and token.extensions.other:
                tm_ext = token.extensions.other.get("com.tokenmoulds", {})
                if "emu" in tm_ext:
                    emu = tm_ext["emu"]
            scale_emu.append(emu)

    if scale_emu:
        result["scale_emu"] = scale_emu

    # Paragraph spacing
    para_before = group.tokens.get("paragraphBefore")
    para_after = group.tokens.get("paragraphAfter")
    if para_before or para_after:
        result["paragraph_emu"] = {}
        if para_before and isinstance(para_before.value, DimensionValue):
            result["paragraph_emu"]["before"] = int(
                para_before.value.to_pt() * EMU_PER_POINT
            )
        if para_after and isinstance(para_after.value, DimensionValue):
            result["paragraph_emu"]["after"] = int(
                para_after.value.to_pt() * EMU_PER_POINT
            )

    return result
