"""Typography unit conversion facilities for OOXML documents."""

from __future__ import annotations

import math
from dataclasses import dataclass

from .scalars import (
    EMU_PER_INCH,
    EMU_PER_POINT,
    EMU_PER_TWIP,
    HALF_POINTS_PER_POINT,
    HUNDREDTHS_PER_POINT,
    TWIPS_PER_POINT,
)


@dataclass(slots=True, frozen=True)
class TypographyContext:
    """Context for typography calculations with baseline grid.

    Provides the baseline grid and font metrics needed for
    snapping values to the vertical rhythm.
    """

    baseline_pt: float
    baseline_emu: int
    x_height_ratio: float = 0.5

    @property
    def baseline_twips(self) -> int:
        return int(round(self.baseline_pt * TWIPS_PER_POINT))

    def snap_pt(self, value: float) -> float:
        """Snap a point value to the nearest baseline grid line."""
        if self.baseline_pt <= 0:
            return value
        return round(value / self.baseline_pt) * self.baseline_pt

    def snap_up_pt(self, value: float) -> float:
        """Snap a point value up to the next baseline grid line."""
        if self.baseline_pt <= 0:
            return value
        return math.ceil(value / self.baseline_pt) * self.baseline_pt

    def snap_twips(self, value: int) -> int:
        """Snap a twips value to the nearest baseline grid line."""
        if self.baseline_twips <= 0:
            return value
        return int(round(value / self.baseline_twips) * self.baseline_twips)

    def snap_up_twips(self, value: int) -> int:
        """Snap a twips value up to the next baseline grid line."""
        if self.baseline_twips <= 0:
            return value
        return int(math.ceil(value / self.baseline_twips) * self.baseline_twips)


class TypographyConverter:
    """Convert between typography units with optional baseline snapping.

    Example:
        conv = TypographyConverter()

        # Simple conversions
        emu = conv.pt_to_emu(12.0)
        twips = conv.pt_to_twips(12.0)

        # With baseline context for snapping
        ctx = TypographyContext(baseline_pt=12.0, baseline_emu=152400)
        line_height = conv.line_height_twips(16.0, multiplier=1.4, context=ctx)
    """

    def __init__(self, context: TypographyContext | None = None) -> None:
        self.context = context

    # --- Point conversions ---

    def pt_to_emu(self, points: float) -> int:
        """Convert points to EMU."""
        return int(round(points * EMU_PER_POINT))

    def pt_to_twips(self, points: float) -> int:
        """Convert points to twips."""
        return int(round(points * TWIPS_PER_POINT))

    def pt_to_half_points(self, points: float) -> int:
        """Convert points to half-points (OOXML sz attribute)."""
        return int(round(points * HALF_POINTS_PER_POINT))

    def pt_to_hundredths(self, points: float) -> int:
        """Convert points to hundredths of a point."""
        return int(round(points * HUNDREDTHS_PER_POINT))

    # --- EMU conversions ---

    def emu_to_pt(self, emu: int) -> float:
        """Convert EMU to points."""
        return emu / EMU_PER_POINT

    def emu_to_twips(self, emu: int) -> int:
        """Convert EMU to twips."""
        return int(round(emu / EMU_PER_TWIP))

    # --- Twips conversions ---

    def twips_to_pt(self, twips: int) -> float:
        """Convert twips to points."""
        return twips / TWIPS_PER_POINT

    def twips_to_emu(self, twips: int) -> int:
        """Convert twips to EMU."""
        return int(round(twips * EMU_PER_TWIP))

    # --- Snapped conversions (require context) ---

    def pt_to_twips_snapped(
        self,
        points: float,
        context: TypographyContext | None = None,
    ) -> int:
        """Convert points to twips, snapped to baseline grid."""
        ctx = context or self.context
        twips = self.pt_to_twips(points)
        if ctx:
            return ctx.snap_twips(twips)
        return twips

    def line_height_twips(
        self,
        font_size_pt: float,
        multiplier: float = 1.4,
        context: TypographyContext | None = None,
        snap_up: bool = True,
    ) -> int:
        """Calculate line height in twips with baseline snapping.

        Args:
            font_size_pt: Font size in points
            multiplier: Line height multiplier (e.g., 1.4 for 140%)
            context: Typography context for baseline grid
            snap_up: If True, snap up to next grid line (default)
        """
        raw_pt = font_size_pt * multiplier
        raw_twips = self.pt_to_twips(raw_pt)

        ctx = context or self.context
        if ctx:
            if snap_up:
                return ctx.snap_up_twips(raw_twips)
            return ctx.snap_twips(raw_twips)
        return raw_twips

    def spacing_twips(
        self,
        baseline_multiples: float,
        context: TypographyContext | None = None,
    ) -> int:
        """Calculate spacing as multiples of baseline grid.

        Args:
            baseline_multiples: Number of baseline grid units (e.g., 2.0)
            context: Typography context with baseline grid
        """
        ctx = context or self.context
        if not ctx:
            raise ValueError("spacing_twips requires a TypographyContext")
        return int(round(ctx.baseline_twips * baseline_multiples))


# Convenience functions for simple conversions


def pt_to_emu(points: float) -> int:
    """Convert points to EMU."""
    return int(round(points * EMU_PER_POINT))


def pt_to_twips(points: float) -> int:
    """Convert points to twips."""
    return int(round(points * TWIPS_PER_POINT))


def emu_to_pt(emu: int) -> float:
    """Convert EMU to points."""
    return emu / EMU_PER_POINT


def emu_to_twips(emu: int) -> int:
    """Convert EMU to twips."""
    return int(round(emu / EMU_PER_TWIP))


def twips_to_pt(twips: int) -> float:
    """Convert twips to points."""
    return twips / TWIPS_PER_POINT


def twips_to_emu(twips: int) -> int:
    """Convert twips to EMU."""
    return int(round(twips * EMU_PER_TWIP))


def inches_to_emu(inches: float) -> int:
    """Convert inches to EMU."""
    return int(round(inches * EMU_PER_INCH))


def emu_to_inches(emu: int) -> float:
    """Convert EMU to inches."""
    return emu / EMU_PER_INCH


def pt_to_hundredths(points: float) -> int:
    """Convert points to hundredths of a point (OOXML sz attribute)."""
    return int(round(points * HUNDREDTHS_PER_POINT))


def pt_to_half_points(points: float) -> int:
    """Convert points to half-points (OOXML font size sz attribute)."""
    return int(round(points * HALF_POINTS_PER_POINT))


__all__ = [
    "TypographyContext",
    "TypographyConverter",
    "pt_to_emu",
    "pt_to_twips",
    "pt_to_hundredths",
    "pt_to_half_points",
    "emu_to_pt",
    "emu_to_twips",
    "twips_to_pt",
    "twips_to_emu",
    "inches_to_emu",
    "emu_to_inches",
]
