"""Paragraph style definitions with derivation formulas.

Defines all paragraph styles from the ODF/OOXML canonical set.
Each style includes properties derived from RecipeInputs.
"""

from __future__ import annotations

from dataclasses import dataclass
import ast
from typing import Optional, TYPE_CHECKING

from tokenmoulds.dtcg.types import (
    DimensionValue,
    Token,
    TokenExtensions,
    TokenGroup,
    TokenType,
)

if TYPE_CHECKING:
    from tokenmoulds.dtcg.recipe import RecipeInputs


@dataclass
class ParagraphStyleDef:
    """Definition of a paragraph style."""

    name: str  # Canonical name
    description: str
    ooxml_id: str  # Word styleId
    odf_name: str  # ODF style:name

    # Typography
    font_family: str = "{typography.font.body}"  # Reference or value
    font_size_formula: str = "{typography.scale.base}"
    font_weight: int = 400
    font_style: str = "normal"  # normal, italic

    # Spacing
    line_height_formula: str = "{typography.lineHeight.relaxed}"
    space_before_formula: str = "0"
    space_after_formula: str = "{spacing.md}"

    # Indentation
    indent_left_formula: str = "0"
    indent_right_formula: str = "0"
    indent_first_formula: str = "0"  # First line indent

    # Alignment
    alignment: str = "left"  # left, center, right, justify

    # Paragraph control
    keep_with_next: bool = False
    keep_together: bool = False
    widow_control: bool = True
    orphan_control: bool = True

    # Color
    color_formula: str = "{color.text.primary}"

    # Kerning
    kerning_threshold_formula: str = "{inputs.dimension.kerningThreshold}"

    # Parent style
    based_on: Optional[str] = None  # Parent style name
    next_style: Optional[str] = None  # Style for next paragraph


# =============================================================================
# BODY TEXT STYLES
# =============================================================================

BODY_STYLES = [
    ParagraphStyleDef(
        name="Normal",
        description="Default paragraph style - base for all text",
        ooxml_id="Normal",
        odf_name="Standard",
        font_family="{typography.font.body}",
        font_size_formula="{typography.scale.base}",
        line_height_formula="{typography.lineHeight.relaxed}",
        space_after_formula="{spacing.md}",
    ),
    ParagraphStyleDef(
        name="BodyText",
        description="Body text - main content paragraphs",
        ooxml_id="BodyText",
        odf_name="Text body",
        based_on="Normal",
        font_family="{typography.font.body}",
        font_size_formula="{typography.scale.base}",
        line_height_formula="{typography.lineHeight.relaxed}",
        space_after_formula="{spacing.md}",
    ),
    ParagraphStyleDef(
        name="BodyTextIndent",
        description="Body text with first line indent",
        ooxml_id="BodyTextIndent",
        odf_name="Text body first line indent",
        based_on="BodyText",
        indent_first_formula="{spacing.lg}",
        space_after_formula="0",  # No space between indented paragraphs
    ),
    ParagraphStyleDef(
        name="BodyTextHanging",
        description="Body text with hanging indent",
        ooxml_id="BodyTextIndent2",
        odf_name="Text body hanging indent",
        based_on="BodyText",
        indent_left_formula="{spacing.lg}",
        indent_first_formula="-{spacing.lg}",  # Negative = hanging
    ),
    ParagraphStyleDef(
        name="BodyTextBlockIndent",
        description="Body text indented from left margin",
        ooxml_id="BodyTextIndent3",
        odf_name="Text body indent",
        based_on="BodyText",
        indent_left_formula="{spacing.xl}",
    ),
]


# =============================================================================
# QUOTE STYLES
# =============================================================================

QUOTE_STYLES = [
    ParagraphStyleDef(
        name="Quote",
        description="Block quotation",
        ooxml_id="Quote",
        odf_name="Quotations",
        based_on="BodyText",
        font_style="italic",
        indent_left_formula="{spacing.xl}",
        indent_right_formula="{spacing.xl}",
        space_before_formula="{spacing.lg}",
        space_after_formula="{spacing.lg}",
        color_formula="{color.text.muted}",
    ),
    ParagraphStyleDef(
        name="IntenseQuote",
        description="Emphasized block quotation",
        ooxml_id="IntenseQuote",
        odf_name="Quotations Intense",
        based_on="Quote",
        font_weight=600,
        color_formula="{color.brand.primary}",
    ),
    ParagraphStyleDef(
        name="BlockQuote",
        description="HTML-style blockquote",
        ooxml_id="HTMLBlockquote",
        odf_name="HTML Blockquote",
        based_on="Quote",
    ),
]


# =============================================================================
# CAPTION AND LABEL STYLES
# =============================================================================

