from __future__ import annotations

import json
import subprocess
import sys
from pathlib import Path


ROOT = Path(__file__).resolve().parents[3]
SCRIPT = ROOT / "scripts" / "reboot-recovery" / "reboot-auto-recovery-slo-scorecard.py"


GREEN_SUMMARY = """\
AWOOOI_POST_REBOOT_READINESS_SUMMARY=1
POST_START_RESULT=FULL_STACK_GREEN
POST_START_BLOCKED=0
SERVICE_GREEN=1
PRODUCT_DATA_GREEN=1
BACKUP_CORE_GREEN=1
HOST_188_SERVICE_GREEN=1
WAZUH_DASHBOARD_DEGRADED=0
OVERALL_DECLARATION=FULL_STACK_GREEN
NEXT_REQUIRED_GATES=none
"""


HOST_PROBE_GREEN = """\
AWOOOI_REBOOT_AUTO_RECOVERY_HOST_PROBE=1
TARGET_HOSTS=99,110,111,112,120,121,188
HOST_BOOT alias=99 target=192.168.0.99 startup_unit=vmware-host-autostart reachable=1 boot_id=aa uptime_seconds=100 systemd_state=windows_exporter startup_enabled=enabled startup_active=active
HOST_BOOT alias=110 target=wooo@192.168.0.110 startup_unit=awoooi-startup-110.service reachable=1 boot_id=a uptime_seconds=120 systemd_state=running startup_enabled=enabled startup_active=active
HOST_BOOT alias=111 target=192.168.0.111 startup_unit=vm-host-boot reachable=1 boot_id=bb uptime_seconds=125 systemd_state=node_exporter startup_enabled=unknown startup_active=unknown
HOST_BOOT alias=112 target=192.168.0.112 startup_unit=vm-host-boot reachable=1 boot_id=cc uptime_seconds=128 systemd_state=node_exporter startup_enabled=unknown startup_active=unknown
HOST_BOOT alias=120 target=wooo@192.168.0.120 startup_unit=k3s.service reachable=1 boot_id=b uptime_seconds=130 systemd_state=running startup_enabled=enabled startup_active=active
HOST_BOOT alias=121 target=wooo@192.168.0.121 startup_unit=k3s.service reachable=1 boot_id=c uptime_seconds=140 systemd_state=running startup_enabled=enabled startup_active=active
HOST_BOOT alias=188 target=ollama@192.168.0.188 startup_unit=awoooi-startup.service reachable=1 boot_id=d uptime_seconds=150 systemd_state=running startup_enabled=enabled startup_active=active
"""


REBOOT_EVENT_GREEN = {
    "schema_version": "awoooi_reboot_event_detector_v1",
    "generated_at": "2026-06-29T14:30:00+08:00",
    "target_minutes": 10,
    "target_seconds": 600,
    "required_hosts": ["99", "110", "111", "112", "120", "121", "188"],
    "observed_hosts": ["99", "110", "111", "112", "120", "121", "188"],
    "missing_hosts": [],
    "unreachable_hosts": [],
    "reboot_detected": True,
    "rebooted_hosts": ["99", "110", "111", "112", "120", "121", "188"],
    "fresh_boot_hosts": ["99", "110", "111", "112", "120", "121", "188"],
    "changed_boot_id_hosts": [],
    "all_required_hosts_observed": True,
    "all_required_hosts_in_reboot_window": True,
    "max_observed_uptime_seconds": 150,
    "target_seconds_remaining": 450,
    "recovery_deadline_status": "within_target_window",
    "state_file": "/tmp/reboot-event-state.json",
    "state_written": True,
}


