"""
Scenario definitions — Floor / Realistic / Stretch.

Every scenario parameter the cost and benefit engines read lives here;
no magic numbers in the calculation modules. Ships with the spec
defaults; callers may construct custom :class:`Scenario` objects.
"""

from __future__ import annotations

from dataclasses import dataclass, field


@dataclass
class Scenario:
    name: str

    # ── Cost-side drivers ───────────────────────────────────────────
    voice_bot_deflection: float          # share of voice volume deflected to bot
    voice_bot_avg_minutes: float         # bot minutes per deflected call
    # Agentic VA deflection is INCREMENTAL — applied to the residual volume
    # after the voice bot has already handled its share (layered model).
    # Effective total deflection = bot_rate + (1 − bot_rate) × va_rate.
    agentic_va_deflection: float         # share of RESIDUAL voice volume to agentic VA
    voice_summarization_eligibility: float
    voice_knowledge_eligibility: float
    email_auto_respond_rate: float       # share of email auto-responded

    # ── Virtual Agent benefit realization factors ───────────────────
    # Applied to both Voice Bot and Agentic VA deflection benefits.
    # completion_rate  — share of "deflected" calls that don't escalate to an agent
    #                    mid-session (bot/VA fully handles the interaction).
    # labour_realization — staffing flexibility: deflected volume doesn't reduce
    #                    headcount 1:1 due to minimums, shrinkage, occupancy ceilings.
    # callback_discount — fraction of deflected calls that re-enter as repeat contacts
    #                    (poorly-handled deflections drive callbacks).
    # Combined realistic factor: 0.70 × 0.80 × (1 − 0.05) ≈ 0.53
    va_completion_rate: float = 0.70
    va_labour_realization: float = 0.80
    va_callback_discount: float = 0.05

    # year -> fraction of full benefit realized
    benefit_realization: dict[int, float] = field(default_factory=dict)

    # year -> fraction of steady-state consumption cost incurred.
    # Per-user licenses are paid in full from day 1; consumption meters
    # ramp with usage (default Y1 = 70%).
    consumption_cost_realization: dict[int, float] = field(
        default_factory=lambda: {1: 0.70, 2: 1.0, 3: 1.0}
    )

    def realization(self, year: int) -> float:
        if year in self.benefit_realization:
            return self.benefit_realization[year]
        last = max(self.benefit_realization, default=0)
        return self.benefit_realization.get(last, 1.0) if year > last else 0.0

    def cost_realization(self, year: int) -> float:
        if year in self.consumption_cost_realization:
            return self.consumption_cost_realization[year]
        last = max(self.consumption_cost_realization, default=0)
        return (
            self.consumption_cost_realization.get(last, 1.0) if year > last else 0.0
        )


#: Benefit reduction parameters. ``claim`` = Genesys ROI-doc figure;
#: ``realistic`` = pressure-tested midpoint of the spec's Y1 range.
#: The benefit engine uses ``realistic`` by default; ``claim`` powers
#: the side-by-side comparison view.
BENEFIT_PARAMS: dict[str, dict[str, float]] = {
    "voice_aht_knowledge_reduction": {"claim": 0.094, "realistic": 0.055},  # 4-7% Y1
    "voice_acw_reduction": {"claim": 1.00, "realistic": 0.40},              # 30-50% Y1
    "digital_aht_reduction": {"claim": 0.18, "realistic": 0.085},           # 5-12% Y1
    "digital_acw_reduction": {"claim": 1.00, "realistic": 0.40},            # 30-50% Y1
    "sta_aht_reduction": {"claim": 0.04, "realistic": 0.015},               # 1-2% Y1
    # ESTIMATED lines (no Genesys claim published):
    "supervisor_copilot_time_saving": {"claim": 0.10, "realistic": 0.05},
    "predictive_routing_aht_reduction": {"claim": 0.04, "realistic": 0.02},
    # Virtual Agent realization factors.
    # ``claim`` = 100% realization (original model assumption — no haircuts).
    # ``realistic`` = production-calibrated midpoints per the spec analysis.
    "va_completion_rate":    {"claim": 1.00, "realistic": 0.70},  # 60-75% voice bot; 50-70% agentic VA Y1
    "va_labour_realization": {"claim": 1.00, "realistic": 0.80},  # 70-85% staffing flexibility
    "va_callback_discount":  {"claim": 0.00, "realistic": 0.05},  # 5-10% deflected re-enter as repeat contacts
}


SCENARIOS: dict[str, Scenario] = {
    "floor": Scenario(
        name="floor",
        voice_bot_deflection=0.20,
        voice_bot_avg_minutes=1.0,
        agentic_va_deflection=0.05,
        voice_summarization_eligibility=0.50,
        voice_knowledge_eligibility=0.40,
        email_auto_respond_rate=0.10,
        # VA realization: conservative — low completion, limited staffing flex
        # Combined: 0.60 × 0.70 × (1 − 0.05) ≈ 0.40
        va_completion_rate=0.60,
        va_labour_realization=0.70,
        va_callback_discount=0.05,
        benefit_realization={1: 0.30, 2: 0.60, 3: 0.80},
    ),
    "realistic": Scenario(
        name="realistic",
        voice_bot_deflection=0.35,
        voice_bot_avg_minutes=1.5,
        agentic_va_deflection=0.15,
        voice_summarization_eligibility=0.70,
        voice_knowledge_eligibility=0.60,
        email_auto_respond_rate=0.20,
        # VA realization: production midpoints per spec analysis
        # Combined: 0.70 × 0.80 × (1 − 0.05) ≈ 0.53
        va_completion_rate=0.70,
        va_labour_realization=0.80,
        va_callback_discount=0.05,
        benefit_realization={1: 0.50, 2: 0.80, 3: 0.95},
    ),
    "stretch": Scenario(
        name="stretch",
        voice_bot_deflection=0.50,
        voice_bot_avg_minutes=2.0,
        agentic_va_deflection=0.25,
        voice_summarization_eligibility=0.90,
        voice_knowledge_eligibility=0.80,
        email_auto_respond_rate=0.50,
        # VA realization: optimistic — high completion, good staffing flexibility
        # Combined: 0.75 × 0.85 × (1 − 0.03) ≈ 0.62
        va_completion_rate=0.75,
        va_labour_realization=0.85,
        va_callback_discount=0.03,
        benefit_realization={1: 0.75, 2: 0.95, 3: 1.00},
    ),
}


def get_scenario(name: str) -> Scenario:
    try:
        return SCENARIOS[name.lower()]
    except KeyError as e:
        raise KeyError(
            f"Unknown scenario {name!r}. Valid: {sorted(SCENARIOS)}"
        ) from e