CAPTION_STYLES = [
    ParagraphStyleDef(
        name="Caption",
        description="Figure/table captions",
        ooxml_id="Caption",
        odf_name="Caption",
        font_size_formula="{typography.scale.sm}",
        font_style="italic",
        space_before_formula="{spacing.sm}",
        space_after_formula="{spacing.md}",
        color_formula="{color.text.muted}",
    ),
    ParagraphStyleDef(
        name="CaptionFigure",
        description="Caption for figures/illustrations",
        ooxml_id="CaptionFigure",
        odf_name="Caption figure",
        based_on="Caption",
    ),
    ParagraphStyleDef(
        name="CaptionTable",
        description="Caption for tables",
        ooxml_id="CaptionTable",
        odf_name="Caption table",
        based_on="Caption",
    ),
    ParagraphStyleDef(
        name="CaptionDrawing",
        description="Caption for drawings",
        ooxml_id="CaptionDrawing",
        odf_name="Caption drawing objects",
        based_on="Caption",
    ),
]


# =============================================================================
# HEADER AND FOOTER STYLES
# =============================================================================

HEADER_FOOTER_STYLES = [
    ParagraphStyleDef(
        name="Header",
        description="Page header content",
        ooxml_id="Header",
        odf_name="Header",
        font_size_formula="{typography.scale.sm}",
        line_height_formula="{typography.lineHeight.normal}",
        space_after_formula="0",
        color_formula="{color.text.muted}",
    ),
    ParagraphStyleDef(
        name="HeaderLeft",
        description="Left page header",
        ooxml_id="HeaderLeft",
        odf_name="Header Left",
        based_on="Header",
        alignment="left",
    ),
    ParagraphStyleDef(
        name="HeaderRight",
        description="Right page header",
        ooxml_id="HeaderRight",
        odf_name="Header Right",
        based_on="Header",
        alignment="right",
    ),
    ParagraphStyleDef(
        name="Footer",
        description="Page footer content",
        ooxml_id="Footer",
        odf_name="Footer",
        font_size_formula="{typography.scale.sm}",
        line_height_formula="{typography.lineHeight.normal}",
        space_before_formula="0",
        color_formula="{color.text.muted}",
    ),
    ParagraphStyleDef(
        name="FooterLeft",
        description="Left page footer",
        ooxml_id="FooterLeft",
        odf_name="Footer Left",
        based_on="Footer",
        alignment="left",
    ),
    ParagraphStyleDef(
        name="FooterRight",
        description="Right page footer",
        ooxml_id="FooterRight",
        odf_name="Footer Right",
        based_on="Footer",
        alignment="right",
    ),
]


# =============================================================================
# FOOTNOTE AND ENDNOTE STYLES
# =============================================================================

NOTE_STYLES = [
    ParagraphStyleDef(
        name="FootnoteText",
        description="Footnote content",
        ooxml_id="FootnoteText",
        odf_name="Footnote",
        font_size_formula="{typography.scale.xs}",
        line_height_formula="{typography.lineHeight.snug}",
        space_after_formula="{spacing.xs}",
        indent_left_formula="{spacing.sm}",
        indent_first_formula="-{spacing.sm}",  # Hanging indent
    ),
    ParagraphStyleDef(
        name="EndnoteText",
        description="Endnote content",
        ooxml_id="EndnoteText",
        odf_name="Endnote",
        based_on="FootnoteText",
    ),
    ParagraphStyleDef(
        name="CommentText",
        description="Comment/annotation text",
        ooxml_id="CommentText",
        odf_name="Comment",
        font_size_formula="{typography.scale.xs}",
        color_formula="{color.text.muted}",
    ),
]


# =============================================================================
# LIST CONTINUATION STYLES
# =============================================================================