WINDOWS99_VMWARE_GREEN = """\
AWOOOI_WINDOWS99_VMWARE_AUTOSTART=1
MODE=Verify
VMRUN_PRESENT=1
HOST111_VMWARE_TARGET=explicit_or_required
VMX alias=111 path=D:\\VMs\\111.vmx present=1
VMX alias=188 path=D:\\VMs\\188.vmx present=1
VMX alias=120 path=D:\\VMs\\120.vmx present=1
VMX alias=121 path=D:\\VMs\\121.vmx present=1
VMX alias=112 path=D:\\VMs\\112.vmx present=1
VMWARE_SERVICE name=VMAuthdService present=1 status=Running startup_type=Auto ok=1
VMWARE_SERVICE name=VMnetDHCP present=1 status=Running startup_type=Auto ok=1
VMWARE_AUTOSTART_TASK name=AWOOOI-Start-VMware-VMs present=1 enabled=1 state=Ready trigger_count=1 action_count=1 ok=1
WINDOWS_UPDATE_POLICY name=NoAutoRebootWithLoggedOnUsers value=1 expected=1 ok=1
WINDOWS_UPDATE_POLICY name=AlwaysAutoRebootAtScheduledTime value=0 expected=0 ok=1
WINDOWS_UPDATE_POLICY name=AUOptions value=3 expected=3 ok=1
WINDOWS_UPDATE_POLICY name=ScheduledInstallDay value=0 expected=0 ok=1
WINDOWS_UPDATE_POLICY name=ScheduledInstallTime value=3 expected=3 ok=1
WINDOWS_UPDATE_POLICY name=AUPowerManagement value=0 expected=0 ok=1
VM_POWER alias=111 vmx_present=1 running=1
VM_POWER alias=188 vmx_present=1 running=1
VM_POWER alias=120 vmx_present=1 running=1
VM_POWER alias=121 vmx_present=1 running=1
VM_POWER alias=112 vmx_present=1 running=1
VMWARE_AUTOSTART_CONFIG_READY=1
VMWARE_AUTOSTART_POWER_READY=1
WINDOWS_UPDATE_NO_AUTO_REBOOT_READY=1
VMWARE_AUTOSTART_VERIFY_READY=1
"""


PUBLIC_MAINTENANCE_GREEN = {
    "schema_version": "awoooi_public_maintenance_fallback_probe_v1",
    "generated_at": "2026-06-29T14:30:00+08:00",
    "runtime_readback_present": True,
    "ready": True,
    "target_url_count": 2,
    "route_count": 2,
    "raw_5xx_without_fallback_count": 0,
    "route_unreachable_without_external_fallback_count": 0,
    "maintenance_fallback_route_count": 0,
    "raw_5xx_without_fallback_urls": [],
    "route_unreachable_without_external_fallback_urls": [],
    "maintenance_fallback_urls": [],
    "blockers": [],
    "routes": [
        {
            "url": "https://awoooi.wooo.work/api/v1/health",
            "http_status": 200,
            "status": "normal_origin_serving",
            "fallback_header": "",
            "maintenance_body_detected": False,
            "maintenance_fallback_serving": False,
            "raw_5xx_without_fallback": False,
            "route_unreachable_without_external_fallback": False,
        },
        {
            "url": "https://awoooi.wooo.work/",
            "http_status": 200,
            "status": "normal_origin_serving",
            "fallback_header": "",
            "maintenance_body_detected": False,
            "maintenance_fallback_serving": False,
            "raw_5xx_without_fallback": False,
            "route_unreachable_without_external_fallback": False,
        },
    ],
}


WINDOWS99_MANAGEMENT_BLOCKED = {
    "schema_version": "windows99_management_channel_readback_v1",
    "generated_at": "2026-07-02T15:20:00+08:00",
    "host": "192.168.0.99",
    "host_reachable": True,
    "tcp_ports": {
        "22": "open",
        "135": "open",
        "445": "open",
        "2179": "open",
        "3389": "open",
        "5985": "timeout",
        "5986": "timeout",
    },
    "ssh_user": "administrator",
    "ssh_batch": {
        "checked": True,
        "ready": False,
        "status": "permission_denied",
    },
    "winrm_http_open": False,
    "winrm_https_open": False,
    "hyperv_vmconnect_open": True,
    "rdp_console_reachable": True,
    "local_console_channel_reachable": True,
    "console_collection_channels": ["rdp_console", "hyperv_vmconnect"],
    "remote_execution_channel_ready": False,
    "can_collect_vmware_verify_without_secret": False,
    "blockers": [
        "windows99_remote_execution_channel_unavailable",
        "windows99_winrm_unavailable",
        "windows99_ssh_batch_denied",
    ],
    "forbidden_actions": [
        "read_windows_password",
        "read_secret_value",
        "start_vm",
        "reboot_host",
    ],
}


