"""Buy/Sell decision table (spec §27 / docs/SCORING.md §5). Never keys off a single metric."""
from __future__ import annotations

from enum import IntEnum
from typing import Optional


class Recommendation(IntEnum):
    STRONG_SELL = 1
    SELL = 2
    REDUCE = 3
    WAIT = 4
    HOLD = 5
    ACCUMULATE = 6
    BUY = 7
    STRONG_BUY = 8


class RiskBand(IntEnum):
    HIGH = 1
    MEDIUM = 2
    LOW = 3


def risk_band_from_score(risk_score_0_100: Optional[float]) -> RiskBand:
    """risk_score convention: higher = lower risk (consistent with every other 0-100 score in
    this codebase, where higher is always better/safer)."""
    if risk_score_0_100 is None:
        return RiskBand.MEDIUM
    if risk_score_0_100 >= 70:
        return RiskBand.LOW
    if risk_score_0_100 >= 40:
        return RiskBand.MEDIUM
    return RiskBand.HIGH


def recommendation_from_table(
    overall_score: Optional[float],
    margin_of_safety: Optional[float],
    risk_score_0_100: Optional[float],
    expected_cagr_base: Optional[float],
) -> Recommendation:
    if overall_score is None or margin_of_safety is None:
        return Recommendation.WAIT  # insufficient data to make an affirmative call

    risk = risk_band_from_score(risk_score_0_100)
    cagr = expected_cagr_base if expected_cagr_base is not None else -1.0

    if overall_score >= 80 and margin_of_safety >= 0.20 and risk in (RiskBand.LOW, RiskBand.MEDIUM) and cagr >= 0.12:
        return Recommendation.STRONG_BUY
    if overall_score >= 65 and margin_of_safety >= 0.10 and cagr >= 0.08:
        return Recommendation.BUY
    if overall_score >= 50 and margin_of_safety >= 0.0 and cagr >= 0.05:
        return Recommendation.ACCUMULATE
    if overall_score >= 40 and -0.10 <= margin_of_safety < 0.20:
        return Recommendation.HOLD
    return Recommendation.WAIT