LIST_STYLES = [
    ParagraphStyleDef(
        name="ListParagraph",
        description="Paragraph within a list",
        ooxml_id="ListParagraph",
        odf_name="List Contents",
        based_on="BodyText",
        indent_left_formula="{spacing.xl}",
        space_after_formula="{spacing.sm}",
    ),
    ParagraphStyleDef(
        name="ListBullet",
        description="Bulleted list item",
        ooxml_id="ListBullet",
        odf_name="List 1",
        based_on="ListParagraph",
    ),
    ParagraphStyleDef(
        name="ListBullet2",
        description="Bulleted list item level 2",
        ooxml_id="ListBullet2",
        odf_name="List 2",
        based_on="ListBullet",
        indent_left_formula="{spacing.xl} * 2",
    ),
    ParagraphStyleDef(
        name="ListBullet3",
        description="Bulleted list item level 3",
        ooxml_id="ListBullet3",
        odf_name="List 3",
        based_on="ListBullet",
        indent_left_formula="{spacing.xl} * 3",
    ),
    ParagraphStyleDef(
        name="ListNumber",
        description="Numbered list item",
        ooxml_id="ListNumber",
        odf_name="Numbering 1",
        based_on="ListParagraph",
    ),
    ParagraphStyleDef(
        name="ListNumber2",
        description="Numbered list item level 2",
        ooxml_id="ListNumber2",
        odf_name="Numbering 2",
        based_on="ListNumber",
        indent_left_formula="{spacing.xl} * 2",
    ),
    ParagraphStyleDef(
        name="ListNumber3",
        description="Numbered list item level 3",
        ooxml_id="ListNumber3",
        odf_name="Numbering 3",
        based_on="ListNumber",
        indent_left_formula="{spacing.xl} * 3",
    ),
    ParagraphStyleDef(
        name="ListNumber4",
        description="Numbered list item level 4",
        ooxml_id="ListNumber4",
        odf_name="Numbering 4",
        based_on="ListNumber",
        indent_left_formula="{spacing.xl} * 4",
    ),
    ParagraphStyleDef(
        name="ListNumber5",
        description="Numbered list item level 5",
        ooxml_id="ListNumber5",
        odf_name="Numbering 5",
        based_on="ListNumber",
        indent_left_formula="{spacing.xl} * 5",
    ),
    ParagraphStyleDef(
        name="ListBullet4",
        description="Bulleted list item level 4",
        ooxml_id="ListBullet4",
        odf_name="List 4",
        based_on="ListBullet",
        indent_left_formula="{spacing.xl} * 4",
    ),
    ParagraphStyleDef(
        name="ListBullet5",
        description="Bulleted list item level 5",
        ooxml_id="ListBullet5",
        odf_name="List 5",
        based_on="ListBullet",
        indent_left_formula="{spacing.xl} * 5",
    ),
    # List continuation styles (no bullet/number)
    ParagraphStyleDef(
        name="ListContinue",
        description="Continuation paragraph in list (no bullet)",
        ooxml_id="ListContinue",
        odf_name="List Contents",
        based_on="ListParagraph",
    ),
    ParagraphStyleDef(
        name="ListContinue2",
        description="Continuation paragraph level 2",
        ooxml_id="ListContinue2",
        odf_name="List Contents 2",
        based_on="ListContinue",
        indent_left_formula="{spacing.xl} * 2",
    ),
    ParagraphStyleDef(
        name="ListContinue3",
        description="Continuation paragraph level 3",
        ooxml_id="ListContinue3",
        odf_name="List Contents 3",
        based_on="ListContinue",
        indent_left_formula="{spacing.xl} * 3",
    ),
    ParagraphStyleDef(
        name="ListContinue4",
        description="Continuation paragraph level 4",
        ooxml_id="ListContinue4",
        odf_name="List Contents 4",
        based_on="ListContinue",
        indent_left_formula="{spacing.xl} * 4",
    ),
    ParagraphStyleDef(
        name="ListContinue5",
        description="Continuation paragraph level 5",
        ooxml_id="ListContinue5",
        odf_name="List Contents 5",
        based_on="ListContinue",
        indent_left_formula="{spacing.xl} * 5",
    ),
]


# =============================================================================
# HEADING STYLES
# =============================================================================