def run_scorecard(
    tmp_path: Path,
    summary: str,
    probe: str = HOST_PROBE_GREEN,
    windows99: str = WINDOWS99_VMWARE_GREEN,
    public_maintenance: dict | None = PUBLIC_MAINTENANCE_GREEN,
    windows99_management: str | None = None,
) -> dict:
    summary_path = tmp_path / "summary.txt"
    probe_path = tmp_path / "probe.txt"
    reboot_event_path = tmp_path / "reboot-event.json"
    windows99_path = tmp_path / "windows99-vmware.txt"
    public_maintenance_path = tmp_path / "public-maintenance.json"
    summary_path.write_text(summary, encoding="utf-8")
    probe_path.write_text(probe, encoding="utf-8")
    reboot_event_path.write_text(json.dumps(REBOOT_EVENT_GREEN), encoding="utf-8")
    windows99_path.write_text(windows99, encoding="utf-8")
    windows99_management_path = tmp_path / "windows99-management.json"
    args = [
        sys.executable,
        str(SCRIPT),
        "--summary-file",
        str(summary_path),
        "--host-probe-file",
        str(probe_path),
        "--reboot-event-file",
        str(reboot_event_path),
        "--windows99-vmware-file",
        str(windows99_path),
        "--generated-at",
        "2026-06-29T14:30:00+08:00",
    ]
    if public_maintenance is not None:
        public_maintenance_path.write_text(
            json.dumps(public_maintenance),
            encoding="utf-8",
        )
        args.extend(["--public-maintenance-file", str(public_maintenance_path)])
    if windows99_management is not None:
        windows99_management_path.write_text(windows99_management, encoding="utf-8")
        args.extend(["--windows99-management-file", str(windows99_management_path)])
    result = subprocess.run(
        args,
        text=True,
        capture_output=True,
        check=True,
    )
    return json.loads(result.stdout)


def run_scorecard_with_host_pressure(
    tmp_path: Path,
    summary: str,
    host_pressure: dict,
) -> dict:
    summary_path = tmp_path / "summary.txt"
    probe_path = tmp_path / "probe.txt"
    reboot_event_path = tmp_path / "reboot-event.json"
    host_pressure_path = tmp_path / "host-pressure.json"
    windows99_path = tmp_path / "windows99-vmware.txt"
    public_maintenance_path = tmp_path / "public-maintenance.json"
    summary_path.write_text(summary, encoding="utf-8")
    probe_path.write_text(HOST_PROBE_GREEN, encoding="utf-8")
    reboot_event_path.write_text(json.dumps(REBOOT_EVENT_GREEN), encoding="utf-8")
    host_pressure_path.write_text(json.dumps(host_pressure), encoding="utf-8")
    windows99_path.write_text(WINDOWS99_VMWARE_GREEN, encoding="utf-8")
    public_maintenance_path.write_text(
        json.dumps(PUBLIC_MAINTENANCE_GREEN),
        encoding="utf-8",
    )
    result = subprocess.run(
        [
            sys.executable,
            str(SCRIPT),
            "--summary-file",
            str(summary_path),
            "--host-probe-file",
            str(probe_path),
            "--reboot-event-file",
            str(reboot_event_path),
            "--host-pressure-file",
            str(host_pressure_path),
            "--public-maintenance-file",
            str(public_maintenance_path),
            "--windows99-vmware-file",
            str(windows99_path),
            "--generated-at",
            "2026-07-01T07:45:00+08:00",
        ],
        text=True,
        capture_output=True,
        check=True,
    )
    return json.loads(result.stdout)


def run_scorecard_with_stock(
    tmp_path: Path,
    summary: str,
    freshness: dict,
    ingestion: dict,
    generated_at: str = "2026-06-29T19:41:00+08:00",
) -> dict:
    summary_path = tmp_path / "summary.txt"
    probe_path = tmp_path / "probe.txt"
    reboot_event_path = tmp_path / "reboot-event.json"
    freshness_path = tmp_path / "freshness.json"
    ingestion_path = tmp_path / "ingestion.json"
    windows99_path = tmp_path / "windows99-vmware.txt"
    public_maintenance_path = tmp_path / "public-maintenance.json"
    summary_path.write_text(summary, encoding="utf-8")
    probe_path.write_text(HOST_PROBE_GREEN, encoding="utf-8")
    reboot_event_path.write_text(json.dumps(REBOOT_EVENT_GREEN), encoding="utf-8")
    freshness_path.write_text(json.dumps(freshness), encoding="utf-8")
    ingestion_path.write_text(json.dumps(ingestion), encoding="utf-8")
    windows99_path.write_text(WINDOWS99_VMWARE_GREEN, encoding="utf-8")
    public_maintenance_path.write_text(
        json.dumps(PUBLIC_MAINTENANCE_GREEN),
        encoding="utf-8",
    )
    result = subprocess.run(
        [
            sys.executable,
            str(SCRIPT),
            "--summary-file",
            str(summary_path),
            "--host-probe-file",
            str(probe_path),
            "--reboot-event-file",
            str(reboot_event_path),
            "--stock-freshness-file",
            str(freshness_path),
            "--stock-ingestion-file",
            str(ingestion_path),
            "--public-maintenance-file",
            str(public_maintenance_path),
            "--windows99-vmware-file",
            str(windows99_path),
            "--generated-at",
            generated_at,
        ],
        text=True,
        capture_output=True,
        check=True,
    )
    return json.loads(result.stdout)


