"""Compare the current state with future-state scenarios on the same synthetic cases.

Usage: python3 python/simulate_future_state.py
Writes outputs/simulation_results.json. Every lever effect is an assumption
(see docs/assumptions.md); results are estimates, not measured outcomes.
"""
import json
from collections import defaultdict
from pathlib import Path

import dispute_model as m

ROOT = Path(__file__).resolve().parent.parent
ANNUALIZE = 52 / m.WEEKS
LOADED_COST_PER_HOUR = 45.0  # illustrative assumption for break-even maths only
PRODUCTIVE_HOURS_PER_FTE = 1650  # illustrative assumption

ALL = {k: True for k in m.CURRENT_STATE}
SCENARIOS = [
    ("current", "Current state", {}, 1.0),
    ("digital_intake", "Guided digital intake only", {"digital_intake": True}, 1.0),
    ("assisted_triage", "Rule-assisted triage only", {"assisted_triage": True}, 1.0),
    ("fast_track", "Low-value fast track only", {"fast_track": True}, 1.0),
    ("visual_management", "Visual management board only", {"visual_management": True}, 1.0),
    ("prefilled_chargeback", "Pre-filled chargeback only", {"prefilled_chargeback": True}, 1.0),
    ("future_conservative", "All five, half effect", ALL, 0.5),
    ("future_base", "All five, full effect", ALL, 1.0),
]


def r1(x, places=1):
    """Round half away from zero, matching SQLite's ROUND()."""
    from decimal import Decimal, ROUND_HALF_UP
    return float(Decimal(repr(x)).quantize(Decimal(1).scaleb(-places), rounding=ROUND_HALF_UP))


def pct(values, q):
    """Same rule as the SQL: the value at 1-based position floor(q * n)."""
    s = sorted(values)
    return s[int(q * len(s)) - 1]


def metrics(disputes, steps):
    by_case = defaultdict(list)
    for s in steps:
        by_case[s["case_id"]].append(s)
    internal, total, touch, queue, rework_cases, handoffs = [], [], 0.0, 0.0, 0, []
    specialist_min = 0.0
    for case_id, rows in by_case.items():
        t = sum(r["touch_minutes"] for r in rows) / 60
        q = sum(r["wait_hours"] for r in rows if r["wait_type"] == "internal_queue")
        w = sum(r["wait_hours"] for r in rows)
        internal.append((t + q) / 24)
        total.append((t + w) / 24)
        touch += t
        queue += q
        rework_cases += any(r["is_rework"] for r in rows)
        teams = [r["team_id"] for r in rows]
        handoffs.append(sum(1 for a, b in zip(teams, teams[1:]) if a != b))
        specialist_min += sum(r["touch_minutes"] for r in rows if r["team_id"] in ("dispute_analysts", "chargeback_team"))
    n = len(by_case)
    fast = [d for d in disputes if d["fast_tracked"]]
    return {
        "cases": n,
        "median_internal_days": round(pct(internal, 0.5), 1),
        "p90_internal_days": round(pct(internal, 0.9), 1),
        "median_total_days": round(pct(total, 0.5), 1),
        "p90_total_days": round(pct(total, 0.9), 1),
        "pct_over_45_days": r1(100 * sum(x > 45 for x in total) / n),
        "pct_over_56_days": r1(100 * sum(x > 56 for x in total) / n),
        "internal_pce_pct": round(100 * touch / (touch + queue), 2),
        "rework_rate_pct": round(100 * rework_cases / n, 1),
        "avg_handoffs": round(sum(handoffs) / n, 2),
        "touch_hours": round(touch, 0),
        "specialist_hours": round(specialist_min / 60, 0),
        "fast_tracked_cases": len(fast),
        "fast_track_written_off_cad": round(sum(d["amount_cad"] for d in fast), 0),
    }


def main():
    draws = m.draw_cases()
    results = {}
    for key, label, levers, effect in SCENARIOS:
        disputes, steps, _ = m.run(draws, levers, effect)
        results[key] = dict(metrics(disputes, steps), label=label, effect=effect)

    base = results["current"]
    for key, r in results.items():
        r["touch_hours_saved_per_year"] = round((base["touch_hours"] - r["touch_hours"]) * ANNUALIZE, 0)
        r["fte_freed_per_year"] = round(r["touch_hours_saved_per_year"] / PRODUCTIVE_HOURS_PER_FTE, 2)

    ft = results["fast_track"]
    hours = ft["touch_hours_saved_per_year"]
    writeoff = ft["fast_track_written_off_cad"] * ANNUALIZE
    break_even = {
        "hours_saved_per_year": hours,
        "written_off_cad_per_year": round(writeoff, 0),
        "labour_value_cad_per_year": round(hours * LOADED_COST_PER_HOUR, 0),
        "break_even_cost_per_hour": round(writeoff / hours, 2) if hours else None,
        "assumed_loaded_cost_per_hour": LOADED_COST_PER_HOUR,
    }
    out = {"scenarios": results, "fast_track_break_even": break_even,
           "assumptions": {"annualize_factor": round(ANNUALIZE, 2), "loaded_cost_per_hour": LOADED_COST_PER_HOUR,
                           "productive_hours_per_fte": PRODUCTIVE_HOURS_PER_FTE,
                           "fast_track_limit_cad": m.FAST_TRACK_LIMIT, "lever_effects": m.LEVER_EFFECTS}}
    (ROOT / "outputs").mkdir(exist_ok=True)
    (ROOT / "outputs" / "simulation_results.json").write_text(json.dumps(out, indent=2))

    cols = ["median_internal_days", "p90_internal_days", "pct_over_45_days", "pct_over_56_days",
            "rework_rate_pct", "internal_pce_pct", "touch_hours", "fast_tracked_cases"]
    print("scenario".ljust(24) + "".join(c[:12].rjust(13) for c in cols))
    for key, r in results.items():
        print(key.ljust(24) + "".join(str(r[c]).rjust(13) for c in cols))
    print("fast track break-even:", break_even)


if __name__ == "__main__":
    main()