HEADING_STYLES = [
    ParagraphStyleDef(
        name="HeadingBase",
        description="Base style for all headings",
        ooxml_id="HeadingBase",
        odf_name="Heading",
        font_family="{typography.font.heading}",
        font_size_formula="{typography.scale.base}",
        font_weight=700,
        line_height_formula="{typography.lineHeight.tight}",
        space_before_formula="{spacing.lg}",
        space_after_formula="{spacing.sm}",
        keep_with_next=True,
        keep_together=True,
        color_formula="{color.text.primary}",
    ),
    ParagraphStyleDef(
        name="Heading1",
        description="Major section heading",
        ooxml_id="Heading1",
        odf_name="Heading 1",
        based_on="HeadingBase",
        font_family="{typography.font.heading}",
        font_size_formula="{typography.scale.3xl}",  # ~30px at 16px base
        font_weight=700,
        line_height_formula="{typography.lineHeight.tight}",
        space_before_formula="{spacing.2xl}",
        space_after_formula="{spacing.lg}",
        keep_with_next=True,
        color_formula="{color.brand.primary}",
        next_style="BodyText",
    ),
    ParagraphStyleDef(
        name="Heading2",
        description="Section heading",
        ooxml_id="Heading2",
        odf_name="Heading 2",
        based_on="HeadingBase",
        font_family="{typography.font.heading}",
        font_size_formula="{typography.scale.2xl}",  # ~24px at 16px base
        font_weight=700,
        line_height_formula="{typography.lineHeight.tight}",
        space_before_formula="{spacing.xl}",
        space_after_formula="{spacing.md}",
        keep_with_next=True,
        color_formula="{color.brand.primary}",
        next_style="BodyText",
    ),
    ParagraphStyleDef(
        name="Heading3",
        description="Subsection heading",
        ooxml_id="Heading3",
        odf_name="Heading 3",
        based_on="HeadingBase",
        font_family="{typography.font.heading}",
        font_size_formula="{typography.scale.xl}",  # ~20px at 16px base
        font_weight=600,
        line_height_formula="{typography.lineHeight.snug}",
        space_before_formula="{spacing.lg}",
        space_after_formula="{spacing.sm}",
        keep_with_next=True,
        next_style="BodyText",
    ),
    ParagraphStyleDef(
        name="Heading4",
        description="Minor heading",
        ooxml_id="Heading4",
        odf_name="Heading 4",
        based_on="HeadingBase",
        font_family="{typography.font.heading}",
        font_size_formula="{typography.scale.lg}",  # ~18px at 16px base
        font_weight=600,
        line_height_formula="{typography.lineHeight.snug}",
        space_before_formula="{spacing.md}",
        space_after_formula="{spacing.sm}",
        keep_with_next=True,
        next_style="BodyText",
    ),
    ParagraphStyleDef(
        name="Heading5",
        description="Detail heading",
        ooxml_id="Heading5",
        odf_name="Heading 5",
        based_on="HeadingBase",
        font_family="{typography.font.heading}",
        font_size_formula="{typography.scale.base}",  # Same as body
        font_weight=600,
        line_height_formula="{typography.lineHeight.normal}",
        space_before_formula="{spacing.md}",
        space_after_formula="{spacing.xs}",
        keep_with_next=True,
        next_style="BodyText",
    ),
    ParagraphStyleDef(
        name="Heading6",
        description="Fine heading",
        ooxml_id="Heading6",
        odf_name="Heading 6",
        based_on="HeadingBase",
        font_family="{typography.font.heading}",
        font_size_formula="{typography.scale.sm}",
        font_weight=600,
        font_style="normal",
        line_height_formula="{typography.lineHeight.normal}",
        space_before_formula="{spacing.sm}",
        space_after_formula="{spacing.xs}",
        keep_with_next=True,
        next_style="BodyText",
    ),
    ParagraphStyleDef(
        name="Heading7",
        description="Heading level 7",
        ooxml_id="Heading7",
        odf_name="Heading 7",
        based_on="HeadingBase",
        font_family="{typography.font.heading}",
        font_size_formula="{typography.scale.sm}",
        font_weight=500,
        font_style="italic",
        line_height_formula="{typography.lineHeight.normal}",
        space_before_formula="{spacing.sm}",
        space_after_formula="{spacing.xs}",
        keep_with_next=True,
        next_style="BodyText",
    ),
    ParagraphStyleDef(
        name="Heading8",
        description="Heading level 8",
        ooxml_id="Heading8",
        odf_name="Heading 8",
        based_on="HeadingBase",
        font_family="{typography.font.heading}",
        font_size_formula="{typography.scale.xs}",
        font_weight=600,
        line_height_formula="{typography.lineHeight.normal}",
        space_before_formula="{spacing.xs}",
        space_after_formula="{spacing.xs}",
        keep_with_next=True,
        next_style="BodyText",
    ),
    ParagraphStyleDef(
        name="Heading9",
        description="Heading level 9",
        ooxml_id="Heading9",
        odf_name="Heading 9",
        based_on="HeadingBase",
        font_family="{typography.font.heading}",
        font_size_formula="{typography.scale.xs}",
        font_weight=500,
        font_style="italic",
        line_height_formula="{typography.lineHeight.normal}",
        space_before_formula="{spacing.xs}",
        space_after_formula="{spacing.xs}",
        keep_with_next=True,
        next_style="BodyText",
    ),
    ParagraphStyleDef(
        name="Heading10",
        description="Heading level 10",
        ooxml_id="Heading10",
        odf_name="Heading 10",
        based_on="HeadingBase",
        font_family="{typography.font.body}",  # Use body font for deepest level
        font_size_formula="{typography.scale.xs}",
        font_weight=600,
        line_height_formula="{typography.lineHeight.normal}",
        space_before_formula="{spacing.xs}",
        space_after_formula="{spacing.xs}",
        keep_with_next=True,
        next_style="BodyText",
    ),
]


# =============================================================================
# TOC STYLES
# =============================================================================