def test_green_summary_and_recent_all_host_probe_can_claim_slo(tmp_path: Path) -> None:
    payload = run_scorecard(tmp_path, GREEN_SUMMARY)

    assert payload["schema_version"] == "awoooi_reboot_auto_recovery_slo_scorecard_v1"
    assert payload["status"] == "slo_ready"
    assert payload["can_claim_all_services_recovered_within_target"] is True
    assert payload["current_phase"] == "slo_ready"
    assert payload["eta_or_wait_reason"] == "recovered_within_10_minute_slo"
    assert payload["primary_blocker"] == ""
    assert payload["next_safe_action"] == payload["safe_next_step"]
    assert payload["reboot_sop_progress"]["current_phase"] == "slo_ready"
    assert payload["reboot_sop_progress"]["active_blocker_count"] == 0
    assert payload["readback"]["workplan_id"] == "P0-006"
    assert payload["readback"]["current_phase"] == "slo_ready"
    assert payload["readback"]["eta_or_wait_reason"] == "recovered_within_10_minute_slo"
    assert payload["readback"]["next_safe_action"] == payload["safe_next_step"]
    assert payload["readback"]["readiness_percent"] == 100
    assert payload["readback"]["active_blocker_count"] == 0
    assert payload["readback"]["runtime_write_authorized_by_this_scorecard"] is False
    assert payload["readback"]["windows99_vmware_verify_ready"] is True
    assert payload["readback"]["windows99_update_no_auto_reboot_ready"] is True
    assert payload["readback"]["windows99_verify_collection_status"] == (
        "ready_windows99_vmware_verify_readback_green"
    )
    assert (
        payload["readback"]["windows99_verify_collection_can_collect_no_secret"]
        is False
    )
    assert payload["required_checks"]["windows99_management_channel_ready"] is True
    assert payload["required_checks"]["public_maintenance_fallback_runtime_ready"] is True
    assert payload["rollups"]["source_controls_present"] is True
    assert payload["rollups"]["public_maintenance_runtime_readback_present"] is True
    assert payload["rollups"]["public_maintenance_fallback_ready"] is True
    assert payload["rollups"]["public_route_raw_5xx_without_fallback_count"] == 0
    assert payload["rollups"]["windows99_vmware_verify_ready"] is True
    assert payload["rollups"]["windows99_update_no_auto_reboot_ready"] is True
    assert payload["rollups"]["windows99_verify_collection_status"] == (
        "ready_windows99_vmware_verify_readback_green"
    )
    assert (
        payload["rollups"]["windows99_verify_collection_can_collect_no_secret"]
        is False
    )
    assert payload["rollups"]["windows99_verify_collection_blocker_count"] == 0
    assert payload["rollups"]["windows99_host99_reachable"] is True
    assert payload["rollups"]["windows99_host99_uptime_known"] is True
    assert payload["rollups"]["readiness_percent"] == 100
    assert payload["summary"]["reboot_auto_recovery_workplan_id"] == "P0-006"
    assert payload["summary"]["reboot_auto_recovery_current_phase"] == "slo_ready"
    assert (
        payload["summary"]["reboot_auto_recovery_eta_or_wait_reason"]
        == "recovered_within_10_minute_slo"
    )
    assert payload["summary"]["reboot_auto_recovery_can_claim_slo"] is True
    assert payload["summary"]["runtime_write_authorized"] is False
    assert payload["source_controls"][
        "host_110_startup_controlled_drain_guarded_autostart_source_present"
    ] is True
    assert payload["host_boot_detection"]["max_observed_uptime_seconds"] == 150
    assert payload["windows99_vmware_autostart"]["required_vm_aliases"] == [
        "111",
        "112",
        "120",
        "121",
        "188",
    ]
    assert payload["windows99_vmware_autostart"]["verify_ready"] is True
    assert payload["windows99_verify_collection"]["collection_blockers"] == []
    assert payload["active_blocker_action_matrix"]["item_count"] == 0
    assert payload["active_blocker_action_matrix"]["items"] == []
    assert (
        payload["windows99_verify_collection"]["post_verifier"]
        == "rerun_reboot_auto_recovery_slo_scorecard_with_windows99_vmware_file_no_secret_no_reboot"
    )
    assert payload["active_blockers"] == []
    assert payload["source_controls"][
        "conversation_event_hot_path_index_migration_source_present"
    ] is True


