"""Aggregate greeks for a multi-leg position (``docs/03-algorithms.md §5``).

Pure summation with the BUY/SELL sign and the leg size. Theta is
converted to USD/day; the other greeks are returned in the same units
they came in (per-contract, dimensionless or fractional).
"""

from __future__ import annotations

from decimal import Decimal

from pydantic import BaseModel, ConfigDict

from cerbero_bite.core.types import OptionLeg

__all__ = ["AggregateGreeks", "aggregate"]


class AggregateGreeks(BaseModel):
    """Net greeks for an option spread."""

    model_config = ConfigDict(frozen=True, extra="forbid")

    delta_net: Decimal
    gamma_net: Decimal
    theta_net: Decimal  # USD per day
    vega_net: Decimal


def _sign(side: str) -> Decimal:
    return Decimal("1") if side == "BUY" else Decimal("-1")


def aggregate(
    *,
    legs: list[OptionLeg],
    eth_price_usd: Decimal,
) -> AggregateGreeks:
    """Return :class:`AggregateGreeks` summed with side and size weights.

    ``theta`` is multiplied by ``eth_price_usd`` to convert from
    ETH/day (Deribit native) to USD/day, which is what the report
    consumes.
    """
    delta = Decimal("0")
    gamma = Decimal("0")
    theta_eth = Decimal("0")
    vega = Decimal("0")

    for leg in legs:
        sign = _sign(leg.side)
        weight = sign * Decimal(leg.size)
        delta += weight * leg.delta
        gamma += weight * leg.gamma
        theta_eth += weight * leg.theta
        vega += weight * leg.vega

    return AggregateGreeks(
        delta_net=delta,
        gamma_net=gamma,
        theta_net=theta_eth * eth_price_usd,
        vega_net=vega,
    )
