"""Common font transformation functions for use with Theme computed fonts."""

from armonia.theme import Theme
from armonia.typography import ComputeFontFn, Font


def scale_size(font_key: str, factor: float) -> ComputeFontFn:
    """Return a font with its size multiplied by ``factor``.

    :param font_key: The name of the base font in the theme.
    :type font_key: str
    :param factor: Multiplication factor. Must be greater than 0.
    :type factor: float
    :return: A function that computes the scaled font from the theme.
    :rtype: ComputeFontFn
    :raises ValueError: If ``factor`` is not greater than 0.

    Example::

        theme.set_computed_font("heading", ff.scale_size("body", 1.5))
    """
    if factor <= 0:
        raise ValueError(f"scale_size factor must be greater than 0, got {factor!r}")

    def compute(theme: Theme) -> Font:
        font = theme.get_font(font_key)
        return font.variant(size=font.size * factor)

    return compute


def adjust_size(font_key: str, delta: float) -> ComputeFontFn:
    """Return a font with its size increased or decreased by ``delta`` pixels.

    :param font_key: The name of the base font in the theme.
    :type font_key: str
    :param delta: The amount to add to the font size. May be negative.
    :type delta: float
    :return: A function that computes the size-adjusted font from the theme.
    :rtype: ComputeFontFn

    Example::

        theme.set_computed_font("small", ff.adjust_size("body", -4.0))
    """

    def compute(theme: Theme) -> Font:
        font = theme.get_font(font_key)
        return font.variant(size=font.size + delta)

    return compute


def bolder(font_key: str, amount: int = 100) -> ComputeFontFn:
    """Return a font with its weight increased by ``amount``, clamped to 1000.

    :param font_key: The name of the base font in the theme.
    :type font_key: str
    :param amount: Weight increase step. Defaults to 100.
    :type amount: int
    :return: A function that computes the bolder font from the theme.
    :rtype: ComputeFontFn

    Example::

        theme.set_computed_font("body_bold", ff.bolder("body", 300))
    """

    def compute(theme: Theme) -> Font:
        font = theme.get_font(font_key)
        return font.variant(weight=min(1000, font.weight + amount))

    return compute


def lighter(font_key: str, amount: int = 100) -> ComputeFontFn:
    """Return a font with its weight decreased by ``amount``, clamped to 1.

    :param font_key: The name of the base font in the theme.
    :type font_key: str
    :param amount: Weight decrease step. Defaults to 100.
    :type amount: int
    :return: A function that computes the lighter-weight font from the theme.
    :rtype: ComputeFontFn

    Example::

        theme.set_computed_font("body_light", ff.lighter("body", 100))
    """

    def compute(theme: Theme) -> Font:
        font = theme.get_font(font_key)
        return font.variant(weight=max(1, font.weight - amount))

    return compute


def italic(font_key: str) -> ComputeFontFn:
    """Return a font with italic style enabled.

    :param font_key: The name of the base font in the theme.
    :type font_key: str
    :return: A function that computes the italic font from the theme.
    :rtype: ComputeFontFn

    Example::

        theme.set_computed_font("body_italic", ff.italic("body"))
    """

    def compute(theme: Theme) -> Font:
        font = theme.get_font(font_key)
        return font.variant(italic=True)

    return compute


def roman(font_key: str) -> ComputeFontFn:
    """Return a font with italic style disabled (roman / upright).

    :param font_key: The name of the base font in the theme.
    :type font_key: str
    :return: A function that computes the roman font from the theme.
    :rtype: ComputeFontFn

    Example::

        theme.set_computed_font("caption_roman", ff.roman("caption_italic"))
    """

    def compute(theme: Theme) -> Font:
        font = theme.get_font(font_key)
        return font.variant(italic=False)

    return compute


def adjust(
    font_key: str,
    size_delta: float = 0.0,
    weight_delta: int = 0,
    make_italic: bool | None = None,
) -> ComputeFontFn:
    """Return a font with multiple properties adjusted at once.

    :param font_key: The name of the base font in the theme.
    :type font_key: str
    :param size_delta: Amount to add to the font size. Defaults to 0.0.
    :type size_delta: float
    :param weight_delta: Amount to add to the font weight, clamped to 1-1000.
        Defaults to 0.
    :type weight_delta: int
    :param make_italic: Override italic flag, or ``None`` to keep current.
        Defaults to ``None``.
    :type make_italic: bool | None
    :return: A function that computes the adjusted font from the theme.
    :rtype: ComputeFontFn

    Example::

        theme.set_computed_font("emphasis", ff.adjust("body", size_delta=2.0, make_italic=True))
    """

    def compute(theme: Theme) -> Font:
        font = theme.get_font(font_key)
        new_size = font.size + size_delta
        new_weight = max(1, min(1000, font.weight + weight_delta))
        new_italic = make_italic if make_italic is not None else font.italic
        return font.variant(size=new_size, weight=new_weight, italic=new_italic)

    return compute


def alias(font_key: str) -> ComputeFontFn:
    """Return a computed font that resolves to another font by name.

    :param font_key: The name of the font to alias.
    :type font_key: str
    :return: A function that returns the aliased font from the theme.
    :rtype: ComputeFontFn

    Example::

        theme.set_computed_font("ui", ff.alias("body"))
    """

    def compute(theme: Theme) -> Font:
        return theme.get_font(font_key)

    return compute


__all__ = [
    "adjust",
    "adjust_size",
    "alias",
    "bolder",
    "italic",
    "lighter",
    "roman",
    "scale_size",
]
