"""Appendix-4 corrected business case — hand-check acceptance numbers."""

from __future__ import annotations

import datetime as dt
import math

import pytest

from tokencalc import appendix4 as a4
from tokencalc.defaults import CTM_DEFAULT_SITES, DEFAULT_METERS, DEFAULT_PRICING

SITES = list(CTM_DEFAULT_SITES)


def test_verbatim_crossfoots_to_slide_totals():
    df = a4.verbatim_dataframe()
    for r, expect in a4.SLIDE_TOTALS["regional_3yr"].items():
        got = df.loc[df.region == r, "three_yr"].sum()
        assert abs(got - expect) <= a4.crossfoot_tolerance(expect), r
    for c, expect in a4.SLIDE_TOTALS["capability_3yr"].items():
        got = df.loc[df.capability == c, "three_yr"].sum()
        assert abs(got - expect) <= a4.crossfoot_tolerance(expect), c
    assert abs(df["three_yr"].sum() - a4.SLIDE_TOTALS["total_3yr"]) <= \
        a4.crossfoot_tolerance(a4.SLIDE_TOTALS["total_3yr"])


def test_benefits_phase_on_the_pm_schedule():
    _, _, benefit_rollout = a4.build_rollouts(SITES)
    long = a4.benefits_by_year(benefit_rollout)
    by_year = long.groupby("year")["benefit"].sum()
    # Earliest region (NA) realizes Jan 2027 → 2026 still $0.
    assert by_year[2026] == 0.0, "2026 must be $0 (NA realizes Jan 2027)"
    # Scaling at the finest grain reproduces every verbatim 3-yr value exactly.
    for (region, cap), (_, three_yr) in a4.VERBATIM_BENEFITS.items():
        got = long.query("region == @region and capability == @cap")["benefit"].sum()
        assert got == pytest.approx(three_yr)


def test_ramp_zeroes_year_one_licences():
    assert a4.licence_costs_by_year(12) == {2026: 0.0, 2027: 4_300_000.0,
                                            2028: 4_300_000.0}
    assert a4.licence_costs_by_year(0)[2026] == 4_300_000.0
    assert a4.licence_costs_by_year(18)[2027] == pytest.approx(4_300_000 * 6 / 12)
    # The order form's ramp is 6 months — licences bill from July 2026.
    assert a4.DEFAULT_RAMP_MONTHS == 6
    assert a4.licence_costs_by_year() == {2026: 2_150_000.0, 2027: 4_300_000.0,
                                          2028: 4_300_000.0}


def test_current_state_run_off():
    cs = a4.current_state_inputs(SITES)
    assert cs["annual_cost"].sum() == pytest.approx(7_300_000)
    by_year = a4.current_costs_by_year(cs)
    assert by_year == {2026: pytest.approx(7_300_000),
                       2027: pytest.approx(7_300_000), 2028: 0.0}
    cs.loc["NA", "contract_termination"] = dt.date(2028, 6, 30)
    assert a4.current_costs_by_year(cs)[2028] == pytest.approx(
        cs.loc["NA", "annual_cost"] * 6 / 12)


def test_token_hand_checks():
    token_ro, email_ro, _ = a4.build_rollouts(SITES)
    core, email = a4.build_scopes(SITES, copilot_includes_asia=False)
    meters = {**DEFAULT_METERS, "Email AI (Auto-Respond)": a4.autorespond_meter(0.05)}
    long = a4.token_costs_by_year(SITES, meters, DEFAULT_PRICING,
                                  a4.claim_scenario(0.255), core, email,
                                  token_ro, email_ro)
    # STA 2028: every site live all 12 months under the compressed PM
    # timeline (APAC live from Nov 2027) → full-year STA for all sites.
    sta = long.query("cost_line == 'Speech & Text Analytics [named]'")
    assert sta.query("year == 2028")["annual_cost"].sum() == pytest.approx(751_680)
    # Agent Copilot 2028 (ASIA off): 1,490 users × 40 tokens × 12 months.
    cp = long.query("cost_line == 'Agent Copilot [named]' and year == 2028")
    assert cp["annual_cost"].sum() == pytest.approx(1_490 * 40 * 12)
    # Rule 1: Copilot covers AI Summary at Copilot sites.
    assert (long.query("cost_line == 'AI Summary & Insights'")["annual_cost"] == 0).all()
    # Under the PM timeline NA goes live Oct 2026, so tokens DO accrue in
    # 2026 (NA only — three live months); no longer a $0 year.
    assert long.query("year == 2026")["annual_cost"].sum() == pytest.approx(424_350)
    # PR NAM steady-month tokens.
    assert math.ceil(
        1_214_358 * DEFAULT_METERS["Predictive Routing"].tokens_per_unit) == 71_433


