#!/usr/bin/env python3
"""Run the complete Polyform population experiment through public HTTP APIs."""

from __future__ import annotations

import argparse
import json
from pathlib import Path
import subprocess
import sys
import zipfile

import httpx

from polyform_backend.simulator import Experiment, run_experiment, run_quarantine_experiment


FAULT_EOCD = "locate_end_of_central_directory.faulty_short_comment"
FAULT_PATH = "normalize_extraction_path.faulty_depth_limit"


def rust_release(root: Path) -> dict:
    binary = root / "target" / "debug" / "zipdiv"
    result = subprocess.run(
        [str(binary), "variants", "--json", "--include-experimental"],
        check=True,
        text=True,
        capture_output=True,
    )
    release = json.loads(result.stdout)
    commit = subprocess.run(
        ["git", "rev-parse", "HEAD"], cwd=root, check=False, text=True, capture_output=True
    ).stdout.strip() or "main"
    repository = "https://github.com/omalled/polyform"
    release.update({"repository_url": repository, "source_commit": commit, "spec_path": "specs/zipdiv.toml"})
    for spec in release["spec_functions"]:
        for implementation in spec["implementations"]:
            provenance = implementation.setdefault("provenance", {})
            source_path = provenance.get("path", "rust/src/variants.rs")
            provenance["source_url"] = f"{repository}/blob/{commit}/{source_path}"
    return release


def register_release(api: httpx.Client, body: dict) -> int:
    response = api.post("/api/v1/releases", json=body)
    if response.status_code == 409:
        releases = api.get("/api/v1/releases").raise_for_status().json()
        existing = next(
            item for item in releases
            if item["application"] == body["application"] and item["version"] == body["version"]
        )
        return int(existing["id"])
    return int(response.raise_for_status().json()["id"])


def actual_rust_clients(root: Path, url: str, release_id: int, count: int, phase: str) -> dict:
    artifacts = root / "artifacts"
    artifacts.mkdir(parents=True, exist_ok=True)
    long_comment = artifacts / "demo-long-comment.zip"
    with zipfile.ZipFile(long_comment, "w") as archive:
        archive.writestr("hello.txt", "Polyform real ZIP workload\n", compress_type=zipfile.ZIP_STORED)
        archive.comment = b"controlled long comment " + b"x" * 2048
    deep_path = artifacts / "demo-deep-path.zip"
    with zipfile.ZipFile(deep_path, "w") as archive:
        archive.writestr("one/two/three.txt", "diversity " * 200, compress_type=zipfile.ZIP_DEFLATED)
    binary = root / "target" / "debug" / "zipdiv"
    outcomes = []
    for index in range(count):
        common = [
            str(binary), "--server", url, "--release-id", str(release_id),
            "--installation-id", f"actual-rust-client-{index}",
        ]
        commands = [
            ("long_comment", common + ["list", str(long_comment)]),
            ("deep_path", common + [
                "extract", str(deep_path), str(artifacts / f"extract-{phase}-{index}")
            ]),
        ]
        for workload, command in commands:
            result = subprocess.run(command, check=False, text=True, capture_output=True)
            lines = (result.stdout or result.stderr).strip().splitlines()
            outcomes.append({
                "client": index,
                "workload": workload,
                "success": result.returncode == 0,
                "result": lines[-1] if lines else "",
            })
    return {
        "clients": count,
        "workloads": len(outcomes),
        "successes": sum(item["success"] for item in outcomes),
        "failures": sum(not item["success"] for item in outcomes),
        "outcomes": outcomes,
    }


def concise(result: dict) -> dict:
    return {
        "mode": result["mode"],
        "clients": result["clients"],
        "executions": result["executions"],
        "failures": result["failures"],
        "failure_rate": result["failures"] / result["executions"] if result["executions"] else 0,
        "affected_fraction": result["affected_fraction"],
    }


