"""Proceeds asset-swap spread helpers.

This variant scales the par-par spread by the dirty-price proceeds notional.
The public output is still a raw decimal spread.
"""

from __future__ import annotations

from dataclasses import dataclass
from decimal import Decimal

from fuggers_py.bonds.instruments import FixedBond
from fuggers_py.core.traits import YieldCurve
from fuggers_py.core.types import Date


@dataclass(frozen=True, slots=True)
class ProceedsAssetSwap:
    """Proceeds-style asset-swap spread calculator."""

    curve: YieldCurve

    def calculate(self, bond: FixedBond, dirty_price: object, settlement_date: Date) -> Decimal:
        """Return the proceeds-style asset-swap spread as a raw decimal."""

        annuity = self._annuity(bond, settlement_date)
        if annuity == 0:
            return Decimal(0)
        swap_rate = self._swap_rate(bond, settlement_date)
        price = Decimal(str(dirty_price))
        if price <= 0:
            raise ValueError("dirty_price must be positive for proceeds asset-swap spread.")

        par_par_spread = ((Decimal(100) - price) / annuity) + bond.coupon_rate() - swap_rate
        return par_par_spread * (Decimal(100) / price)

    def _swap_rate(self, bond: FixedBond, settlement_date: Date) -> Decimal:
        start_df = self.curve.discount_factor(settlement_date)
        end_df = self.curve.discount_factor(bond.maturity_date())
        annuity = self._annuity(bond, settlement_date)
        if annuity == 0 or start_df <= 0:
            return Decimal(0)
        return (Decimal(1) - end_df / start_df) / annuity

    def _annuity(self, bond: FixedBond, settlement_date: Date) -> Decimal:
        day_count = bond.rules().accrual_day_count_obj()
        start_df = self.curve.discount_factor(settlement_date)
        if start_df <= 0:
            raise ValueError("Curve discount factor at settlement must be positive.")
        annuity = Decimal(0)
        for cf in bond.cash_flows(settlement_date):
            if not cf.is_coupon():
                continue
            accrual_start = cf.accrual_start or settlement_date
            accrual_end = cf.accrual_end or cf.date
            tau = day_count.year_fraction(accrual_start, accrual_end)
            annuity += tau * self.curve.discount_factor(cf.date) / start_df
        return annuity


__all__ = ["ProceedsAssetSwap"]
