#!/usr/bin/env python3
"""Capture legacy-meter review payload as dated evidence JSON.

Usage:
    python scripts/capture_legacy_meter_evidence.py [--hours 8] [--days N] [--api-base URL] [--dry-run]

Default review window is 8 hours (post-deploy monitoring). Pass --days for the
weekly activity-core cadence (typically 7).

Writes:
    docs/evidence/legacy-meter-weekly-review-YYYYMMDD.json

When the hub is reachable, optionally marks interfaces retired via PATCH when
--retire-keys is supplied (comma-separated legacy-meter keys).
"""
from __future__ import annotations

import argparse
import datetime
import json
import os
import sys
import urllib.error
import urllib.request
from pathlib import Path

SCRIPT_DIR = Path(__file__).parent
REPO_ROOT = SCRIPT_DIR.parent
EVIDENCE_DIR = REPO_ROOT / "docs" / "evidence"


def _api_get(base: str, path: str, timeout: float = 30.0) -> dict:
    url = f"{base.rstrip('/')}{path}"
    with urllib.request.urlopen(url, timeout=timeout) as resp:
        return json.loads(resp.read())


def _api_patch(base: str, interface_id: str, body: dict, timeout: float = 30.0) -> dict:
    url = f"{base.rstrip('/')}/legacy-meter/interfaces/{interface_id}"
    data = json.dumps(body).encode()
    req = urllib.request.Request(
        url,
        data=data,
        method="PATCH",
        headers={"Content-Type": "application/json"},
    )
    with urllib.request.urlopen(req, timeout=timeout) as resp:
        return json.loads(resp.read())


def _retire_interfaces(base: str, keys: list[str]) -> list[dict]:
    interfaces = _api_get(base, "/legacy-meter/interfaces")
    by_key = {item["interface_key"]: item for item in interfaces}
    retired: list[dict] = []
    for key in keys:
        interface = by_key.get(key)
        if interface is None:
            print(f"skip retire: {key} not registered", file=sys.stderr)
            continue
        if interface.get("status") == "retired":
            print(f"skip retire: {key} already retired")
            retired.append(interface)
            continue
        updated = _api_patch(
            base,
            interface["id"],
            {"status": "retired", "notes": "Retired by STATE-WP-0069 closeout"},
        )
        print(f"retired: {key}")
        retired.append(updated)
    return retired


DEFAULT_REVIEW_DAYS = 7


def _review_query(*, days: int | None, hours: int | None) -> str:
    """Build the review-window query.

    Defaults to a 7-day window to match the ``weekly-review`` endpoint and the
    ``cadence: weekly`` label in the payload. An hours-scoped window only
    samples part of each day, so an interface called outside that band reads as
    unused and is falsely reported as a retirement candidate.
    """
    if hours is not None:
        return f"hours={hours}"
    return f"days={days or DEFAULT_REVIEW_DAYS}"


def capture(
    *,
    api_base: str,
    days: int | None,
    hours: int | None,
    retire_keys: list[str],
    dry_run: bool,
) -> Path:
    query = _review_query(days=days, hours=hours)
    review = _api_get(api_base, f"/legacy-meter/weekly-review?{query}")
    retired: list[dict] = []
    if retire_keys and not dry_run:
        retired = _retire_interfaces(api_base, retire_keys)
        review = _api_get(api_base, f"/legacy-meter/weekly-review?{query}")

    today = datetime.date.today().strftime("%Y%m%d")
    out_path = EVIDENCE_DIR / f"legacy-meter-weekly-review-{today}.json"
    payload: dict = {
        "captured_at": datetime.datetime.now(datetime.timezone.utc).isoformat(),
        "api_base": api_base,
        "workplan": "STATE-WP-0070",
        "retired_interfaces": retired,
        "weekly_review": review,
    }
    if hours is not None:
        payload["hours"] = hours
    else:
        payload["days"] = days or DEFAULT_REVIEW_DAYS

    if dry_run:
        print(json.dumps(payload, indent=2))
        return out_path

    EVIDENCE_DIR.mkdir(parents=True, exist_ok=True)
    out_path.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")
    candidates = len(review.get("retirement_candidates", []))
    interfaces = len(review.get("interfaces", []))
    window = query.replace("=", " ")
    print(f"wrote {out_path} ({interfaces} interfaces, {candidates} retirement candidates, {window})")
    return out_path


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument(
        "--hours",
        type=int,
        default=None,
        help="Review window in hours; only samples part of each day — use for "
        "spot checks, never as retirement evidence",
    )
    parser.add_argument(
        "--days",
        type=int,
        default=None,
        help=f"Review window in days (default: {DEFAULT_REVIEW_DAYS}, weekly cadence)",
    )
    parser.add_argument(
        "--api-base",
        default=os.environ.get("API_BASE", "http://127.0.0.1:8000"),
    )
    parser.add_argument(
        "--retire-keys",
        default="event_subject:org.statehub.workstream.completed",
        help="Comma-separated legacy-meter keys to mark retired before capture",
    )
    parser.add_argument("--dry-run", action="store_true")
    args = parser.parse_args()

    retire_keys = [key.strip() for key in args.retire_keys.split(",") if key.strip()]
    try:
        capture(
            api_base=args.api_base,
            days=args.days,
            hours=args.hours,
            retire_keys=retire_keys,
            dry_run=args.dry_run,
        )
    except urllib.error.URLError as exc:
        print(f"Error: could not reach State Hub API at {args.api_base}: {exc}", file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    main()