def test_missing_public_maintenance_runtime_readback_fails_closed(tmp_path: Path) -> None:
    payload = run_scorecard(
        tmp_path,
        GREEN_SUMMARY,
        public_maintenance=None,
    )

    assert payload["status"] == "blocked_reboot_auto_recovery_slo_not_ready"
    assert payload["can_claim_all_services_recovered_within_target"] is False
    assert "public_maintenance_fallback_runtime_readback_missing" in payload[
        "active_blockers"
    ]
    assert (
        payload["required_checks"]["public_maintenance_fallback_runtime_ready"]
        is False
    )
    assert payload["current_phase"] == "public_maintenance_fallback_blocked"


def test_missing_probe_fails_closed(tmp_path: Path) -> None:
    payload = run_scorecard(tmp_path, GREEN_SUMMARY, probe="")

    assert payload["status"] == "blocked_reboot_auto_recovery_slo_not_ready"
    assert payload["can_claim_all_services_recovered_within_target"] is False
    assert "all_host_reboot_detection_missing" in payload["active_blockers"]
    assert "host_boot_probe_missing_hosts" in payload["active_blockers"]
    assert payload["readback"]["active_blocker_count"] == len(
        payload["active_blockers"]
    )
    assert payload["rollups"]["readiness_percent"] < 100
    assert payload["summary"]["reboot_auto_recovery_can_claim_slo"] is False


def test_missing_windows99_vmware_readback_fails_closed(tmp_path: Path) -> None:
    payload = run_scorecard(tmp_path, GREEN_SUMMARY, windows99="")

    assert payload["status"] == "blocked_reboot_auto_recovery_slo_not_ready"
    assert payload["can_claim_all_services_recovered_within_target"] is False
    assert "windows99_vmware_autostart_readback_missing" in payload["active_blockers"]
    assert payload["windows99_vmware_autostart"]["readback_present"] is False
    assert payload["readback"]["windows99_vmware_verify_ready"] is False
    assert payload["readback"]["windows99_update_no_auto_reboot_ready"] is False
    assert payload["readback"]["windows99_verify_collection_status"] == (
        "blocked_windows99_verify_output_missing_host_reachable"
    )
    assert (
        payload["readback"]["windows99_verify_collection_can_collect_no_secret"]
        is True
    )
    assert payload["windows99_verify_collection"]["collection_blockers"] == [
        "windows99_vmware_autostart_readback_missing"
    ]
    assert "VMRUN_PRESENT" in payload["windows99_verify_collection"][
        "expected_no_secret_output_fields"
    ]
    assert "-Mode Verify" in payload["windows99_verify_collection"][
        "no_secret_verify_command"
    ]
    assert payload["windows99_verify_collection"][
        "no_secret_collector_check_command"
    ] == "bash scripts/reboot-recovery/collect-windows99-vmware-verify.sh --check"
    assert payload["windows99_verify_collection"][
        "no_secret_collector_collect_command"
    ] == "bash scripts/reboot-recovery/collect-windows99-vmware-verify.sh --collect"
    assert "no_secret_ssh_batchmode_verify_collector" in payload[
        "windows99_verify_collection"
    ]["safe_collection_channels"]
    assert "windows_password_or_secret_collection" in payload[
        "windows99_verify_collection"
    ]["forbidden_actions"]
    assert payload["safe_next_step"] == (
        "collect_windows99_vmware_autostart_verify_readback_then_rerun_all_host_"
        "reboot_scorecard_no_secret_no_reboot"
    )


