"""WACC (spec §25 / docs/VALUATION.md §1).

AUDIT (StockLab overhaul, Part 9): every input that can fall back to a default is now tracked in
`WACCResult.sources` as an explicit `{input_name: "ACTUAL" | "ASSUMED"}` map, with the assumed
value and the reason surfaced in `WACCResult.assumption_notes` — e.g. `"Beta 1.00 — Source:
ASSUMED (no disclosed/regressed beta available, used market-neutral default)"`. Before this pass,
`beta`'s `DEFAULT_BETA_IF_MISSING` fallback and `cost_of_debt_pretax`'s spread-over-risk-free
fallback were applied silently — the caller had no way to tell a real disclosed beta from the 1.0
default short of re-deriving it themselves. `risk_free_rate` and `equity_risk_premium` are ALWAYS
assumptions (there is no "actual" value for a forward-looking rate) — flagged as ASSUMED
unconditionally, matching their existing dataclass-field documentation, now made runtime-visible
rather than only a source comment.
"""
from __future__ import annotations

from dataclasses import dataclass, field
from typing import Optional


@dataclass(frozen=True)
class WACCInputs:
    risk_free_rate: float          # e.g. 0.045 for 4.5% — ASSUMPTION, configurable per currency
    beta: Optional[float]
    equity_risk_premium: float     # e.g. 0.045 — ASSUMPTION, configurable per market
    cost_of_debt_pretax: Optional[float]   # interest_expense / total_debt, with a floor
    tax_rate: float
    market_cap: Optional[float]
    total_debt: Optional[float]


@dataclass(frozen=True)
class WACCResult:
    value: Optional[float]
    cost_of_equity: Optional[float]
    cost_of_debt_after_tax: Optional[float]
    equity_weight: Optional[float]
    debt_weight: Optional[float]
    inputs: WACCInputs
    beta_used: Optional[float] = None
    cost_of_debt_pretax_used: Optional[float] = None
    sources: dict = field(default_factory=dict)          # {"beta": "ACTUAL"|"ASSUMED", ...}
    assumption_notes: list = field(default_factory=list)  # human-readable, one per ASSUMED input


# Configurable defaults — spec §57 (every threshold configurable). Callers (the recompute worker,
# reading from app.core.config.Settings) may override either; module constants remain the
# fallback for direct engine use/tests.
DEFAULT_BETA_IF_MISSING = 1.0
MIN_COST_OF_DEBT = 0.01  # floor so a near-zero disclosed rate doesn't distort WACC
RISK_FREE_SPREAD_FALLBACK = 0.015  # spread-over-risk-free used when no disclosed cost of debt exists


def compute_wacc(
    inputs: WACCInputs,
    default_beta_if_missing: float = DEFAULT_BETA_IF_MISSING,
    min_cost_of_debt: float = MIN_COST_OF_DEBT,
    risk_free_spread_fallback: float = RISK_FREE_SPREAD_FALLBACK,
) -> WACCResult:
    sources: dict[str, str] = {
        "risk_free_rate": "ASSUMED",
        "equity_risk_premium": "ASSUMED",
    }
    notes: list[str] = [
        f"Risk-free rate {inputs.risk_free_rate:.4f} — Source: ASSUMED (configured default, not "
        f"security-specific)",
        f"Equity risk premium {inputs.equity_risk_premium:.4f} — Source: ASSUMED (configured "
        f"default, not security- or market-specific)",
    ]

    if inputs.beta is not None:
        beta = inputs.beta
        sources["beta"] = "ACTUAL"
    else:
        beta = default_beta_if_missing
        sources["beta"] = "ASSUMED"
        notes.append(f"Beta {beta:.2f} — Source: ASSUMED (no disclosed/regressed beta available, "
                      f"used market-neutral default of {default_beta_if_missing:.2f})")
    cost_of_equity = inputs.risk_free_rate + beta * inputs.equity_risk_premium

    cost_of_debt = inputs.cost_of_debt_pretax
    if cost_of_debt is None or cost_of_debt < min_cost_of_debt:
        cost_of_debt = max(inputs.risk_free_rate + risk_free_spread_fallback, min_cost_of_debt)
        sources["cost_of_debt_pretax"] = "ASSUMED"
        notes.append(f"Pre-tax cost of debt {cost_of_debt:.4f} — Source: ASSUMED (no disclosed "
                      f"interest-expense/total-debt ratio in range; used risk-free + "
                      f"{risk_free_spread_fallback:.3f} spread floor)")
    else:
        sources["cost_of_debt_pretax"] = "ACTUAL"
    cost_of_debt_after_tax = cost_of_debt * (1 - inputs.tax_rate)

    e = inputs.market_cap or 0.0
    d = inputs.total_debt or 0.0
    v = e + d
    if v <= 0:
        return WACCResult(None, cost_of_equity, cost_of_debt_after_tax, None, None, inputs,
                           beta_used=beta, cost_of_debt_pretax_used=cost_of_debt,
                           sources=sources, assumption_notes=notes)

    ew, dw = e / v, d / v
    wacc = ew * cost_of_equity + dw * cost_of_debt_after_tax
    return WACCResult(wacc, cost_of_equity, cost_of_debt_after_tax, ew, dw, inputs,
                       beta_used=beta, cost_of_debt_pretax_used=cost_of_debt,
                       sources=sources, assumption_notes=notes)