TOC_STYLES = [
    ParagraphStyleDef(
        name="TOCHeading",
        description="Table of Contents heading",
        ooxml_id="TOCHeading",
        odf_name="Contents Heading",
        font_family="{typography.font.heading}",
        font_size_formula="{typography.scale.lg}",
        font_weight=700,
        space_before_formula="{spacing.xl}",
        space_after_formula="{spacing.md}",
        keep_with_next=True,
    ),
    ParagraphStyleDef(
        name="TOC1",
        description="TOC level 1 entry",
        ooxml_id="TOC1",
        odf_name="Contents 1",
        font_weight=600,
        space_before_formula="{spacing.sm}",
        space_after_formula="{spacing.xs}",
    ),
    ParagraphStyleDef(
        name="TOC2",
        description="TOC level 2 entry",
        ooxml_id="TOC2",
        odf_name="Contents 2",
        indent_left_formula="{spacing.md}",
        space_after_formula="{spacing.xs}",
    ),
    ParagraphStyleDef(
        name="TOC3",
        description="TOC level 3 entry",
        ooxml_id="TOC3",
        odf_name="Contents 3",
        indent_left_formula="{spacing.lg}",
        font_size_formula="{typography.scale.sm}",
        space_after_formula="{spacing.xs}",
        color_formula="{color.text.muted}",
    ),
    ParagraphStyleDef(
        name="TOC4",
        description="TOC level 4 entry",
        ooxml_id="TOC4",
        odf_name="Contents 4",
        indent_left_formula="{spacing.lg} + {spacing.md}",
        font_size_formula="{typography.scale.sm}",
        space_after_formula="{spacing.xs}",
        color_formula="{color.text.muted}",
    ),
    ParagraphStyleDef(
        name="TOC5",
        description="TOC level 5 entry",
        ooxml_id="TOC5",
        odf_name="Contents 5",
        indent_left_formula="{spacing.xl}",
        font_size_formula="{typography.scale.sm}",
        space_after_formula="{spacing.xs}",
        color_formula="{color.text.muted}",
    ),
    ParagraphStyleDef(
        name="TOC6",
        description="TOC level 6 entry",
        ooxml_id="TOC6",
        odf_name="Contents 6",
        indent_left_formula="{spacing.xl} + {spacing.sm}",
        font_size_formula="{typography.scale.xs}",
        space_after_formula="{spacing.xs}",
        color_formula="{color.text.muted}",
    ),
    ParagraphStyleDef(
        name="TOC7",
        description="TOC level 7 entry",
        ooxml_id="TOC7",
        odf_name="Contents 7",
        indent_left_formula="{spacing.xl} + {spacing.md}",
        font_size_formula="{typography.scale.xs}",
        space_after_formula="{spacing.xs}",
        color_formula="{color.text.muted}",
    ),
    ParagraphStyleDef(
        name="TOC8",
        description="TOC level 8 entry",
        ooxml_id="TOC8",
        odf_name="Contents 8",
        indent_left_formula="{spacing.2xl}",
        font_size_formula="{typography.scale.xs}",
        space_after_formula="{spacing.xs}",
        color_formula="{color.text.muted}",
    ),
    ParagraphStyleDef(
        name="TOC9",
        description="TOC level 9 entry",
        ooxml_id="TOC9",
        odf_name="Contents 9",
        indent_left_formula="{spacing.2xl} + {spacing.sm}",
        font_size_formula="{typography.scale.xs}",
        space_after_formula="{spacing.xs}",
        color_formula="{color.text.muted}",
    ),
    ParagraphStyleDef(
        name="TOC10",
        description="TOC level 10 entry",
        ooxml_id="TOC10",
        odf_name="Contents 10",
        indent_left_formula="{spacing.2xl} + {spacing.md}",
        font_size_formula="{typography.scale.xs}",
        space_after_formula="{spacing.xs}",
        color_formula="{color.text.muted}",
    ),
]


# =============================================================================
# INDEX STYLES
# =============================================================================

