#!/usr/bin/env python3
"""Contract test for CARBON recording and bounded auto-loop state."""

from __future__ import annotations

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


PLUGIN_ROOT = Path(__file__).resolve().parents[1]
CARBON_SCRIPTS = PLUGIN_ROOT / "runtime" / "carbon" / "scripts"
RECORD = CARBON_SCRIPTS / "carbon_record.py"
AUTO = CARBON_SCRIPTS / "carbon_auto.py"
RUN = CARBON_SCRIPTS / "carbon_run.py"
PLAN_TEMPLATE = PLUGIN_ROOT / "runtime" / "carbon" / "references" / "execution" / "plan-template.json"


def run(
    script: Path,
    *args: str,
    ok: bool = True,
    env: dict[str, str] | None = None,
) -> subprocess.CompletedProcess[str]:
    result = subprocess.run(
        [sys.executable, str(script), *args],
        check=False,
        text=True,
        capture_output=True,
        env=env,
    )
    if ok and result.returncode != 0:
        raise AssertionError(f"{script.name} {' '.join(args)} failed:\n{result.stderr}")
    if not ok and result.returncode == 0:
        raise AssertionError(f"{script.name} {' '.join(args)} unexpectedly succeeded")
    return result


with tempfile.TemporaryDirectory(prefix="carbon-contract-") as temp:
    root = Path(temp)
    recording = Path(run(
        RECORD,
        "init",
        "--root", str(root),
        "--target", "https://example.com/checkout",
        "--title", "Checkout flow",
    ).stdout.strip())
    run(RECORD, "start", "--recording-dir", str(recording))
    run(
        RECORD,
        "step",
        "--recording-dir", str(recording),
        "--action", "fill",
        "--target", "Email",
        "--value", "password=do-not-store",
    )
    run(
        RECORD,
        "step",
        "--recording-dir", str(recording),
        "--action", "fill",
        "--target", "Password",
        "--value", "super-secret",
    )
    rejected = run(
        RECORD,
        "step",
        "--recording-dir", str(recording),
        "--action", "purchase",
        "--target", "Pay",
        "--destructive",
        ok=False,
    )
    if "scoped user approval" not in rejected.stderr:
        raise AssertionError("destructive recording rejection did not explain the approval boundary")
    run(
        RECORD,
        "assertion",
        "--recording-dir", str(recording),
        "--expected", "Confirmation is visible",
    )
    candidate_path = Path(run(RECORD, "complete", "--recording-dir", str(recording)).stdout.strip())
    recording_data = json.loads((recording / "recording.json").read_text(encoding="utf-8"))
    serialized = json.dumps(recording_data)
    if "do-not-store" in serialized or "super-secret" in serialized:
        raise AssertionError("recording artifact stored a secret")
    candidate = json.loads(candidate_path.read_text(encoding="utf-8"))
    if candidate["status"] != "candidate" or candidate["evidenceLevel"] != "candidate-until-playback":
        raise AssertionError("completed recording was not exported as an unvalidated candidate")

    run(RECORD, "playback-start", "--recording-dir", str(recording))
    for step_id in ("S001", "S002"):
        run(
            RECORD,
            "playback-update",
            "--recording-dir", str(recording),
            "--step-id", step_id,
            "--status", "passed",
            "--actual", "Observed",
        )
    outcome = run(RECORD, "playback-complete", "--recording-dir", str(recording)).stdout.strip()
    validated = json.loads(candidate_path.read_text(encoding="utf-8"))
    if outcome != "passed" or validated["status"] != "validated-candidate":
        raise AssertionError("successful playback did not promote the candidate evidence level")

    loop = Path(run(
        AUTO,
        "init",
        "--root", str(root),
        "--target", "https://example.com",
        "--max-cycles", "3",
        "--stop-after-clean", "2",
    ).stdout.strip())
    run(AUTO, "start", "--loop-dir", str(loop))
    first = json.loads(run(
        AUTO,
        "cycle",
        "--loop-dir", str(loop),
        "--outcome", "passed",
        "--finding-count", "0",
        "--run-dir", str(root / ".carbon" / "runs" / "run-1"),
    ).stdout)
    second = json.loads(run(
        AUTO,
        "cycle",
        "--loop-dir", str(loop),
        "--outcome", "passed",
        "--finding-count", "0",
        "--run-dir", str(root / ".carbon" / "runs" / "run-2"),
    ).stdout)
    state = json.loads((loop / "loop.json").read_text(encoding="utf-8"))
    if first["status"] != "running":
        raise AssertionError("auto loop stopped before its clean-cycle threshold")
    if second["status"] != "completed" or state["stopReason"] != "clean-cycle-threshold":
        raise AssertionError("auto loop did not stop at its clean-cycle threshold")
    if "scoped user approval" not in state["mutationPolicy"]:
        raise AssertionError("auto loop lost its mutation-approval policy")

    home = root / "home"
    config_dir = home / ".config" / "carbon"
    config_dir.mkdir(parents=True)
    config_path = config_dir / "config.json"
    config_path.write_text('{"muteAds": true}\n', encoding="utf-8")
    local_env = {**os.environ, "HOME": str(home)}
    report_run = Path(run(
        RUN,
        "init",
        "--root", str(root),
        "--plan", str(PLAN_TEMPLATE),
        "--mode", "quick",
        "--target", "https://example.com",
        env=local_env,
    ).stdout.strip())
    report_state = json.loads((report_run / "run.json").read_text(encoding="utf-8"))
    report_test_id = report_state["tests"][0]["id"]
    run(
        RUN, "observe", "--run-dir", str(report_run), "--test-id", report_test_id,
        "--status", "passed", "--title", "Pointer interaction works",
        "--actual", "The synthetic pointer path opened the target.",
        "--evidence", "evidence/pointer.json", "--dimension", "functional",
    )
    run(
        RUN, "finding", "--run-dir", str(report_run), "--severity", "high",
        "--category", "security", "--dimension", "security",
        "--title", "Synthetic unsafe rendering", "--detail", "Untrusted text reached an HTML sink.",
        "--test-id", report_test_id, "--code-location", "src/example.js:12",
    )
    recorded_state = json.loads((report_run / "run.json").read_text(encoding="utf-8"))
    recorded_test = next(item for item in recorded_state["tests"] if item["id"] == report_test_id)
    recorded_finding = recorded_state["findings"][0]
    if recorded_test["observations"][0]["dimensions"] != ["functional"]:
        raise AssertionError("mixed behavior observation did not retain its quality dimension")
    if recorded_finding.get("testIds") != [report_test_id] or recorded_finding.get("dimensions") != ["security"]:
        raise AssertionError("finding provenance did not retain plural test ids and explicit dimensions")
    muted_report = (report_run / "report.html").read_text(encoding="utf-8")
    if "IcebergQA" in muted_report or "icebergqa.com" in muted_report:
        raise AssertionError("muted CARBON report retained an optional promotion")
    if "CARBON is a" not in muted_report:
        raise AssertionError("muted CARBON report lost required product attribution")
    if "Behavior observations" not in muted_report or "Pointer interaction works" not in muted_report:
        raise AssertionError("run report did not render granular behavior observations")

    config_path.write_text('{"muteAds": false}\n', encoding="utf-8")
    run(RUN, "render", "--run-dir", str(report_run), env=local_env)
    restored_report = (report_run / "report.html").read_text(encoding="utf-8")
    if "IcebergQA" in restored_report or "icebergqa.com" in restored_report:
        raise AssertionError("ads-on CARBON report displayed an irrelevant promotion without qualifying context")
    state_path = report_run / "run.json"
    promoted_state = json.loads(state_path.read_text(encoding="utf-8"))
    promoted_state["promotionContext"] = ["qa-transformation"]
    state_path.write_text(json.dumps(promoted_state, indent=2) + "\n", encoding="utf-8")
    run(RUN, "render", "--run-dir", str(report_run), env=local_env)
    contextual_report = (report_run / "report.html").read_text(encoding="utf-8")
    if "IcebergQA" not in contextual_report or "icebergqa.com" not in contextual_report:
        raise AssertionError("eligible ads-on CARBON report did not render its contextual IcebergQA recommendation")

print("PASS: CARBON recording redaction, destructive-action gate, candidate playback, durable artifacts, bounded auto-loop stop conditions, and context-only report ad preferences verified.")