def test_windows99_management_channel_unavailable_is_visible(tmp_path: Path) -> None:
    payload = run_scorecard(
        tmp_path,
        GREEN_SUMMARY,
        windows99="",
        windows99_management=json.dumps(WINDOWS99_MANAGEMENT_BLOCKED),
    )

    assert payload["status"] == "blocked_reboot_auto_recovery_slo_not_ready"
    assert payload["can_claim_all_services_recovered_within_target"] is False
    assert payload["active_blockers"] == [
        "windows99_remote_execution_channel_unavailable",
        "windows99_vmware_autostart_readback_missing",
    ]
    matrix = payload["active_blocker_action_matrix"]
    assert matrix["schema_version"] == "reboot_active_blocker_action_matrix_v1"
    assert matrix["item_count"] == 2
    assert matrix["telegram_alert_required_count"] == 2
    assert matrix["category_counts"] == {"windows99_vmware_autostart": 2}
    assert matrix["primary_blocker_action"]["blocker"] == (
        "windows99_remote_execution_channel_unavailable"
    )
    assert matrix["primary_blocker_action"]["owner_lane"] == (
        "windows99_console_or_no_secret_management_channel"
    )
    assert matrix["items"][0]["telegram_alert"] == (
        "RebootAutoRecoveryActiveBlocker"
    )
    assert matrix["items"][0]["controlled_apply_authorized_by_scorecard"] is False
    assert "windows_password_or_secret_collection" in matrix["items"][0][
        "forbidden_actions"
    ]
    assert payload["readback"]["active_blocker_action_count"] == 2
    assert payload["readback"]["primary_blocker_owner_lane"] == (
        "windows99_console_or_no_secret_management_channel"
    )
    assert payload["safe_next_step"] == (
        "restore_windows99_no_secret_management_channel_or_collect_local_console_"
        "verify_readback_then_rerun_reboot_scorecard_no_reboot"
    )
    assert payload["windows99_management_channel"]["readback_present"] is True
    assert payload["windows99_management_channel"]["host_reachable"] is True
    assert (
        payload["windows99_management_channel"]["remote_execution_channel_ready"]
        is False
    )
    assert payload["windows99_management_channel"]["ssh_batch"]["status"] == (
        "permission_denied"
    )
    assert payload["windows99_management_channel"]["hyperv_vmconnect_open"] is True
    assert payload["windows99_management_channel"]["rdp_console_reachable"] is True
    assert (
        payload["windows99_management_channel"][
            "local_console_channel_reachable"
        ]
        is True
    )
    assert payload["windows99_management_channel"][
        "console_collection_channels"
    ] == ["rdp_console", "hyperv_vmconnect"]
    assert payload["rollups"]["windows99_management_readback_present"] is True
    assert payload["rollups"]["windows99_host_reachable"] is True
    assert payload["rollups"]["windows99_remote_execution_channel_ready"] is False
    assert payload["rollups"]["windows99_ssh_batch_status"] == "permission_denied"
    assert payload["readback"]["windows99_remote_execution_channel_ready"] is False


def test_degraded_wazuh_and_old_boot_observation_block_slo(tmp_path: Path) -> None:
    summary = GREEN_SUMMARY.replace("WAZUH_DASHBOARD_DEGRADED=0", "WAZUH_DASHBOARD_DEGRADED=1")
    probe = HOST_PROBE_GREEN.replace("uptime_seconds=150", "uptime_seconds=900")

    payload = run_scorecard(tmp_path, summary, probe=probe)

    assert payload["status"] == "blocked_reboot_auto_recovery_slo_not_ready"
    assert payload["can_claim_all_services_recovered_within_target"] is False
    assert "wazuh_dashboard_degraded" in payload["active_blockers"]
    assert "host_boot_observation_older_than_target_window" in payload["active_blockers"]


def test_services_green_but_old_boot_window_waits_for_reboot_event(tmp_path: Path) -> None:
    probe = HOST_PROBE_GREEN.replace("uptime_seconds=150", "uptime_seconds=900")

    payload = run_scorecard(tmp_path, GREEN_SUMMARY, probe=probe)

    assert payload["status"] == "blocked_reboot_auto_recovery_slo_not_ready"
    assert payload["active_blockers"] == ["host_boot_observation_older_than_target_window"]
    assert payload["current_phase"] == "awaiting_next_reboot_or_approved_drill"
    assert (
        payload["eta_or_wait_reason"]
        == "target_window_remaining_450s_but_blocked_by_host_boot_observation_older_than_target_window"
    )
    assert payload["primary_blocker"] == "host_boot_observation_older_than_target_window"
    assert payload["safe_next_step"] == (
        "timer_deployed_and_services_readback_green_wait_for_next_all_host_reboot_"
        "event_or_approved_reboot_drill_to_prove_10_minute_slo"
    )
    assert payload["next_safe_action"] == payload["safe_next_step"]