INDEX_STYLES = [
    ParagraphStyleDef(
        name="IndexBase",
        description="Base style for index entries",
        ooxml_id="IndexBase",
        odf_name="Index",
        font_size_formula="{typography.scale.sm}",
        line_height_formula="{typography.lineHeight.snug}",
        space_after_formula="{spacing.xs}",
    ),
    ParagraphStyleDef(
        name="IndexHeading",
        description="Index section heading (A, B, C...)",
        ooxml_id="IndexHeading",
        odf_name="Index Heading",
        based_on="IndexBase",
        font_weight=700,
        space_before_formula="{spacing.md}",
        space_after_formula="{spacing.sm}",
        keep_with_next=True,
    ),
    ParagraphStyleDef(
        name="Index1",
        description="Index level 1 entry",
        ooxml_id="Index1",
        odf_name="Index 1",
        based_on="IndexBase",
    ),
    ParagraphStyleDef(
        name="Index2",
        description="Index level 2 entry",
        ooxml_id="Index2",
        odf_name="Index 2",
        based_on="IndexBase",
        indent_left_formula="{spacing.md}",
    ),
    ParagraphStyleDef(
        name="Index3",
        description="Index level 3 entry",
        ooxml_id="Index3",
        odf_name="Index 3",
        based_on="IndexBase",
        indent_left_formula="{spacing.lg}",
    ),
    ParagraphStyleDef(
        name="IndexSeparator",
        description="Index section separator",
        ooxml_id="IndexSeparator",
        odf_name="Index Separator",
        based_on="IndexBase",
        alignment="center",
        space_before_formula="{spacing.sm}",
        space_after_formula="{spacing.sm}",
    ),
    # User-defined index styles (1-10)
    ParagraphStyleDef(
        name="UserIndex1",
        description="User index level 1",
        ooxml_id="UserIndex1",
        odf_name="User Index 1",
        based_on="IndexBase",
    ),
    ParagraphStyleDef(
        name="UserIndex2",
        description="User index level 2",
        ooxml_id="UserIndex2",
        odf_name="User Index 2",
        based_on="IndexBase",
        indent_left_formula="{spacing.md}",
    ),
    ParagraphStyleDef(
        name="UserIndex3",
        description="User index level 3",
        ooxml_id="UserIndex3",
        odf_name="User Index 3",
        based_on="IndexBase",
        indent_left_formula="{spacing.lg}",
    ),
    ParagraphStyleDef(
        name="UserIndex4",
        description="User index level 4",
        ooxml_id="UserIndex4",
        odf_name="User Index 4",
        based_on="IndexBase",
        indent_left_formula="{spacing.xl}",
    ),
    ParagraphStyleDef(
        name="UserIndex5",
        description="User index level 5",
        ooxml_id="UserIndex5",
        odf_name="User Index 5",
        based_on="IndexBase",
        indent_left_formula="{spacing.xl} + {spacing.sm}",
    ),
    ParagraphStyleDef(
        name="UserIndex6",
        description="User index level 6",
        ooxml_id="UserIndex6",
        odf_name="User Index 6",
        based_on="IndexBase",
        indent_left_formula="{spacing.xl} + {spacing.md}",
    ),
    ParagraphStyleDef(
        name="UserIndex7",
        description="User index level 7",
        ooxml_id="UserIndex7",
        odf_name="User Index 7",
        based_on="IndexBase",
        indent_left_formula="{spacing.2xl}",
    ),
    ParagraphStyleDef(
        name="UserIndex8",
        description="User index level 8",
        ooxml_id="UserIndex8",
        odf_name="User Index 8",
        based_on="IndexBase",
        indent_left_formula="{spacing.2xl} + {spacing.sm}",
    ),
    ParagraphStyleDef(
        name="UserIndex9",
        description="User index level 9",
        ooxml_id="UserIndex9",
        odf_name="User Index 9",
        based_on="IndexBase",
        indent_left_formula="{spacing.2xl} + {spacing.md}",
    ),
    ParagraphStyleDef(
        name="UserIndex10",
        description="User index level 10",
        ooxml_id="UserIndex10",
        odf_name="User Index 10",
        based_on="IndexBase",
        indent_left_formula="{spacing.3xl}",
    ),
    # Illustration/Table/Object indices
    ParagraphStyleDef(
        name="IllustrationIndex",
        description="Index of illustrations entry",
        ooxml_id="TableofFigures",
        odf_name="Illustration Index 1",
        based_on="IndexBase",
    ),
    ParagraphStyleDef(
        name="TableIndex",
        description="Index of tables entry",
        ooxml_id="TableofTables",
        odf_name="Table Index 1",
        based_on="IndexBase",
    ),
    ParagraphStyleDef(
        name="ObjectIndex",
        description="Index of objects entry",
        ooxml_id="TableofObjects",
        odf_name="Object Index 1",
        based_on="IndexBase",
    ),
    ParagraphStyleDef(
        name="BibliographyEntry",
        description="Bibliography entry",
        ooxml_id="Bibliography",
        odf_name="Bibliography 1",
        based_on="IndexBase",
        indent_left_formula="{spacing.lg}",
        indent_first_formula="-{spacing.lg}",  # Hanging indent
    ),
]


# =============================================================================
# SPECIAL STYLES
# =============================================================================

