"""
Multibagger Potential Score (StockLab overhaul, Part 22).

Architecturally separate from Investment Score on purpose — a company can score well here (high
growth, funded by reinvestment, still small enough to compound for years) while scoring only
average on the standard five-pillar Investment Score (which rewards current profitability/
stability more than growth optionality), and vice versa. This module is never imported by
`app/engines/scoring/overall.py` — confirmed by grep, same discipline as the Macro Context and
Accounting Quality separations elsewhere in this audit — and never should be, without the same
"verify before wiring" step every other cross-engine dependency in this codebase requires.

Every input is a metric this codebase already computes (revenue growth, margin trend, reinvestment
rate, dilution, valuation, market cap bucket) — no new data, no invented "multibagger score"
divorced from measurable inputs. Small/micro-cap bias is intentional (spec: multibaggers are
overwhelmingly found before they're large-cap) but is a real, disclosed scoring choice, not a
hidden one — see `market_cap_bucket_score`.
"""
from __future__ import annotations

from dataclasses import dataclass, field
from typing import Optional

from app.engines.types import MarketCapBucket


@dataclass(frozen=True)
class MultibaggerInputs:
    revenue_growth_cagr_3y: Optional[float] = None
    revenue_growth_trend: Optional[float] = None  # normalized slope, positive = accelerating
    operating_margin_trend: Optional[float] = None  # normalized slope, positive = expanding
    reinvestment_rate: Optional[float] = None  # capex+... / revenue — high reinvestment funds growth
    share_count_growth_cagr_3y: Optional[float] = None  # negative/low is good (little dilution)
    peg_ratio: Optional[float] = None  # lower is a better growth-adjusted entry price
    market_cap_bucket: Optional[MarketCapBucket] = None


@dataclass(frozen=True)
class MultibaggerComponent:
    name: str
    weight: float
    raw_score: float
    contribution: float


@dataclass(frozen=True)
class MultibaggerResult:
    value: Optional[float]  # 0-100
    components: list[MultibaggerComponent] = field(default_factory=list)


DEFAULT_WEIGHTS = {
    "growth_magnitude": 0.30,
    "growth_acceleration": 0.15,
    "margin_trajectory": 0.15,
    "reinvestment": 0.15,
    "low_dilution": 0.10,
    "valuation_reasonableness": 0.10,
    "market_cap_headroom": 0.05,
}

_CAP_SCORE = {
    MarketCapBucket.MICRO: 100.0,
    MarketCapBucket.SMALL: 85.0,
    MarketCapBucket.MID: 55.0,
    MarketCapBucket.LARGE: 20.0,  # not impossible (large caps can still multibag over long horizons) but structurally harder
}


def _scale(value: float, lo: float, hi: float) -> float:
    """Linear-map value in [lo, hi] to [0, 100], clamped at the ends."""
    if hi == lo:
        return 50.0
    pct = (value - lo) / (hi - lo)
    return max(0.0, min(100.0, pct * 100.0))


def compute_multibagger_score(inputs: MultibaggerInputs, weights: Optional[dict[str, float]] = None) -> MultibaggerResult:
    weights = dict(weights or DEFAULT_WEIGHTS)
    raw: dict[str, float] = {}

    if inputs.revenue_growth_cagr_3y is not None:
        raw["growth_magnitude"] = _scale(inputs.revenue_growth_cagr_3y, 0.0, 0.40)  # 0%->0, 40%+->100
    if inputs.revenue_growth_trend is not None:
        raw["growth_acceleration"] = _scale(inputs.revenue_growth_trend, -0.05, 0.05)
    if inputs.operating_margin_trend is not None:
        raw["margin_trajectory"] = _scale(inputs.operating_margin_trend, -0.05, 0.05)
    if inputs.reinvestment_rate is not None:
        raw["reinvestment"] = _scale(inputs.reinvestment_rate, 0.0, 0.30)
    if inputs.share_count_growth_cagr_3y is not None:
        raw["low_dilution"] = _scale(-inputs.share_count_growth_cagr_3y, -0.05, 0.05)  # shrinking/flat count scores high
    if inputs.peg_ratio is not None and inputs.peg_ratio > 0:
        raw["valuation_reasonableness"] = _scale(-inputs.peg_ratio, -4.0, -0.5)  # PEG 0.5->100, PEG 4+->0
    if inputs.market_cap_bucket is not None:
        raw["market_cap_headroom"] = _CAP_SCORE.get(inputs.market_cap_bucket, 50.0)

    available = {k: v for k, v in raw.items() if k in weights}
    if not available:
        return MultibaggerResult(value=None, components=[])
    total_w = sum(weights[k] for k in available)
    components = [
        MultibaggerComponent(name=k, weight=weights[k] / total_w, raw_score=v,
                              contribution=(weights[k] / total_w) * v)
        for k, v in available.items()
    ]
    value = sum(c.contribution for c in components)
    return MultibaggerResult(value=value, components=components)
