"""
Input bundles — validated dataclasses, no untyped dicts.

All volumes are MONTHLY; all AHT/ACW figures are SECONDS; all labour
costs are ANNUAL fully-loaded USD.
"""

from __future__ import annotations

from dataclasses import dataclass, field

from .meters import Confidence

#: Sanity bounds for handle times (seconds).
AHT_MIN_SECONDS = 10
AHT_MAX_SECONDS = 3600

#: Working hours per FTE-year used to derive per-second labour rates.
WORKING_HOURS_PER_YEAR = 2_080
WORKING_SECONDS_PER_YEAR = WORKING_HOURS_PER_YEAR * 3600


@dataclass
class SiteInput:
    site_name: str          # "NAM", "EMEA", "AUZ", "APAC HK", …
    region_pricing: str     # "US", "AU", "EU", "APAC"
    agents: int             # excluding supervisors
    supervisors: int
    voice_volume_monthly: int
    email_volume_monthly: int
    chat_volume_monthly: int
    sms_volume_monthly: int
    voice_aht_seconds: int
    email_aht_seconds: int
    chat_aht_seconds: int
    voice_acw_seconds: int
    fully_loaded_agent_cost_annual: float
    fully_loaded_supervisor_cost_annual: float
    licence_type: str = "named"   # "named" | "concurrent"
    languages: list[str] = field(default_factory=list)

    def __post_init__(self) -> None:
        if self.licence_type not in ("named", "concurrent"):
            raise ValueError(
                f"{self.site_name}: licence_type must be 'named' or 'concurrent', "
                f"got {self.licence_type!r}"
            )
        if self.agents < 0 or self.supervisors < 0:
            raise ValueError(f"{self.site_name}: agent/supervisor counts must be >= 0")
        for name in (
            "voice_volume_monthly",
            "email_volume_monthly",
            "chat_volume_monthly",
            "sms_volume_monthly",
        ):
            if getattr(self, name) < 0:
                raise ValueError(f"{self.site_name}: {name} must be >= 0")
        for name in ("voice_aht_seconds", "email_aht_seconds", "chat_aht_seconds"):
            v = getattr(self, name)
            if v and not AHT_MIN_SECONDS <= v <= AHT_MAX_SECONDS:
                raise ValueError(
                    f"{self.site_name}: {name}={v}s outside sensible bounds "
                    f"({AHT_MIN_SECONDS}-{AHT_MAX_SECONDS}s)"
                )
        if self.voice_acw_seconds < 0:
            raise ValueError(f"{self.site_name}: voice_acw_seconds must be >= 0")

    @property
    def named_users(self) -> int:
        return self.agents + self.supervisors

    @property
    def agent_cost_per_second(self) -> float:
        """Fully-loaded agent labour rate per working second (DBZ-safe)."""
        return self.fully_loaded_agent_cost_annual / WORKING_SECONDS_PER_YEAR

    @property
    def supervisor_cost_per_second(self) -> float:
        return self.fully_loaded_supervisor_cost_annual / WORKING_SECONDS_PER_YEAR


@dataclass
class FeatureScope:
    """Which feature is enabled at which sites, in which phase.

    ``phase`` is the model year (1-3) the feature switches on;
    ``adoption_curve`` maps model year -> adoption fraction (0.0-1.0)
    applied to consumption-metered features (per-user licenses are paid
    in full from the phase year onward).
    """

    feature: str
    enabled_sites: list[str]
    phase: int = 1
    adoption_curve: dict[int, float] = field(default_factory=dict)
    deflection_target: float | None = None
    eligibility_pct: float | None = None

    def __post_init__(self) -> None:
        if self.phase < 1:
            raise ValueError(f"{self.feature}: phase must be >= 1")
        for year, pct in self.adoption_curve.items():
            if not 0.0 <= pct <= 1.0:
                raise ValueError(
                    f"{self.feature}: adoption_curve[{year}]={pct} outside 0-1"
                )
        for name in ("deflection_target", "eligibility_pct"):
            v = getattr(self, name)
            if v is not None and not 0.0 <= v <= 1.0:
                raise ValueError(f"{self.feature}: {name}={v} outside 0-1")

    def active(self, site_name: str, year: int) -> bool:
        return site_name in self.enabled_sites and year >= self.phase

    def adoption(self, year: int) -> float:
        """Adoption fraction for ``year`` (1.0 when no curve given)."""
        if not self.adoption_curve:
            return 1.0
        if year in self.adoption_curve:
            return self.adoption_curve[year]
        # Past the last defined year → hold the last value.
        last = max(self.adoption_curve)
        return self.adoption_curve[last] if year > last else 0.0


@dataclass
class CostTakeout:
    """A retired platform/licence whose cost the programme reclaims.

    ``start_month`` (1-12, within ``start_year``) prorates the first
    active year — e.g. NICE IEX can only be switched off once NAM is
    live, so start_year=1, start_month=7 reclaims 6/12 of Y1.
    """

    name: str               # "NICE IEX (NAM)", "Legacy CC platform", …
    annual_cost: float
    start_year: int = 1
    confidence: Confidence = Confidence.ESTIMATED
    notes: str = ""
    start_month: int = 1

    def __post_init__(self) -> None:
        if self.annual_cost < 0:
            raise ValueError(f"{self.name}: annual_cost must be >= 0")
        if self.start_year < 1:
            raise ValueError(f"{self.name}: start_year must be >= 1")
        if not 1 <= self.start_month <= 12:
            raise ValueError(f"{self.name}: start_month must be 1-12")

    def value_in_year(self, year: int) -> float:
        if year < self.start_year:
            return 0.0
        if year == self.start_year:
            return self.annual_cost * (12 - (self.start_month - 1)) / 12
        return self.annual_cost
