from __future__ import annotations

import json
import subprocess
import sys
from pathlib import Path


ROOT = Path(__file__).resolve().parents[3]
SCORECARD_SCRIPT = (
    ROOT / "scripts" / "reboot-recovery" / "post-reboot-credential-escrow-intake-scorecard.py"
)
TEMPLATE_SCRIPT = ROOT / "scripts" / "reboot-recovery" / "post-reboot-owner-response-template.py"

ESCROW_ITEMS = [
    "restic_repository_password",
    "offsite_provider_credentials",
    "break_glass_admin_credentials",
    "dns_registrar_recovery",
    "oauth_ai_provider_recovery",
]


def write_text(path: Path, text: str) -> Path:
    path.write_text(text, encoding="utf-8")
    return path


def write_packet(tmp_path: Path, gate_ids: list[str]) -> Path:
    owner_packets = []
    for gate_id in gate_ids:
        owner_packets.append(
            {
                "packet_id": gate_id,
                "title": f"{gate_id} owner evidence",
                "priority": "P0",
                "required_items": ESCROW_ITEMS if gate_id == "credential_escrow_evidence" else [],
            }
        )
    packet_path = tmp_path / "owner-packets.json"
    packet_path.write_text(
        json.dumps(
            {
                "schema_version": "awoooi_post_reboot_next_gate_owner_packets_v1",
                "source": {"next_required_gates": gate_ids},
                "owner_packets": owner_packets,
            },
            indent=2,
        )
        + "\n",
        encoding="utf-8",
    )
    return packet_path


def generate_template(packet_path: Path, tmp_path: Path) -> Path:
    response_path = tmp_path / "owner-response-template.json"
    subprocess.run(
        [
            sys.executable,
            str(TEMPLATE_SCRIPT),
            "--owner-packet-file",
            str(packet_path),
            "--output",
            str(response_path),
        ],
        text=True,
        capture_output=True,
        check=True,
    )
    return response_path


def generate_valid_redacted_response(packet_path: Path, tmp_path: Path) -> Path:
    response_path = generate_template(packet_path, tmp_path)
    response = json.loads(response_path.read_text(encoding="utf-8"))
    item = response["responses"][0]
    item.update(
        {
            "owner_role": "backup_dr_owner",
            "owner_team": "platform_security",
            "decision": "accepted",
            "decision_reason": "redacted_non_secret_evidence_refs_supplied",
            "redacted_evidence_refs": ["escrow-review-ticket-20260629-001"],
            "followup_owner": "backup_dr_owner",
        }
    )
    for index, escrow_item in enumerate(item["escrow_items"], start=1):
        escrow_item.update(
            {
                "non_secret_evidence_ref": f"escrow-evidence-ref-20260629-{index:03d}",
                "recovery_owner": "backup_dr_owner",
                "reviewer": "security_reviewer",
                "last_reviewed_at": "2026-06-29",
                "contains_secret_value": False,
            }
        )
    response_path.write_text(json.dumps(response, indent=2) + "\n", encoding="utf-8")
    return response_path


def run_scorecard(
    summary_path: Path,
    packet_path: Path,
    response_path: Path,
    offsite_path: Path,
    escrow_status_path: Path,
) -> dict:
    result = subprocess.run(
        [
            sys.executable,
            str(SCORECARD_SCRIPT),
            "--summary-file",
            str(summary_path),
            "--owner-packet-file",
            str(packet_path),
            "--response-file",
            str(response_path),
            "--offsite-report-file",
            str(offsite_path),
            "--escrow-status-file",
            str(escrow_status_path),
            "--json",
        ],
        text=True,
        capture_output=True,
        check=True,
    )
    return json.loads(result.stdout)


def write_common_artifacts(tmp_path: Path) -> tuple[Path, Path, Path]:
    summary_path = write_text(
        tmp_path / "summary.txt",
        """\
SERVICE_GREEN=1
NEXT_REQUIRED_GATES=credential_escrow_evidence
ESCROW_MISSING_COUNT=5
RUNTIME_ACTION_AUTHORIZED=0
""",
    )
    offsite_path = write_text(
        tmp_path / "offsite-report.txt",
        """\
SCRIPT_MISSING_COUNT=0
OFFSITE_CONFIGURED=1
RCLONE_CONFIGURED=1
MISSING_ESCROW_MARKER_COUNT=5
ESCROW_MISSING_COUNT=5
""",
    )
    escrow_status_path = write_text(
        tmp_path / "escrow-status.txt",
        "\n".join(f"{item} missing" for item in ESCROW_ITEMS) + "\n",
    )
    return summary_path, offsite_path, escrow_status_path


