#!/usr/bin/env python3
"""Verify CARBON Failure copies first, injects only in sandbox, and detects original drift."""

from __future__ import annotations

import json
import os
import subprocess
import tempfile
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
LAUNCHER = ROOT / "bin" / "carbon-runtime"


def run(*args: str, ok: bool = True) -> subprocess.CompletedProcess[str]:
    result = subprocess.run(
        [str(LAUNCHER), "failure", *args],
        text=True,
        capture_output=True,
        check=False,
    )
    if ok and result.returncode != 0:
        raise AssertionError(result.stderr or result.stdout)
    if not ok and result.returncode == 0:
        raise AssertionError(f"failure {' '.join(args)} unexpectedly succeeded")
    return result


with tempfile.TemporaryDirectory(prefix="carbon-failure-contract-") as temporary:
    project = Path(temporary) / "sample-app"
    source = project / "src"
    source.mkdir(parents=True)
    original_config = source / "config.json"
    original_code = source / "app.js"
    original_config.write_text('{"endpoint":"http://127.0.0.1:3000","retries":2}\n', encoding="utf-8")
    original_code.write_text("export const recover = () => true;\n", encoding="utf-8")
    external = Path(temporary) / "external-secret.txt"
    external.write_text("must-not-be-copied", encoding="utf-8")
    os.symlink(external, source / "external-link")
    (project / ".env").write_text("SECRET=must-not-be-copied\n", encoding="utf-8")

    prepared = json.loads(run(
        "prepare", "--root", str(project), "--artifact", "src", "--scope", "API recovery"
    ).stdout)
    run_dir = Path(prepared["runDir"])
    baseline = Path(prepared["baselineDir"])
    sandbox = Path(prepared["sandboxDir"])
    assert (baseline / "src" / "config.json").is_file()
    assert (sandbox / "src" / "config.json").is_file()
    assert not (sandbox / "src" / "external-link").exists()
    assert "src/external-link" in prepared["copy"]["skippedSymlinks"]
    assert original_config.read_text(encoding="utf-8").startswith('{"endpoint"')

    injected = json.loads(run(
        "inject", "--run-dir", str(run_dir), "--kind", "invalid-json", "--path", "src/config.json"
    ).stdout)
    assert injected["kind"] == "invalid-json"
    assert (sandbox / "src" / "config.json").read_text(encoding="utf-8") == '{"carbon_failure":'
    assert original_config.read_text(encoding="utf-8").startswith('{"endpoint"')
    verified = json.loads(run("verify", "--run-dir", str(run_dir)).stdout)
    assert verified["status"] == "verified-unchanged"

    escaped = run(
        "inject", "--run-dir", str(run_dir), "--kind", "empty", "--path", "../../src/config.json", ok=False
    )
    assert "escapes the sandbox" in escaped.stderr

    reset = json.loads(run("reset", "--run-dir", str(run_dir)).stdout)
    assert reset["reset"] is True
    assert (sandbox / "src" / "config.json").read_bytes() == (baseline / "src" / "config.json").read_bytes()

    scenario = json.loads(run(
        "scenario", "--run-dir", str(run_dir), "--type", "http-503",
        "--target", "inventory API", "--method", "local loopback stub",
        "--expected", "Retry is bounded and the user sees a recoverable error."
    ).stdout)
    observation = json.loads(run(
        "observe", "--run-dir", str(run_dir), "--scenario-id", scenario["id"],
        "--status", "passed", "--actual", "Two retries occurred and recovery UI appeared.",
        "--evidence", ".carbon/failure/evidence/http-503.json"
    ).stdout)
    assert observation["status"] == "passed"
    completed = json.loads(run("complete", "--run-dir", str(run_dir)).stdout)
    assert completed["status"] == "completed"
    assert completed["originalIntegrity"]["status"] == "verified-unchanged"
    assert completed["injections"] == 1 and completed["scenarios"] == 1 and completed["observations"] == 1

    original_code.write_text("export const recover = () => false;\n", encoding="utf-8")
    drift = run("verify", "--run-dir", str(run_dir), ok=False)
    assert drift.returncode == 3
    drift_payload = json.loads(drift.stdout)
    assert drift_payload["status"] == "changed" and drift_payload["changedPaths"] == ["src/app.js"]

print("PASS: CARBON Failure preserves originals, skips symlinks and secrets, confines injections to the sandbox, resets cleanly, records observed scenarios, and detects original drift.")
