"""
Implementation rollout & ramp model.

Captures the gap between **when Genesys starts billing** (contract
start) and **when each region actually goes live**:

- The platform licence commit bills in full from contract start; the
  vendor's *ramp period* compensates with a first-year credit
  (typical: 6-month ramp → 50% Y1 discount on the platform commit).
- AI token usage (per-user and consumption meters) starts only when a
  site goes live, and bills for the months the site is live in each
  model year.
- Benefits likewise accrue only from go-live (the scenario realization
  curve then models adoption maturity *within* the live period).

A site with ``go_live_month = m`` is live for ``12*year − m`` months of
the first ``year`` years (clamped to 0..12 per year). So NAM at month 6
is live 6 months of Y1; EMEA at month 9 → 3 months; AUZ/APAC at month
12 → 0 months in Y1 and fully live from Y2.
"""

from __future__ import annotations

from dataclasses import dataclass, field

MONTHS_PER_YEAR = 12


@dataclass
class RolloutPlan:
    #: ISO date Genesys starts billing the licence commit (informational,
    #: surfaced in UI/exports; the model works in months-from-start).
    contract_start: str | None = None

    #: Total build duration, months (informational).
    build_months: int = 12

    #: Vendor ramp period, months. Documentation for the Y1 credit below.
    ramp_months: int = 6

    #: First-year credit on the platform licence commit. Typical
    #: 6-month ramp = 50% discount in year 1; years 2+ bill in full.
    first_year_platform_discount: float = 0.5

    #: site_name -> go-live month (months after contract start).
    #: Sites absent from the map are treated as live from day 0.
    go_live_month: dict[str, int] = field(default_factory=dict)

    def __post_init__(self) -> None:
        if not 0.0 <= self.first_year_platform_discount <= 1.0:
            raise ValueError("first_year_platform_discount must be within 0-1")
        if self.ramp_months < 0 or self.build_months < 0:
            raise ValueError("ramp_months/build_months must be >= 0")
        for site, m in self.go_live_month.items():
            if m < 0:
                raise ValueError(f"{site}: go_live_month must be >= 0")

    # ── Availability ────────────────────────────────────────────────

    def live_months_in_year(self, site_name: str, year: int) -> int:
        """Months ``site_name`` is live during model year ``year`` (1-based)."""
        go_live = self.go_live_month.get(site_name, 0)
        live_by_year_end = max(0, MONTHS_PER_YEAR * year - go_live)
        live_by_prev_year_end = max(0, MONTHS_PER_YEAR * (year - 1) - go_live)
        return min(MONTHS_PER_YEAR, live_by_year_end - live_by_prev_year_end)

    def fraction_live(self, site_name: str, year: int) -> float:
        return self.live_months_in_year(site_name, year) / MONTHS_PER_YEAR

    # ── Billing ─────────────────────────────────────────────────────

    def platform_factor(self, year: int) -> float:
        """Fraction of the full platform commit billed in ``year``."""
        return 1.0 - self.first_year_platform_discount if year == 1 else 1.0


#: Behaviour identical to the pre-rollout model: everything live from
#: day 0, no ramp credit.
NO_ROLLOUT = RolloutPlan(
    build_months=0, ramp_months=0, first_year_platform_discount=0.0
)
