"""Constant-maturity benchmark generation from fitted bond curves."""

from __future__ import annotations

from dataclasses import dataclass
from decimal import Decimal

from fuggers_py.bonds.instruments import FixedBondBuilder
from fuggers_py.bonds.types import YieldCalculationRules
from fuggers_py.core.types import Currency, Date, Frequency
from fuggers_py.curves.fitted_bonds import (
    BondFairValueRequest,
    NotionalBenchmark,
    build_notional_benchmark,
    fair_value_from_fit,
)


def _to_decimal(value: object) -> Decimal:
    if isinstance(value, Decimal):
        return value
    return Decimal(str(value))


@dataclass(frozen=True, slots=True)
class ConstantMaturityBenchmark:
    """Synthetic constant-maturity benchmark built from a fitted curve."""

    target_maturity_years: Decimal
    coupon_rate: Decimal
    fair_value_clean_price: Decimal
    fair_value_dirty_price: Decimal
    fair_value_yield: Decimal
    benchmark: NotionalBenchmark


def generate_constant_maturity_benchmark(
    fit_result,
    target_maturity_years: object,
    *,
    component_count: int = 2,
    coupon_rate: object | None = None,
    frequency: Frequency = Frequency.SEMI_ANNUAL,
    currency: Currency = Currency.USD,
    benchmark_only: bool = True,
) -> ConstantMaturityBenchmark:
    """Generate a constant-maturity benchmark and fair-value estimate."""
    benchmark = build_notional_benchmark(
        fit_result,
        target_maturity_years,
        component_count=component_count,
        benchmark_only=benchmark_only,
    )
    target = _to_decimal(target_maturity_years)
    if coupon_rate is None:
        weighted_coupon = Decimal(0)
        for component in benchmark.components:
            point = fit_result.get_bond(component.instrument_id)
            weighted_coupon += component.weight * (point.coupon_rate or point.fitted_yield)
        resolved_coupon = weighted_coupon
    else:
        resolved_coupon = _to_decimal(coupon_rate)
    maturity_date = fit_result.reference_date.add_days(int(round(float(target) * 365.0)))
    synthetic_bond = (
        FixedBondBuilder.new()
        .with_issue_date(fit_result.reference_date)
        .with_maturity_date(maturity_date)
        .with_coupon_rate(resolved_coupon)
        .with_frequency(frequency)
        .with_currency(currency)
        .with_rules(YieldCalculationRules.us_treasury())
        .build()
    )
    fair_value = fair_value_from_fit(
        fit_result,
        BondFairValueRequest(
            bond=synthetic_bond,
            settlement_date=fit_result.reference_date,
        ),
    )
    return ConstantMaturityBenchmark(
        target_maturity_years=target,
        coupon_rate=resolved_coupon,
        fair_value_clean_price=fair_value.fair_value_clean_price,
        fair_value_dirty_price=fair_value.fair_value_dirty_price,
        fair_value_yield=fair_value.fair_value_yield,
        benchmark=benchmark,
    )


__all__ = ["ConstantMaturityBenchmark", "generate_constant_maturity_benchmark"]