def main() -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--url", default="http://127.0.0.1:8000")
    parser.add_argument("--clients", type=int, default=72)
    parser.add_argument("--runs", type=int, default=4)
    parser.add_argument("--actual-clients", type=int, default=3)
    parser.add_argument("--output", type=Path, default=Path("artifacts/demo-results.json"))
    parser.add_argument(
        "--leave-faults-active",
        action="store_true",
        help="Prepare an interactive experiment without automatically quarantining its faults.",
    )
    args = parser.parse_args()
    root = Path(__file__).resolve().parents[1]

    with httpx.Client(base_url=args.url, timeout=30) as api:
        api.get("/health").raise_for_status()
        release_id = register_release(api, rust_release(root))

    cohort = max(12, args.clients)
    if args.leave_faults_active:
        balanced = run_experiment(
            args.url, release_id,
            Experiment(cohort, args.runs, "diversified", "balanced", (FAULT_EOCD, FAULT_PATH), None, 202),
        )
        comparison = {"balanced": concise(balanced)}
    else:
        homogeneous = run_experiment(
            args.url, release_id,
            Experiment(cohort, args.runs, "homogeneous", "balanced", (FAULT_EOCD,), None, 101),
        )
        random_result = run_experiment(
            args.url, release_id,
            Experiment(cohort, args.runs, "diversified", "random", (FAULT_EOCD, FAULT_PATH), None, 202),
        )
        balanced = run_experiment(
            args.url, release_id,
            Experiment(cohort, args.runs, "diversified", "balanced", (FAULT_EOCD, FAULT_PATH), None, 202),
        )
        comparison = {
            "homogeneous": concise(homogeneous),
            "random": concise(random_result),
            "balanced": concise(balanced),
        }

    with httpx.Client(base_url=args.url, timeout=30) as api:
        suspects = api.get(
            "/api/v1/stats/implementations", params={"release_id": release_id}
        ).raise_for_status().json()
    ranked = sorted(
        suspects,
        key=lambda item: (item["failure_rate"], item["executions"]),
        reverse=True,
    )

    rust_before = actual_rust_clients(root, args.url, release_id, args.actual_clients, "before")
    quarantine = None
    rust_after = None
    if not args.leave_faults_active:
        quarantine = run_quarantine_experiment(
            args.url,
            release_id,
            Experiment(cohort, args.runs, "diversified", "balanced", (FAULT_EOCD, FAULT_PATH), None, 404),
            (FAULT_EOCD, FAULT_PATH),
        )
        rust_after = actual_rust_clients(root, args.url, release_id, args.actual_clients, "after")
    with httpx.Client(base_url=args.url, timeout=30) as api:
        final_summary = api.get(
            "/api/v1/dashboard/summary", params={"release_id": release_id}
        ).raise_for_status().json()

    report = {
        "release_id": release_id,
        "ground_truth_file": "rust/experiment/faulty_variants.json",
        "comparison": comparison,
        "top_suspects_before_quarantine": ranked[:8],
        "quarantine": ({
            "changes": quarantine["quarantine"],
            "before": concise(quarantine["before"]),
            "after": concise(quarantine["after"]),
            "failure_rate_drop": quarantine["failure_rate_drop"],
        } if quarantine else {"changes": [], "before": None, "after": None, "failure_rate_drop": None}),
        "actual_rust_clients": {"before_quarantine": rust_before, "after_quarantine": rust_after},
        "final_summary": final_summary,
    }
    args.output.parent.mkdir(parents=True, exist_ok=True)
    args.output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n")

    print(f"Registered ZIP release {release_id} with {final_summary['spec_functions']} spec functions")
    for label, result in report["comparison"].items():
        print(
            f"{label:11} affected={result['affected_fraction']:.1%} "
            f"failure_rate={result['failure_rate']:.1%}"
        )
    print("Top statistical suspects before quarantine:")
    for item in ranked[:4]:
        risk = "infinite" if item.get("infinite_relative_risk") else f"{item.get('relative_risk', 0):.2f}x"
        print(f"  {item['id']}: failures={item['failures']}/{item['executions']}, risk={risk}")
    if quarantine and rust_after:
        print(
            f"Quarantine recomposed {sum(item['affected_clients'] for item in quarantine['quarantine'])} assignments; "
            f"cohort failure rate {quarantine['before']['failures']/quarantine['before']['executions']:.1%} "
            f"→ {quarantine['after']['failures']/quarantine['after']['executions']:.1%}"
        )
        print(
            f"Actual Rust workloads: {rust_before['failures']} failures before quarantine, "
            f"{rust_after['failures']} after"
        )
    else:
        print("Controlled faults remain active for manual quarantine in the live dashboard")
        print(f"Actual Rust workloads: {rust_before['failures']} failures before intervention")
    print(f"Full report: {args.output}")
    return 0


if __name__ == "__main__":
    sys.exit(main())