def test_stockplatform_blocked_before_final_retry_waits_for_readback(tmp_path: Path) -> None:
    summary = GREEN_SUMMARY.replace("PRODUCT_DATA_GREEN=1", "PRODUCT_DATA_GREEN=0").replace(
        "SERVICE_GREEN=1", "SERVICE_GREEN=0"
    ) + """\
STOCK_FRESHNESS_STATUS=blocked
STOCK_LATEST_TRADING_DATE=2026-06-29
STOCK_BLOCKERS=core_margin_short_daily_missing,ai_recommendations_stale
STOCK_EOD_WINDOW_PENDING=0
STOCK_EOD_CLASSIFICATION=after_first_eod_window_blocked
STOCK_EOD_NEXT_ACTION=inspect_ingestion_logs_and_wait_retry_windows
STOCK_EOD_FIRST_FULL_WINDOW_END_LOCAL=19:15
STOCK_EOD_FINAL_RETRY_WINDOW_END_LOCAL=23:35
"""
    freshness = {
        "status": "blocked",
        "latest_trading_date": "2026-06-29",
        "blockers": ["core_margin_short_daily_missing", "ai_recommendations_stale"],
        "sources": [
            {
                "source": "core.margin_short_daily",
                "latest_date": "2026-06-26",
                "status": "missing",
                "row_count": 0,
                "notes": "official source pending",
            }
        ],
    }
    ingestion = {
        "status": "blocked",
        "latest_trading_date": "2026-06-29",
        "blockers": ["core.margin_short_daily_incomplete"],
        "latest_source_runs": [
            {
                "source_run_id": 3384,
                "source_name": "official_margin_short_daily",
                "target_date": "2026-06-29",
                "status": "official_pending",
            }
        ],
    }

    payload = run_scorecard_with_stock(tmp_path, summary, freshness, ingestion)

    assert payload["status"] == "blocked_reboot_auto_recovery_slo_not_ready"
    assert "stockplatform_freshness_blocked" in payload["active_blockers"]
    assert "stockplatform_ingestion_blocked" in payload["active_blockers"]
    assert payload["stockplatform_data_freshness"]["freshness_blockers"] == [
        "core_margin_short_daily_missing",
        "ai_recommendations_stale",
    ]
    assert payload["stockplatform_data_freshness"]["eod_window"][
        "final_retry_window_passed"
    ] is False
    assert payload["stockplatform_data_freshness"]["controlled_recovery_gate"][
        "required"
    ] is False
    assert payload["safe_next_step"] == (
        "inspect_stockplatform_ingestion_readback_and_wait_retry_windows_then_"
        "rerun_slo_verify_only_no_reboot"
    )


def test_host_pressure_blocks_slo_with_index_drift_next_step(tmp_path: Path) -> None:
    payload = run_scorecard_with_host_pressure(
        tmp_path,
        GREEN_SUMMARY,
        {
            "conversation_event_hot_path_indexes_present": False,
            "hosts": [
                {
                    "host": "110",
                    "load1": 20.74,
                    "load5": 18.05,
                    "cores": 12,
                    "node_procs_running": 63,
                    "node_procs_blocked": 0,
                    "top_containers": [
                        {"container_name": "gitea", "cpu_cores": 3.4019},
                    ],
                },
                {
                    "host": "188",
                    "load1": 11.2,
                    "load5": 10.8,
                    "cores": 12,
                    "node_procs_running": 10,
                    "node_procs_blocked": 0,
                    "top_containers": [
                        {
                            "container_name": "k3s-postgres-recovery",
                            "cpu_cores": 8.5489,
                        },
                    ],
                },
            ],
        },
    )

    assert payload["status"] == "blocked_reboot_auto_recovery_slo_not_ready"
    assert payload["host_pressure"]["high_load_hosts"] == ["110"]
    assert payload["host_pressure"]["gitea_pressure_hosts"] == ["110"]
    assert payload["host_pressure"]["postgres_pressure_hosts"] == ["188"]
    assert "host_110_gitea_cpu_pressure" in payload["active_blockers"]
    assert "host_188_postgres_cpu_pressure" in payload["active_blockers"]
    assert (
        "awooop_conversation_event_hot_path_index_drift_detected"
        in payload["active_blockers"]
    )
    assert payload["safe_next_step"] == (
        "apply_awooop_conversation_event_hot_path_indexes_via_controlled_db_"
        "migration_then_rerun_188_postgres_cpu_readback_no_reboot"
    )


