"""Style resolver - converts BuiltinStyleDef + design tokens into DocumentIR styles.

The resolver takes:
1. Built-in style definitions (word_builtin.py)
2. ResolvedDesign from the design engine
3. DocumentIR base styles (body, heading, etc.)

And produces fully resolved styles ready for the Word emitter.
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import List, Optional

from tokenmoulds.design.engine import ResolvedDesign, RoleTypography
from tokenmoulds.ir import OpenTypeFeatures
from tokenmoulds.styles.word_builtin import (
    BUILTIN_STYLES,
    BuiltinStyleDef,
    StyleCategory,
    StyleType,
)


@dataclass
class ResolvedStyle:
    """A fully resolved style ready for emission."""

    style_id: str
    name: str
    style_type: str  # "paragraph", "character", "table", "numbering"
    based_on: Optional[str]
    next_style: Optional[str]
    ui_priority: int
    qformat: bool
    semi_hidden: bool
    unhide_when_used: bool
    # Style properties (only populated for paragraph/character styles)
    font_family: Optional[str] = None
    font_size_pt: Optional[float] = None
    bold: bool = False
    italic: bool = False
    small_caps: bool = False
    all_caps: bool = False
    color: Optional[str] = None
    tracking_twips: int = 0
    space_before_twips: int = 0
    space_after_twips: int = 0
    left_indent_twips: int = 0
    first_line_indent_twips: int = 0
    outline_level: Optional[int] = None
    keep_with_next: bool = False
    keep_lines_together: bool = False
    opentype: Optional[OpenTypeFeatures] = None
    # Linked style (for paragraph styles with character link)
    linked_style_id: Optional[str] = None


class StyleResolver:
    """Resolves built-in style definitions into complete styles using design tokens."""

    def __init__(
        self,
        design: ResolvedDesign,
        body_font: str,
        heading_font: str,
        body_color: str = "#000000",
        heading_color: str = "#000000",
    ):
        """Initialize the style resolver.

        Args:
            design: Resolved design from the design engine
            body_font: Body font family
            heading_font: Heading font family
            body_color: Body text color (hex)
            heading_color: Heading text color (hex, can be accent)
        """
        self.design = design
        self.body_font = body_font
        self.heading_font = heading_font
        self.body_color = body_color
        self.heading_color = heading_color

        # Base measurements
        self.baseline_pt = design.baseline.grid_pt
        self.baseline_twips = design.baseline.grid_twips

        # Get body typography for reference
        self.body_typo = design.roles.get("text.body.primary")
        self.body_size_pt = (
            self.body_typo.font_size_pt if self.body_typo else self.baseline_pt
        )

    def resolve_all(self) -> List[ResolvedStyle]:
        """Resolve all built-in styles."""
        resolved = []

        for style_def in BUILTIN_STYLES:
            resolved_style = self._resolve_style(style_def)
            resolved.append(resolved_style)

        return resolved

    def _resolve_style(self, style_def: BuiltinStyleDef) -> ResolvedStyle:
        """Resolve a single style definition."""
        # Base properties from definition
        resolved = ResolvedStyle(
            style_id=style_def.style_id,
            name=style_def.name,
            style_type=style_def.style_type.value,
            based_on=style_def.based_on,
            next_style=style_def.next_style,
            ui_priority=style_def.ui_priority,
            qformat=style_def.qformat,
            semi_hidden=style_def.semi_hidden,
            unhide_when_used=style_def.unhide_when_used,
        )

        # Skip detailed resolution for table/numbering styles
        if style_def.style_type in (StyleType.TABLE, StyleType.NUMBERING):
            return resolved

        # Resolve typography based on category
        if style_def.category == StyleCategory.HEADING:
            self._resolve_heading_style(resolved, style_def)
        elif style_def.category == StyleCategory.DOCUMENT:
            self._resolve_document_style(resolved, style_def)
        elif style_def.category == StyleCategory.LIST:
            self._resolve_list_style(resolved, style_def)
        elif style_def.category == StyleCategory.TOC:
            self._resolve_toc_style(resolved, style_def)
        elif style_def.category == StyleCategory.INDEX:
            self._resolve_index_style(resolved, style_def)
        elif style_def.category == StyleCategory.FOOTNOTE:
            self._resolve_footnote_style(resolved, style_def)
        elif style_def.category == StyleCategory.SPECIAL:
            self._resolve_special_style(resolved, style_def)
        elif style_def.category == StyleCategory.CHARACTER:
            self._resolve_character_style(resolved, style_def)
        else:
            # Default: inherit from body
            self._apply_body_defaults(resolved, style_def)

        return resolved

    def _resolve_heading_style(
        self, resolved: ResolvedStyle, style_def: BuiltinStyleDef
    ) -> None:
        """Resolve heading style properties."""
        resolved.font_family = self.heading_font
        resolved.color = self.heading_color
        resolved.outline_level = style_def.outline_level
        resolved.keep_with_next = True
        resolved.keep_lines_together = True

        # Get size from role or calculate from level
        if style_def.role:
            role_typo = self.design.roles.get(style_def.role)
            if role_typo:
                resolved.font_size_pt = role_typo.font_size_pt
                resolved.tracking_twips = role_typo.tracking.tracking_twips
                resolved.opentype = self._convert_opentype(role_typo)

        if resolved.font_size_pt is None:
            # Calculate from outline level
            level = (style_def.outline_level or 0) + 1
            scale_factor = 3.0 - (level - 1) * 0.4
            resolved.font_size_pt = self.body_size_pt * max(scale_factor, 0.8)

        # Apply style definition properties
        resolved.bold = style_def.bold
        resolved.italic = style_def.italic
        resolved.tracking_twips += style_def.tracking_adjustment

        # Spacing
        resolved.space_before_twips = int(
            self.baseline_twips * style_def.space_before_scale
        )
        resolved.space_after_twips = int(
            self.baseline_twips * style_def.space_after_scale
        )

        # Linked character style
        resolved.linked_style_id = f"{style_def.style_id}Char"

    def _resolve_document_style(
        self, resolved: ResolvedStyle, style_def: BuiltinStyleDef
    ) -> None:
        """Resolve document style (Normal, Body Text, etc.) properties."""
        resolved.font_family = self.body_font
        resolved.color = self.body_color

        # Get size from role or scale
        if style_def.role:
            role_typo = self.design.roles.get(style_def.role)
            if role_typo:
                resolved.font_size_pt = role_typo.font_size_pt
                resolved.tracking_twips = role_typo.tracking.tracking_twips
                resolved.opentype = self._convert_opentype(role_typo)

        if resolved.font_size_pt is None:
            if style_def.size_scale:
                resolved.font_size_pt = self.body_size_pt * style_def.size_scale
            else:
                resolved.font_size_pt = self.body_size_pt

        # Apply style definition properties
        resolved.bold = style_def.bold
        resolved.italic = style_def.italic
        resolved.tracking_twips += style_def.tracking_adjustment

        # Spacing
        resolved.space_before_twips = int(
            self.baseline_twips * style_def.space_before_scale
        )
        resolved.space_after_twips = int(
            self.baseline_twips * style_def.space_after_scale
        )

        # Indentation
        if style_def.indent_scale > 0:
            resolved.left_indent_twips = int(
                self.baseline_twips * style_def.indent_scale
            )

    def _resolve_list_style(
        self, resolved: ResolvedStyle, style_def: BuiltinStyleDef
    ) -> None:
        """Resolve list style properties."""
        self._apply_body_defaults(resolved, style_def)

        # List indentation based on level
        if style_def.indent_scale > 0:
            resolved.left_indent_twips = int(
                self.baseline_twips * style_def.indent_scale
            )

    def _resolve_toc_style(
        self, resolved: ResolvedStyle, style_def: BuiltinStyleDef
    ) -> None:
        """Resolve TOC/index style properties."""
        self._resolve_navigational_style(resolved, style_def)

    def _resolve_index_style(
        self, resolved: ResolvedStyle, style_def: BuiltinStyleDef
    ) -> None:
        """Resolve TOC/index style properties."""
        self._resolve_navigational_style(resolved, style_def)

    def _resolve_navigational_style(
        self, resolved: ResolvedStyle, style_def: BuiltinStyleDef
    ) -> None:
        """Shared logic for TOC and index styles."""
        self._apply_body_defaults(resolved, style_def)

        resolved.bold = style_def.bold

        if style_def.indent_scale > 0:
            resolved.left_indent_twips = int(
                self.baseline_twips * style_def.indent_scale
            )

        resolved.space_before_twips = int(
            self.baseline_twips * style_def.space_before_scale
        )

    def _resolve_footnote_style(
        self, resolved: ResolvedStyle, style_def: BuiltinStyleDef
    ) -> None:
        """Resolve footnote/endnote style properties."""
        resolved.font_family = self.body_font
        resolved.color = self.body_color

        # Footnotes are smaller
        if style_def.size_scale:
            resolved.font_size_pt = self.body_size_pt * style_def.size_scale
        else:
            resolved.font_size_pt = self.body_size_pt * 0.8

        # Slightly looser tracking for small text
        resolved.tracking_twips = 10

    def _resolve_special_style(
        self, resolved: ResolvedStyle, style_def: BuiltinStyleDef
    ) -> None:
        """Resolve special style (Caption, Quote, Header, etc.) properties."""
        # Determine font based on style
        if style_def.style_id in ("Quote", "IntenseQuote"):
            resolved.font_family = self.heading_font
        else:
            resolved.font_family = self.body_font

        resolved.color = self.body_color

        # Get size from role or scale
        if style_def.role:
            role_typo = self.design.roles.get(style_def.role)
            if role_typo:
                resolved.font_size_pt = role_typo.font_size_pt
                resolved.tracking_twips = role_typo.tracking.tracking_twips

        if resolved.font_size_pt is None:
            if style_def.size_scale:
                resolved.font_size_pt = self.body_size_pt * style_def.size_scale
            else:
                resolved.font_size_pt = self.body_size_pt

        # Apply style definition properties
        resolved.bold = style_def.bold
        resolved.italic = style_def.italic
        resolved.tracking_twips += style_def.tracking_adjustment

        # Spacing
        resolved.space_before_twips = int(
            self.baseline_twips * style_def.space_before_scale
        )
        resolved.space_after_twips = int(
            self.baseline_twips * style_def.space_after_scale
        )

        # Indentation
        if style_def.indent_scale > 0:
            resolved.left_indent_twips = int(
                self.baseline_twips * style_def.indent_scale
            )

    def _resolve_character_style(
        self, resolved: ResolvedStyle, style_def: BuiltinStyleDef
    ) -> None:
        """Resolve character style properties."""
        resolved.bold = style_def.bold
        resolved.italic = style_def.italic
        resolved.small_caps = style_def.small_caps
        resolved.all_caps = style_def.all_caps

        # Size scaling if specified
        if style_def.size_scale:
            resolved.font_size_pt = self.body_size_pt * style_def.size_scale

        # Tracking
        if style_def.small_caps and self.body_typo:
            resolved.tracking_twips = self.body_typo.tracking.small_caps_tracking_twips
        elif style_def.all_caps and self.body_typo:
            resolved.tracking_twips = self.body_typo.tracking.caps_tracking_twips
        else:
            resolved.tracking_twips = style_def.tracking_adjustment

    def _apply_body_defaults(
        self, resolved: ResolvedStyle, style_def: BuiltinStyleDef
    ) -> None:
        """Apply body text defaults to a style."""
        resolved.font_family = self.body_font
        resolved.font_size_pt = self.body_size_pt
        resolved.color = self.body_color

        if self.body_typo:
            resolved.tracking_twips = self.body_typo.tracking.tracking_twips
            resolved.opentype = self._convert_opentype(self.body_typo)

    def _convert_opentype(self, role_typo: RoleTypography) -> OpenTypeFeatures:
        """Convert role typography OpenType settings to IR OpenTypeFeatures."""
        return OpenTypeFeatures(
            ligatures=role_typo.opentype.ligatures,
            number_form=role_typo.opentype.number_form,
            number_spacing=role_typo.opentype.number_spacing,
            contextual_alternates=role_typo.opentype.contextual_alternates,
        )
