"""Shared typography style generation for the Document IR.

Both the token-based builder and the design-bridge use this module
to produce the same 20 styles (12 paragraph + 8 character) with
identical style IDs.
"""

from __future__ import annotations

from dataclasses import dataclass, field

from tokenmoulds.ir.model import (
    CharacterStyle,
    CharacterTypography,
    OpenTypeFeatures,
    ParagraphControl,
    ParagraphSpacing,
    ParagraphStyle,
)


def _pt_to_twips(points: float) -> int:
    """Convert points to twips (1 pt = 20 twips)."""
    return int(round(points * 20))


@dataclass
class TypographyTracking:
    """Tracking (letter-spacing) values in twips for typography styles."""

    tight: int = -20  # TrackingTight
    loose: int = 40  # TrackingLoose
    very_tight: int = -40  # TrackingVeryTight
    small_caps: int = 40  # SmallCaps / SmallCapsChar
    all_caps: int = 80  # AllCaps
    display_base: int = -20  # Base display tracking


@dataclass
class TypographyOpenType:
    """OpenType feature settings for body and display roles."""

    body: OpenTypeFeatures = field(default_factory=OpenTypeFeatures)
    display: OpenTypeFeatures = field(
        default_factory=lambda: OpenTypeFeatures(
            ligatures="standard",
            number_form="lining",
        ),
    )


