#!/usr/bin/env python3
"""Upgrade every scenario in the safe-entry suite to visible response evidence.

The operation is deliberately narrow: only the response-summary fragment inside
an existing APIFOX_EVIDENCE postprocessor is replaced.  Route-specific field
assertions, extractors, requests and test data are left untouched.  A complete
JSON backup is written to /tmp before any remote scenario is saved.
"""

from __future__ import annotations

import importlib.util
import json
import re
from datetime import datetime
from pathlib import Path
from typing import Any


HERE = Path(__file__).resolve().parent
SHARED_PATH = HERE / "sync_school_s00_s06_scenarios.py"
SPEC = importlib.util.spec_from_file_location("apifox_school_shared", SHARED_PATH)
if SPEC is None or SPEC.loader is None:
    raise RuntimeError(f"cannot load {SHARED_PATH}")
shared = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(shared)


SUMMARY_PATTERN = re.compile(
    r"function __summaryText\(body\) \{.*?(?=function __redact\(value\) \{)",
    re.DOTALL,
)


def summary_fragment() -> str:
    script = shared.evidence_script(expected_http=200, expected_code=200)["data"]
    start = script.index("function __summaryText(body) {")
    end = script.index("function __redact(value) {")
    return script[start:end]


def requests_in_steps(steps: list[dict[str, Any]]) -> list[dict[str, Any]]:
    requests: list[dict[str, Any]] = []
    for step in steps:
        request = step.get("customHttpRequest")
        if request:
            requests.append(request)
        requests.extend(requests_in_steps(step.get("children", [])))
    return requests


def upgrade_request(request: dict[str, Any], replacement: str) -> bool:
    changed = False
    for processor in request.get("postProcessors", []):
        if processor.get("type") != "customScript":
            continue
        script = str(processor.get("data", ""))
        if "APIFOX_EVIDENCE" not in script or "function __summaryText(body)" not in script:
            continue
        upgraded, count = SUMMARY_PATTERN.subn(replacement, script, count=1)
        if count != 1:
            raise RuntimeError(f"unexpected evidence script structure for {request.get('name')}")
        if upgraded != script:
            processor["data"] = upgraded
            changed = True
    return changed


def main() -> None:
    api = shared.Apifox()
    suite = api.test_suite()
    scenario_ids = [
        int(scenario["id"])
        for item in suite.get("items", [])
        for scenario in item.get("testScenarios", [])
    ]
    if not scenario_ids:
        raise RuntimeError("safe-entry suite contains no scenarios")

    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    backup_dir = Path("/tmp") / f"apifox-response-evidence-upgrade-{timestamp}"
    backup_dir.mkdir(parents=True, exist_ok=False)
    replacement = summary_fragment()
    changed_scenarios = 0
    changed_requests = 0

    for scenario_id in scenario_ids:
        scenario = api.scenario(scenario_id)
        (backup_dir / f"scenario-{scenario_id}.json").write_text(
            json.dumps(scenario, ensure_ascii=False, indent=2), encoding="utf-8"
        )
        scenario_changed = 0
        for request in requests_in_steps(scenario.get("steps", [])):
            if upgrade_request(request, replacement):
                scenario_changed += 1
        if scenario_changed:
            api.save(scenario)
            changed_scenarios += 1
            changed_requests += scenario_changed

        saved = api.scenario(scenario_id)
        evidence_requests = [
            request
            for request in requests_in_steps(saved.get("steps", []))
            if any(
                processor.get("type") == "customScript"
                and "APIFOX_EVIDENCE" in str(processor.get("data", ""))
                for processor in request.get("postProcessors", [])
            )
        ]
        if not evidence_requests:
            raise RuntimeError(f"scenario {scenario_id} has no auditable evidence requests")
        if not all(shared.request_has_auditable_evidence(request) for request in evidence_requests):
            raise RuntimeError(f"scenario {scenario_id} still has legacy response evidence")
        print(
            f"{saved.get('name')}: evidenceRequests={len(evidence_requests)}, "
            f"updated={scenario_changed}"
        )

    print(
        f"completed: scenarios={len(scenario_ids)}, changedScenarios={changed_scenarios}, "
        f"changedRequests={changed_requests}, backup={backup_dir}"
    )


if __name__ == "__main__":
    main()