SPECIAL_STYLES = [
    ParagraphStyleDef(
        name="Title",
        description="Document title",
        ooxml_id="Title",
        odf_name="Title",
        font_family="{typography.font.heading}",
        font_size_formula="{typography.scale.3xl}",
        font_weight=700,
        line_height_formula="{typography.lineHeight.tight}",
        space_after_formula="{spacing.xl}",
        alignment="center",
        color_formula="{color.brand.primary}",
    ),
    ParagraphStyleDef(
        name="Subtitle",
        description="Document subtitle",
        ooxml_id="Subtitle",
        odf_name="Subtitle",
        font_family="{typography.font.heading}",
        font_size_formula="{typography.scale.xl}",
        font_weight=400,
        line_height_formula="{typography.lineHeight.snug}",
        space_after_formula="{spacing.2xl}",
        alignment="center",
        color_formula="{color.text.muted}",
    ),
    ParagraphStyleDef(
        name="Signature",
        description="Signature line",
        ooxml_id="Signature",
        odf_name="Signature",
        space_before_formula="{spacing.2xl}",
        alignment="left",
    ),
    ParagraphStyleDef(
        name="Salutation",
        description="Letter salutation (Dear...)",
        ooxml_id="Salutation",
        odf_name="Salutation",
        space_before_formula="{spacing.lg}",
        space_after_formula="{spacing.md}",
    ),
    ParagraphStyleDef(
        name="Closing",
        description="Letter closing (Sincerely...)",
        ooxml_id="Closing",
        odf_name="Complimentary close",
        space_before_formula="{spacing.lg}",
    ),
    ParagraphStyleDef(
        name="Date",
        description="Date line",
        ooxml_id="Date",
        odf_name="Date",
        space_after_formula="{spacing.md}",
    ),
    ParagraphStyleDef(
        name="Preformatted",
        description="Preformatted/code block",
        ooxml_id="HTMLPreformatted",
        odf_name="Preformatted Text",
        font_family="monospace",
        font_size_formula="{typography.scale.sm}",
        line_height_formula="{typography.lineHeight.normal}",
        space_before_formula="{spacing.md}",
        space_after_formula="{spacing.md}",
    ),
]


# =============================================================================
# ALL PARAGRAPH STYLES
# =============================================================================

ALL_PARAGRAPH_STYLES = (
    BODY_STYLES
    + QUOTE_STYLES
    + CAPTION_STYLES
    + HEADER_FOOTER_STYLES
    + NOTE_STYLES
    + LIST_STYLES
    + HEADING_STYLES
    + TOC_STYLES
    + INDEX_STYLES
    + SPECIAL_STYLES
)


def build_paragraph_styles(inputs: RecipeInputs) -> TokenGroup:
    """Build paragraph style tokens from inputs.

    Args:
        inputs: Recipe inputs for derivation

    Returns:
        TokenGroup containing all paragraph styles
    """
    group = TokenGroup(
        name="paragraph",
        description="Paragraph styles - body text, quotes, captions, etc.",
    )

    for style_def in ALL_PARAGRAPH_STYLES:
        style_group = _build_style_token(style_def, inputs)
        group.groups[style_def.name] = style_group

    return group