def generate_typography_styles(
    body_font: str,
    heading_font: str,
    body_size_pt: float,
    text_color: str = "#000000",
    tracking: TypographyTracking | None = None,
    opentype: TypographyOpenType | None = None,
) -> tuple[list[ParagraphStyle], list[CharacterStyle]]:
    """Generate comprehensive typography styles showcasing all features.

    Creates paragraph and character styles demonstrating:
    - Tracking variations (tight, normal, loose)
    - OpenType features (ligatures, figures)
    - Small caps, all caps
    - Various weights and sizes

    These styles are written to the template's styles.xml.

    Args:
        body_font: Body font family
        heading_font: Heading font family
        body_size_pt: Body font size in points
        text_color: Default text color
        tracking: Tracking values; uses defaults when *None*
        opentype: OpenType feature sets; uses defaults when *None*

    Returns:
        Tuple of (paragraph_styles, character_styles)
    """
    if tracking is None:
        tracking = TypographyTracking()
    if opentype is None:
        opentype = TypographyOpenType()

    baseline_pt = body_size_pt
    paragraph_styles: list[ParagraphStyle] = []
    character_styles: list[CharacterStyle] = []

    # Derived caps tracking for kicker (extra loose)
    kicker_tracking = tracking.all_caps + 20

    # --- PARAGRAPH STYLES ---

    # Body variations
    body_sizes = [
        ("BodySmall", baseline_pt * 0.85, "Body text small"),
        ("BodyLarge", baseline_pt * 1.15, "Body text large"),
    ]

    for style_id, size_pt, name in body_sizes:
        paragraph_styles.append(
            ParagraphStyle(
                style_id=style_id,
                name=name,
                font_family=body_font,
                font_size_pt=size_pt,
                color=text_color,
                spacing=ParagraphSpacing(
                    before_twips=_pt_to_twips(baseline_pt * 0.5),
                    after_twips=_pt_to_twips(baseline_pt * 0.5),
                ),
            )
        )

    # Tracking variations
    tracking_variations = [
        ("TrackingTight", tracking.tight, "Tracking Tight"),
        ("TrackingLoose", tracking.loose, "Tracking Loose"),
        ("TrackingVeryTight", tracking.very_tight, "Tracking Very Tight"),
    ]

    for style_id, tracking_twips, name in tracking_variations:
        paragraph_styles.append(
            ParagraphStyle(
                style_id=style_id,
                name=name,
                font_family=body_font,
                font_size_pt=baseline_pt,
                color=text_color,
                spacing=ParagraphSpacing(
                    before_twips=_pt_to_twips(baseline_pt * 0.5),
                    after_twips=_pt_to_twips(baseline_pt * 0.5),
                ),
                character=CharacterTypography(
                    tracking_twips=tracking_twips,
                    kern_threshold_half_pt=16,
                ),
            )
        )

    # Display styles (large, for titles/headers beyond H1)
    # Tracking gets progressively tighter for larger sizes
    display_sizes = [
        (
            "Display1",
            baseline_pt * 4.0,
            tracking.display_base - 10,
        ),  # Largest, tightest
        ("Display2", baseline_pt * 3.0, tracking.display_base),
        ("Display3", baseline_pt * 2.5, tracking.display_base + 10),
    ]

    for style_id, size_pt, display_tracking in display_sizes:
        paragraph_styles.append(
            ParagraphStyle(
                style_id=style_id,
                name=style_id.replace("Display", "Display "),
                font_family=heading_font,
                font_size_pt=size_pt,
                color=text_color,
                bold=True,
                spacing=ParagraphSpacing(
                    before_twips=_pt_to_twips(baseline_pt),
                    after_twips=_pt_to_twips(baseline_pt * 0.5),
                ),
                character=CharacterTypography(
                    tracking_twips=display_tracking,
                    kern_threshold_half_pt=16,
                    opentype=opentype.display,
                ),
                control=ParagraphControl(
                    keep_with_next=True,
                    keep_lines_together=True,
                ),
            )
        )

    # Caption and label styles
    paragraph_styles.append(
        ParagraphStyle(
            style_id="Caption",
            name="Caption",
            font_family=body_font,
            font_size_pt=baseline_pt * 0.75,
            color=text_color,
            spacing=ParagraphSpacing(
                before_twips=_pt_to_twips(baseline_pt * 0.25),
                after_twips=_pt_to_twips(baseline_pt * 0.25),
            ),
            character=CharacterTypography(
                tracking_twips=10,  # Slightly loose for small text
            ),
        )
    )

    # Small caps style - use derived tracking
    paragraph_styles.append(
        ParagraphStyle(
            style_id="SmallCaps",
            name="Small Caps",
            font_family=body_font,
            font_size_pt=baseline_pt,
            color=text_color,
            spacing=ParagraphSpacing(
                before_twips=_pt_to_twips(baseline_pt * 0.5),
                after_twips=_pt_to_twips(baseline_pt * 0.5),
            ),
            character=CharacterTypography(
                small_caps=True,
                tracking_twips=tracking.small_caps,
                kern_threshold_half_pt=16,
                opentype=opentype.body,
            ),
        )
    )

    # All caps style (for labels, kickers) - use derived tracking
    paragraph_styles.append(
        ParagraphStyle(
            style_id="AllCaps",
            name="All Caps",
            font_family=body_font,
            font_size_pt=baseline_pt * 0.8,
            color=text_color,
            spacing=ParagraphSpacing(
                before_twips=_pt_to_twips(baseline_pt * 0.5),
                after_twips=_pt_to_twips(baseline_pt * 0.25),
            ),
            character=CharacterTypography(
                all_caps=True,
                tracking_twips=tracking.all_caps,
                kern_threshold_half_pt=16,
                opentype=opentype.body,
            ),
        )
    )

    # Kicker style (above headlines) - use derived tracking + extra for emphasis
    paragraph_styles.append(
        ParagraphStyle(
            style_id="Kicker",
            name="Kicker",
            font_family=body_font,
            font_size_pt=baseline_pt * 0.75,
            color=text_color,
            bold=True,
            spacing=ParagraphSpacing(
                before_twips=_pt_to_twips(baseline_pt),
                after_twips=0,  # No space before headline
            ),
            character=CharacterTypography(
                all_caps=True,
                tracking_twips=kicker_tracking,
                opentype=opentype.body,
            ),
        )
    )

    # Byline style
    paragraph_styles.append(
        ParagraphStyle(
            style_id="Byline",
            name="Byline",
            font_family=body_font,
            font_size_pt=baseline_pt * 0.9,
            color=text_color,
            italic=True,
            spacing=ParagraphSpacing(
                before_twips=_pt_to_twips(baseline_pt * 0.5),
                after_twips=_pt_to_twips(baseline_pt),
            ),
        )
    )

    # Lead paragraph (first paragraph, slightly larger)
    paragraph_styles.append(
        ParagraphStyle(
            style_id="Lead",
            name="Lead Paragraph",
            font_family=body_font,
            font_size_pt=baseline_pt * 1.2,
            color=text_color,
            spacing=ParagraphSpacing(
                before_twips=_pt_to_twips(baseline_pt),
                after_twips=_pt_to_twips(baseline_pt),
            ),
            character=CharacterTypography(
                kern_threshold_half_pt=16,
                opentype=OpenTypeFeatures(
                    ligatures="standard",
                ),
            ),
        )
    )

    # Pullquote (large, for emphasis)
    paragraph_styles.append(
        ParagraphStyle(
            style_id="Pullquote",
            name="Pullquote",
            font_family=heading_font,
            font_size_pt=baseline_pt * 1.5,
            color=text_color,
            italic=True,
            spacing=ParagraphSpacing(
                before_twips=_pt_to_twips(baseline_pt * 1.5),
                after_twips=_pt_to_twips(baseline_pt * 1.5),
            ),
            character=CharacterTypography(
                tracking_twips=-10,
                kern_threshold_half_pt=16,
            ),
            control=ParagraphControl(
                justification="center",
            ),
        )
    )

    # --- CHARACTER STYLES ---

    # Ligature demonstration
    character_styles.append(
        CharacterStyle(
            style_id="Ligatures",
            name="Ligatures",
            character=CharacterTypography(
                opentype=OpenTypeFeatures(
                    ligatures="all",  # fi, fl, ff, ffi, ffl
                    contextual_alternates=True,
                ),
            ),
        )
    )

    # No ligatures (for code, URLs)
    character_styles.append(
        CharacterStyle(
            style_id="NoLigatures",
            name="No Ligatures",
            character=CharacterTypography(
                opentype=OpenTypeFeatures(
                    ligatures="none",
                ),
            ),
        )
    )

    # Oldstyle figures (for body text)
    character_styles.append(
        CharacterStyle(
            style_id="OldstyleFigures",
            name="Oldstyle Figures",
            character=CharacterTypography(
                opentype=OpenTypeFeatures(
                    number_form="oldStyle",
                    number_spacing="proportional",
                ),
            ),
        )
    )

    # Lining figures (for tables, headers)
    character_styles.append(
        CharacterStyle(
            style_id="LiningFigures",
            name="Lining Figures",
            character=CharacterTypography(
                opentype=OpenTypeFeatures(
                    number_form="lining",
                    number_spacing="tabular",
                ),
            ),
        )
    )

    # Tabular figures (for aligned numbers)
    character_styles.append(
        CharacterStyle(
            style_id="TabularFigures",
            name="Tabular Figures",
            character=CharacterTypography(
                opentype=OpenTypeFeatures(
                    number_spacing="tabular",
                ),
            ),
        )
    )

    # Strong emphasis (bold + slight tracking)
    character_styles.append(
        CharacterStyle(
            style_id="Strong",
            name="Strong",
            bold=True,
            character=CharacterTypography(
                tracking_twips=5,
            ),
        )
    )

    # Emphasis (italic)
    character_styles.append(
        CharacterStyle(
            style_id="Emphasis",
            name="Emphasis",
            italic=True,
        )
    )

    # Small caps character style - use derived tracking
    character_styles.append(
        CharacterStyle(
            style_id="SmallCapsChar",
            name="Small Caps Character",
            character=CharacterTypography(
                small_caps=True,
                tracking_twips=tracking.small_caps,
            ),
        )
    )

    # Kerning demo (explicit kerning)
    character_styles.append(
        CharacterStyle(
            style_id="KerningDemo",
            name="Kerning Demo",
            character=CharacterTypography(
                kern_threshold_half_pt=0,  # Kern everything
            ),
        )
    )

    return paragraph_styles, character_styles