def test_scorecard_blocks_until_non_secret_evidence_refs_exist(tmp_path: Path) -> None:
    summary_path, offsite_path, escrow_status_path = write_common_artifacts(tmp_path)
    packet_path = write_packet(tmp_path, ["credential_escrow_evidence"])
    response_path = generate_template(packet_path, tmp_path)

    scorecard = run_scorecard(
        summary_path,
        packet_path,
        response_path,
        offsite_path,
        escrow_status_path,
    )

    assert scorecard["status"] == "blocked_waiting_non_secret_credential_escrow_evidence"
    assert scorecard["active_gate_present"] is True
    assert scorecard["owner_packet_gates"] == ["credential_escrow_evidence"]
    assert scorecard["response_gates"] == ["credential_escrow_evidence"]
    assert scorecard["unexpected_response_gate_count"] == 0
    assert scorecard["required_item_count"] == 5
    assert scorecard["effective_escrow_missing_count"] == 5
    assert scorecard["script_missing_count"] == 0
    assert scorecard["offsite_configured"] == 1
    assert scorecard["rclone_configured"] == 1
    assert scorecard["preflight_status"] == "blocked_waiting_owner_response_content"
    assert scorecard["owner_response_received_count"] == 0
    assert scorecard["owner_response_accepted_count"] == 0
    assert scorecard["runtime_gate_count"] == 0
    assert scorecard["runtime_action_authorized"] == 0
    assert scorecard["host_write_authorized"] == 0
    assert scorecard["secret_value_collection_allowed"] == 0
    assert scorecard["credential_marker_write_requested_count"] == 0
    assert scorecard["credential_marker_write_authorized_count"] == 0


def test_scorecard_blocks_forbidden_runtime_or_marker_requests(tmp_path: Path) -> None:
    summary_path, offsite_path, escrow_status_path = write_common_artifacts(tmp_path)
    packet_path = write_packet(tmp_path, ["credential_escrow_evidence"])
    response_path = generate_template(packet_path, tmp_path)
    response = json.loads(response_path.read_text(encoding="utf-8"))
    response["responses"][0]["runtime_action_requested"] = True
    response["responses"][0]["credential_marker_write_authorized"] = True
    response_path.write_text(json.dumps(response, indent=2) + "\n", encoding="utf-8")

    scorecard = run_scorecard(
        summary_path,
        packet_path,
        response_path,
        offsite_path,
        escrow_status_path,
    )

    assert scorecard["status"] == "blocked_forbidden_runtime_or_marker_request"
    assert scorecard["runtime_action_requested_count"] == 1
    assert scorecard["credential_marker_write_authorized_count"] == 1
    assert scorecard["forbidden_true_field_count"] == 2
    assert scorecard["owner_response_accepted_count"] == 0
    assert scorecard["runtime_gate_count"] == 0


def test_valid_redacted_response_reaches_independent_reviewer_even_before_markers(tmp_path: Path) -> None:
    summary_path, offsite_path, escrow_status_path = write_common_artifacts(tmp_path)
    packet_path = write_packet(tmp_path, ["credential_escrow_evidence"])
    response_path = generate_valid_redacted_response(packet_path, tmp_path)

    scorecard = run_scorecard(
        summary_path,
        packet_path,
        response_path,
        offsite_path,
        escrow_status_path,
    )

    assert scorecard["status"] == "ready_for_independent_reviewer_acceptance"
    assert scorecard["next_step"] == "independent_reviewer_acceptance_then_marker_dry_run"
    assert scorecard["preflight_status"] == "ready_for_independent_reviewer_acceptance"
    assert scorecard["effective_escrow_missing_count"] == 5
    assert scorecard["owner_response_received_count"] == 1
    assert scorecard["owner_response_accepted_count"] == 1
    assert scorecard["runtime_gate_count"] == 0
    assert scorecard["runtime_action_authorized"] == 0
    assert scorecard["host_write_authorized"] == 0
    assert scorecard["secret_value_collection_allowed"] == 0
    assert scorecard["credential_marker_write_requested_count"] == 0
    assert scorecard["credential_marker_write_authorized_count"] == 0
    assert scorecard["forbidden_true_field_count"] == 0


def test_scorecard_rejects_stale_extra_wazuh_response_gate(tmp_path: Path) -> None:
    summary_path, offsite_path, escrow_status_path = write_common_artifacts(tmp_path)
    packet_path = write_packet(tmp_path, ["credential_escrow_evidence"])
    response_path = generate_template(packet_path, tmp_path)
    response = json.loads(response_path.read_text(encoding="utf-8"))
    response["responses"].append(
        {
            "gate_id": "wazuh_manager_registry_export",
            "owner_role": "owner_role_here",
            "owner_team": "owner_team_here",
            "decision": "pending",
            "decision_reason": "decision_reason_here",
            "affected_scope": "stale Wazuh response should not be active",
            "redacted_evidence_refs": ["redacted_evidence_ref_here"],
            "followup_owner": "followup_owner_here",
            "runtime_action_requested": False,
            "host_write_requested": False,
            "secret_value_included": False,
            "secret_value_collection_allowed": False,
        }
    )
    response_path.write_text(json.dumps(response, indent=2) + "\n", encoding="utf-8")

    scorecard = run_scorecard(
        summary_path,
        packet_path,
        response_path,
        offsite_path,
        escrow_status_path,
    )

    assert scorecard["status"] == "blocked_owner_packet_or_response_gate_mismatch"
    assert scorecard["response_gates"] == [
        "credential_escrow_evidence",
        "wazuh_manager_registry_export",
    ]
    assert scorecard["unexpected_response_gate_count"] == 1
    assert scorecard["unexpected_response_gates"] == ["wazuh_manager_registry_export"]
    assert scorecard["owner_response_received_count"] == 0
    assert scorecard["runtime_gate_count"] == 0