def _build_style_token(
    style_def: ParagraphStyleDef, inputs: RecipeInputs
) -> TokenGroup:
    """Build a single style as a TokenGroup."""
    spacing_map = {
        "spacing.xs": inputs.baseline_pt * 0.25,
        "spacing.sm": inputs.baseline_pt * 0.5,
        "spacing.md": inputs.baseline_pt * 1.0,
        "spacing.lg": inputs.baseline_pt * 1.5,
        "spacing.xl": inputs.baseline_pt * 2.0,
        "spacing.2xl": inputs.baseline_pt * 3.0,
        "spacing.3xl": inputs.baseline_pt * 4.0,
        "spacing.4xl": inputs.baseline_pt * 6.0,
    }
    ratio = inputs.type_scale_ratio
    scale_map = {
        "typography.scale.xs": inputs.baseline_pt * (ratio**-2),
        "typography.scale.sm": inputs.baseline_pt * (ratio**-1),
        "typography.scale.base": inputs.baseline_pt,
        "typography.scale.md": inputs.baseline_pt * (ratio**1),
        "typography.scale.lg": inputs.baseline_pt * (ratio**2),
        "typography.scale.xl": inputs.baseline_pt * (ratio**3),
        "typography.scale.2xl": inputs.baseline_pt * (ratio**4),
        "typography.scale.3xl": inputs.baseline_pt * (ratio**5),
        "typography.scale.4xl": inputs.baseline_pt * (ratio**6),
    }
    dimension_lookup = {**spacing_map, **scale_map}
    dimension_lookup["inputs.dimension.kerningThreshold"] = inputs.kerning_threshold_pt

    def _eval_expression(expr: str) -> float:
        parsed = ast.parse(expr, mode="eval")

        def _eval(node: ast.AST) -> float:
            if isinstance(node, ast.Expression):
                return _eval(node.body)
            if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
                return float(node.value)
            if isinstance(node, ast.UnaryOp) and isinstance(
                node.op, (ast.UAdd, ast.USub)
            ):
                value = _eval(node.operand)
                return value if isinstance(node.op, ast.UAdd) else -value
            if isinstance(node, ast.BinOp) and isinstance(
                node.op, (ast.Add, ast.Sub, ast.Mult, ast.Div)
            ):
                left = _eval(node.left)
                right = _eval(node.right)
                if isinstance(node.op, ast.Add):
                    return left + right
                if isinstance(node.op, ast.Sub):
                    return left - right
                if isinstance(node.op, ast.Mult):
                    return left * right
                return left / right
            raise ValueError(f"Unsupported formula expression: {expr}")

        return _eval(parsed)

    def _dimension_from_formula(formula: str) -> DimensionValue:
        value = formula.strip()
        try:
            return DimensionValue(value=float(value), unit="pt")
        except ValueError:
            pass
        for key, replacement in dimension_lookup.items():
            value = value.replace(f"{{{key}}}", str(replacement))
        resolved = _eval_expression(value)
        return DimensionValue(value=round(resolved, 2), unit="pt")

    style = TokenGroup(
        name=style_def.name,
        description=style_def.description,
    )

    # OOXML/ODF mapping
    style.tokens["ooxmlId"] = Token(
        name="ooxmlId",
        value=style_def.ooxml_id,
        description="Word styleId",
    )
    style.tokens["odfName"] = Token(
        name="odfName",
        value=style_def.odf_name,
        description="ODF style:name",
    )

    # Font family (reference)
    style.tokens["fontFamily"] = Token(
        name="fontFamily",
        value=style_def.font_family,
        type=TokenType.FONT_FAMILY,
    )

    # Font size (with formula)
    style.tokens["fontSize"] = Token(
        name="fontSize",
        value=style_def.font_size_formula,
        type=TokenType.DIMENSION,
        extensions=TokenExtensions(
            derivation_formula=style_def.font_size_formula,
            derivation_source="formula",
        ),
    )

    # Font weight
    style.tokens["fontWeight"] = Token(
        name="fontWeight",
        value=style_def.font_weight,
        type=TokenType.FONT_WEIGHT,
    )

    # Line height
    style.tokens["lineHeight"] = Token(
        name="lineHeight",
        value=style_def.line_height_formula,
        type=TokenType.NUMBER,
        extensions=TokenExtensions(
            derivation_formula=style_def.line_height_formula,
            derivation_source="formula",
        ),
    )

    # Spacing
    style.tokens["spaceBefore"] = Token(
        name="spaceBefore",
        value=_dimension_from_formula(style_def.space_before_formula),
        type=TokenType.DIMENSION,
        extensions=TokenExtensions(
            derivation_formula=style_def.space_before_formula,
            derivation_source="formula",
        ),
    )
    style.tokens["spaceAfter"] = Token(
        name="spaceAfter",
        value=_dimension_from_formula(style_def.space_after_formula),
        type=TokenType.DIMENSION,
        extensions=TokenExtensions(
            derivation_formula=style_def.space_after_formula,
            derivation_source="formula",
        ),
    )

    # Indentation
    style.tokens["indentLeft"] = Token(
        name="indentLeft",
        value=_dimension_from_formula(style_def.indent_left_formula),
        type=TokenType.DIMENSION,
        extensions=TokenExtensions(
            derivation_formula=style_def.indent_left_formula,
            derivation_source="formula",
        ),
    )
    style.tokens["indentFirst"] = Token(
        name="indentFirst",
        value=_dimension_from_formula(style_def.indent_first_formula),
        type=TokenType.DIMENSION,
        extensions=TokenExtensions(
            derivation_formula=style_def.indent_first_formula,
            derivation_source="formula",
        ),
    )

    # Kerning threshold
    style.tokens["kerningThreshold"] = Token(
        name="kerningThreshold",
        value=_dimension_from_formula(style_def.kerning_threshold_formula),
        type=TokenType.DIMENSION,
        extensions=TokenExtensions(
            derivation_formula=style_def.kerning_threshold_formula,
            derivation_source="formula",
        ),
    )

    # Alignment
    style.tokens["alignment"] = Token(
        name="alignment",
        value=style_def.alignment,
    )

    # Color
    style.tokens["color"] = Token(
        name="color",
        value=style_def.color_formula,
        type=TokenType.COLOR,
        extensions=TokenExtensions(
            derivation_formula=style_def.color_formula,
            derivation_source="formula",
        ),
    )

    # Paragraph control
    style.tokens["keepWithNext"] = Token(
        name="keepWithNext",
        value=1 if style_def.keep_with_next else 0,
        type=TokenType.NUMBER,
    )
    style.tokens["keepTogether"] = Token(
        name="keepTogether",
        value=1 if style_def.keep_together else 0,
        type=TokenType.NUMBER,
    )

    # Inheritance
    if style_def.based_on:
        style.tokens["basedOn"] = Token(
            name="basedOn",
            value=f"{{styles.paragraph.{style_def.based_on}.ooxmlId}}",
            description="Parent style",
        )

    if style_def.next_style:
        style.tokens["nextStyle"] = Token(
            name="nextStyle",
            value=f"{{styles.paragraph.{style_def.next_style}}}",
            description="Style for following paragraph",
        )

    return style
