#!/usr/bin/env python3
"""Reproduce NomadCrew's 2027 cross-region calendar model (Python 3, stdlib only).

Run: python reproduce.py bank-holidays-source.json output-directory
Uses the frozen GOV.UK snapshot supplied with this script. No network calls.
Both start and end dates count as full days away, including the return day.
"""
import csv
import hashlib
import json
import sys
from datetime import date, timedelta
from pathlib import Path

REGIONS = ("england-and-wales", "scotland", "northern-ireland")
FIELDS = ("start_date", "end_date", "calendar_days", "leave_england_wales",
          "leave_scotland", "leave_northern_ireland", "max_individual_leave",
          "total_crew_leave", "leave_gap", "leave_dates_england_wales",
          "leave_dates_scotland", "leave_dates_northern_ireland")


def calculate(source):
    holidays = {r: {date.fromisoformat(e["date"]) for e in source[r]["events"]}
                for r in REGIONS}
    # Fail rather than silently miscount a boundary if the source lacks 2028.
    for region in REGIONS:
        assert date(2028, 1, 3) in holidays[region], region
    rows = []
    start = date(2027, 1, 1)
    while start.year == 2027:
        for length in range(3, 11):
            days = [start + timedelta(days=i) for i in range(length)]
            leave = [[d for d in days if d.weekday() < 5 and d not in holidays[r]]
                     for r in REGIONS]
            counts = [len(dates) for dates in leave]
            rows.append(dict(zip(FIELDS, [
                start.isoformat(), days[-1].isoformat(), length, *counts,
                max(counts), sum(counts), max(counts) - min(counts),
                *[";".join(d.isoformat() for d in dates) for dates in leave]
            ])))
        start += timedelta(days=1)
    return rows


def main():
    if len(sys.argv) != 3:
        raise SystemExit("Usage: python reproduce.py bank-holidays-source.json output-directory")
    snapshot = Path(sys.argv[1])
    output = Path(sys.argv[2])
    source_bytes = snapshot.read_bytes()
    rows = calculate(json.loads(source_bytes))
    assert len(rows) == 2920
    by_window = {(r["start_date"], r["calendar_days"]): r for r in rows}
    assert [by_window[("2027-12-25", 10)][k] for k in FIELDS[3:6]] == [3, 3, 3]
    assert [by_window[("2027-03-26", 10)][k] for k in FIELDS[3:6]] == [4, 5, 4]
    august_free = [r for r in rows if r["start_date"].startswith("2027-08")
                   and r["max_individual_leave"] == 0]
    assert not august_free
    output.mkdir(parents=True, exist_ok=True)
    with (output / "windows.csv").open("w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=FIELDS, lineterminator="\n")
        writer.writeheader()
        writer.writerows(rows)
    summary = {
        "model_version": "1.0", "source_checked_date_utc": "2026-10-03",
        "source_url": "https://www.gov.uk/bank-holidays.json",
        "source_sha256": hashlib.sha256(source_bytes).hexdigest(),
        "windows": len(rows), "regions": list(REGIONS),
        "christmas": by_window[("2027-12-25", 10)],
        "easter": by_window[("2027-03-26", 10)],
        "august_zero_leave_windows": len(august_free),
        "minimum_max_leave_for_10_days": min(r["max_individual_leave"] for r in rows if r["calendar_days"] == 10),
    }
    (output / "summary.json").write_bytes((json.dumps(summary, indent=2) + "\n").encode("utf-8"))
    print(json.dumps(summary, indent=2))


if __name__ == "__main__":
    main()