def test_impl_costs_reconcile_with_v2_doc():
    _, impl_y, kb_y, steady_y = a4.build_impl_costs(SITES, "mid", 225.0,
                                                    include_kb=True)
    assert sum(impl_y.values()) == pytest.approx(5_850 * 225)   # V2's "$1.3M"
    assert sum(kb_y.values()) == pytest.approx(1_000 * 225)
    assert steady_y == {2026: 0.0, 2027: pytest.approx(700 * 225),
                        2028: pytest.approx(700 * 225)}
    # Impl spend is fully booked by each region's implementation month.
    assert a4.impl_year_fractions(18) == pytest.approx([12 / 18, 6 / 18, 0.0])
    assert a4.impl_year_fractions(27) == pytest.approx([12 / 27, 12 / 27, 3 / 27])


def test_case_flows_and_kpis():
    benefits = {2026: 0.0, 2027: 2_000_000.0, 2028: 12_000_000.0}
    costs = {2026: 10_000_000.0, 2027: 13_000_000.0, 2028: 8_000_000.0}
    inc, net = a4.case_flows(costs, benefits)
    assert inc == {2026: pytest.approx(2_700_000),
                   2027: pytest.approx(5_700_000),
                   2028: pytest.approx(700_000)}
    for y in a4.YEARS:
        assert net[y] == pytest.approx(benefits[y] - inc[y])
    kpis = a4.case_kpis(inc, net)
    assert kpis["benefits_3yr"] == pytest.approx(sum(benefits.values()))
    assert kpis["net_3yr"] == pytest.approx(sum(net.values()))
    assert kpis["roi"] == pytest.approx(kpis["net_3yr"] / kpis["incremental_cost_3yr"])
    assert kpis["discount_rate"] == 0.135
    # Net cost saving → ROI undefined.
    inc2 = {y: -1.0 for y in a4.YEARS}
    net2 = {y: benefits[y] + 1.0 for y in a4.YEARS}
    assert a4.case_kpis(inc2, net2)["roi"] is None


def test_contracted_overlays_verbatim():
    assert a4.tco("ccaas_annual") == 3_200_000            # signed contract
    assert a4.TCO_VERBATIM["ccaas_annual"] == 4_300_000   # deck record intact
    assert a4.tco("current_annual") == a4.TCO_VERBATIM["current_annual"]
    assert a4.licence_costs_by_year(12, a4.tco("ccaas_annual"))[2027] == 3_200_000


def test_sow_milestones_and_managed_services():
    assert a4.PS_CONTRACTED_TOTAL == pytest.approx(2_025_446.48)
    for m in a4.PS_MILESTONES:                            # amounts match the shares
        assert m["amount"] == pytest.approx(m["share"] * a4.PS_CONTRACTED_TOTAL,
                                            abs=0.01)
    ps = a4.ps_costs_by_year(contracted=True)             # 50/50 across 2026-27
    assert ps[2026] == pytest.approx(607_633.94 + 405_089.30 + 167_000)
    assert ps[2027] == pytest.approx(607_633.94 + 405_089.30)
    assert ps[2028] == 0.0
    # The deck's verbatim year-1 lump stays intact for the as-pitched frame.
    assert a4.ps_costs_by_year() == {2026: 2_567_000, 2027: 0.0, 2028: 0.0}
    # Managed services bill from the month after MCX go-live (Oct 16 → Nov).
    ms = a4.managed_services_by_year()
    assert ms[2026] == pytest.approx(410_918.40 * 2 / 12)
    assert ms[2027] == pytest.approx(410_918.40)
    assert ms[2028] == pytest.approx(410_918.40)