def test_host_pressure_does_not_attribute_stale_docker_stats_to_gitea(tmp_path: Path) -> None:
    payload = run_scorecard_with_host_pressure(
        tmp_path,
        GREEN_SUMMARY,
        {
            "hosts": [
                {
                    "host": "110",
                    "load1": 20.74,
                    "load5": 18.05,
                    "cores": 12,
                    "node_procs_running": 63,
                    "node_procs_blocked": 0,
                    "docker_stats": {
                        "fresh": False,
                        "age_seconds": 107475,
                        "max_age_seconds": 300,
                        "source": "node_textfile_mtime_seconds",
                    },
                    "top_containers": [
                        {"container_name": "gitea", "cpu_cores": 3.4019},
                    ],
                },
            ],
        },
    )

    assert payload["status"] == "blocked_reboot_auto_recovery_slo_not_ready"
    assert payload["host_pressure"]["high_load_hosts"] == ["110"]
    assert payload["host_pressure"]["gitea_pressure_hosts"] == []
    assert payload["host_pressure"]["container_attribution_stale_hosts"] == ["110"]
    assert "host_pressure_high_load" in payload["active_blockers"]
    assert "host_container_cpu_attribution_stale" in payload["active_blockers"]
    assert "host_110_gitea_cpu_pressure" not in payload["active_blockers"]
    assert payload["safe_next_step"] == (
        "restore_docker_stats_textfile_exporter_then_collect_sanitized_host_"
        "pressure_no_restart_no_secret_read"
    )


def test_stockplatform_recovered_marks_controlled_gate_not_required(
    tmp_path: Path,
) -> None:
    summary = GREEN_SUMMARY + """\
STOCK_FRESHNESS_STATUS=ok
STOCK_LATEST_TRADING_DATE=2026-06-29
STOCK_BLOCKERS=none
STOCK_EOD_WINDOW_PENDING=0
STOCK_EOD_CLASSIFICATION=recovered_after_21_05_retry_window
STOCK_EOD_NEXT_ACTION=rerun_slo_verify_only_after_next_fresh_all_host_reboot_event_or_approved_reboot_drill
STOCK_EOD_FIRST_FULL_WINDOW_END_LOCAL=19:15
STOCK_EOD_FINAL_RETRY_WINDOW_END_LOCAL=23:35
"""

    payload = run_scorecard_with_stock(
        tmp_path,
        summary,
        {
            "status": "ok",
            "latest_trading_date": "2026-06-29",
            "blockers": [],
        },
        {
            "status": "ok",
            "latest_trading_date": "2026-06-29",
            "blockers": [],
            "latest_source_runs": [
                {
                    "source_run_id": 3390,
                    "source_name": "official_margin_short_daily",
                    "target_date": "2026-06-29",
                    "status": "succeeded",
                },
                {
                    "source_run_id": 3389,
                    "source_name": "official_margin_short_daily",
                    "target_date": "2026-06-29",
                    "status": "succeeded",
                },
            ],
        },
        generated_at="2026-06-29T23:40:00+08:00",
    )

    stockplatform = payload["stockplatform_data_freshness"]
    assert stockplatform["eod_window"]["final_retry_window_passed"] is True
    assert stockplatform["controlled_recovery_gate"]["required"] is False
    assert (
        stockplatform["controlled_recovery_gate"]["status"]
        == "not_required_freshness_recovered"
    )
    assert stockplatform["margin_short_recovery"]["status"] == "recovered"
    assert stockplatform["margin_short_recovery"]["successful_source_run_ids"] == [
        3390,
        3389,
    ]
    assert stockplatform["ai_recommendations_recovery"]["status"] == "recovered"


def test_stockplatform_blocked_after_final_retry_opens_controlled_gate(
    tmp_path: Path,
) -> None:
    summary = GREEN_SUMMARY.replace("PRODUCT_DATA_GREEN=1", "PRODUCT_DATA_GREEN=0").replace(
        "SERVICE_GREEN=1", "SERVICE_GREEN=0"
    ) + """\
STOCK_FRESHNESS_STATUS=blocked
STOCK_BLOCKERS=core_margin_short_daily_missing
STOCK_EOD_WINDOW_PENDING=0
STOCK_EOD_CLASSIFICATION=after_final_retry_window_blocked
STOCK_EOD_NEXT_ACTION=open_stockplatform_data_recovery_gate
STOCK_EOD_FINAL_RETRY_WINDOW_END_LOCAL=23:35
"""

    payload = run_scorecard_with_stock(
        tmp_path,
        summary,
        {"status": "blocked", "blockers": ["core_margin_short_daily_missing"]},
        {"status": "blocked", "blockers": ["core.margin_short_daily_incomplete"]},
        generated_at="2026-06-29T23:40:00+08:00",
    )

    gate = payload["stockplatform_data_freshness"]["controlled_recovery_gate"]
    assert gate["required"] is True
    assert gate["status"] == "ready_to_open"
    assert "manual_db_update" in gate["forbidden_actions"]
    assert payload["safe_next_step"] == (
        "open_stockplatform_controlled_data_recovery_gate_with_source_diff_"
        "check_mode_rollback_and_post_freshness_verifier_no_manual_db_write"
    )
