from __future__ import annotations

import json

import pytest

from src.services.service_health_gap_matrix import load_latest_service_health_gap_matrix


def test_load_latest_service_health_gap_matrix_reads_newest_file(tmp_path):
    older = _snapshot(generated_at="2026-06-04T00:00:00+08:00", completion=40)
    newer = _snapshot(generated_at="2026-06-05T00:00:00+08:00", completion=100)
    (tmp_path / "service_health_gap_matrix_2026-06-04.json").write_text(
        json.dumps(older),
        encoding="utf-8",
    )
    (tmp_path / "service_health_gap_matrix_2026-06-05.json").write_text(
        json.dumps(newer),
        encoding="utf-8",
    )

    loaded = load_latest_service_health_gap_matrix(tmp_path)

    assert loaded["generated_at"] == "2026-06-05T00:00:00+08:00"
    assert loaded["program_status"]["overall_completion_percent"] == 100
    assert loaded["rollups"]["total_targets"] == 7
    assert loaded["operation_boundaries"]["active_probe_allowed"] is False


def test_service_health_gap_matrix_requires_read_only_mode(tmp_path):
    snapshot = _snapshot()
    snapshot["program_status"]["read_only_mode"] = False
    (tmp_path / "service_health_gap_matrix_2026-06-05.json").write_text(
        json.dumps(snapshot),
        encoding="utf-8",
    )

    with pytest.raises(ValueError, match="read_only_mode"):
        load_latest_service_health_gap_matrix(tmp_path)


def test_service_health_gap_matrix_blocks_probe_restart_endpoint_and_notifications(tmp_path):
    snapshot = _snapshot()
    snapshot["operation_boundaries"]["active_probe_allowed"] = True
    snapshot["operation_boundaries"]["service_restart_allowed"] = True
    snapshot["operation_boundaries"]["endpoint_change_allowed"] = True
    snapshot["operation_boundaries"]["notification_send_allowed"] = True
    (tmp_path / "service_health_gap_matrix_2026-06-05.json").write_text(
        json.dumps(snapshot),
        encoding="utf-8",
    )

    with pytest.raises(ValueError, match="operation boundaries"):
        load_latest_service_health_gap_matrix(tmp_path)


def test_service_health_gap_matrix_requires_rollup_consistency(tmp_path):
    snapshot = _snapshot()
    snapshot["rollups"]["target_ids_requiring_action"] = []
    (tmp_path / "service_health_gap_matrix_2026-06-05.json").write_text(
        json.dumps(snapshot),
        encoding="utf-8",
    )

    with pytest.raises(ValueError, match="target_ids_requiring_action"):
        load_latest_service_health_gap_matrix(tmp_path)


def test_service_health_gap_matrix_requires_required_gaps_and_stale_endpoints(tmp_path):
    snapshot = _snapshot()
    snapshot["health_gaps"] = snapshot["health_gaps"][:-1]
    snapshot["stale_endpoints"] = snapshot["stale_endpoints"][:-1]
    _refresh_rollups(snapshot)
    (tmp_path / "service_health_gap_matrix_2026-06-05.json").write_text(
        json.dumps(snapshot),
        encoding="utf-8",
    )

    with pytest.raises(ValueError, match="missing required health gaps"):
        load_latest_service_health_gap_matrix(tmp_path)


def test_service_health_gap_matrix_requires_operator_denials(tmp_path):
    snapshot = _snapshot()
    snapshot["operator_contract"]["must_not_interpret_as"].remove("active probe 批准")
    (tmp_path / "service_health_gap_matrix_2026-06-05.json").write_text(
        json.dumps(snapshot),
        encoding="utf-8",
    )

    with pytest.raises(ValueError, match="operator_contract"):
        load_latest_service_health_gap_matrix(tmp_path)


def test_service_health_gap_matrix_requires_endpoint_and_notification_policies(tmp_path):
    snapshot = _snapshot()
    snapshot["operator_contract"]["endpoint_policy"] = "可修改端點"
    (tmp_path / "service_health_gap_matrix_2026-06-05.json").write_text(
        json.dumps(snapshot),
        encoding="utf-8",
    )

    with pytest.raises(ValueError, match="endpoint_policy"):
        load_latest_service_health_gap_matrix(tmp_path)


def test_service_health_gap_matrix_rejects_secret_payload_keys(tmp_path):
    snapshot = _snapshot()
    snapshot["latest_observations"][0]["authorization_header"] = "redacted"
    (tmp_path / "service_health_gap_matrix_2026-06-05.json").write_text(
        json.dumps(snapshot),
        encoding="utf-8",
    )

    with pytest.raises(ValueError, match="forbidden secret payload key"):
        load_latest_service_health_gap_matrix(tmp_path)


def test_service_health_gap_matrix_fails_when_missing(tmp_path):
    with pytest.raises(FileNotFoundError):
        load_latest_service_health_gap_matrix(tmp_path)


def _snapshot(
    *,
    generated_at: str = "2026-06-05T00:00:00+08:00",
    completion: int = 100,
) -> dict:
    targets = [
        _target("production_api_health_public", "api_health", "verified", "critical", "fresh_readback"),
        _target("awoooi_web_health_manifest", "web_health", "verified", "high", "manifest_mapped"),
        _target("ollama_three_layer_health_contract", "ai_provider_health", "action_required", "critical", "source_mismatch"),
        _target("openclaw_health_endpoint_contract", "ai_provider_health", "action_required", "critical", "source_mismatch"),
        _target("prometheus_alertmanager_endpoint_reference", "observability_health", "action_required", "high", "source_mismatch"),
        _target("gitea_workflow_runner_health_contract", "devops_health", "action_required", "critical", "stale_evidence"),
        _target("kali_scanner_health_reference", "security_edge_health", "action_required", "medium", "manifest_only"),
    ]
    gaps = [
        _gap("endpoint_reference_stale_hosts", ["ollama_three_layer_health_contract"]),
        _gap("gitea_runner_attestation_health_gap", ["gitea_workflow_runner_health_contract"]),
        _gap("health_check_script_not_authoritative", ["production_api_health_public"]),
        _gap("openclaw_nemo_rca_health_review", ["openclaw_health_endpoint_contract"]),
        _gap("security_scanner_health_evidence_gap", ["kali_scanner_health_reference"]),
    ]
    endpoints = [
        _endpoint("legacy_188_ollama_provider_endpoint"),
        _endpoint("prometheus_alertmanager_110_188_split"),
        _endpoint("openclaw_8088_comment_vs_8089_contract"),
    ]
    payload = {
        "schema_version": "service_health_gap_matrix_v1",
        "generated_at": generated_at,
        "program_status": {
            "overall_completion_percent": completion,
            "current_priority": "P1",
            "current_task_id": "P1-005",
            "next_task_id": "P1-006",
            "read_only_mode": True,
        },
        "source_refs": ["docs/schemas/service_health_gap_matrix_v1.schema.json"],
        "rollups": {},
        "service_health_targets": targets,
        "health_gaps": gaps,
        "stale_endpoints": endpoints,
        "latest_observations": [
            {
                "observation_id": "service_health_gap_matrix_seed",
                "status": "verified",
                "summary": "只讀 health gap seed。",
                "evidence_refs": ["docs/reference/SERVICE-ENDPOINTS.md"],
            }
        ],
        "operator_contract": {
            "display_mode": "read_only_service_health_gap_matrix",
            "must_not_interpret_as": [
                "服務重啟批准",
                "endpoint 修改批准",
                "active probe 批准",
                "live health check 已執行",
                "Secret payload 已讀取或可輸出",
                "Telegram 成功通知批准",
                "workflow / deploy / reload 觸發批准",
                "provider 切換批准",
                "runtime execution 授權",
                "OpenClaw 取代或降級批准",
            ],
            "secret_display_policy": "只顯示來源檔案與 env var 名稱。",
            "restart_policy": "P1-005 只能列缺口；任何 restart 都需另行批准。",
            "endpoint_policy": "P1-005 不修改端點，只提出 source truth cleanup。",
            "notification_policy": "成功 smoke 不通知；失敗或 stale 才進 P1-007 通知合約。",
        },
        "operation_boundaries": {
            "read_only_api_allowed": True,
            "service_restart_allowed": False,
            "pod_restart_allowed": False,
            "host_restart_allowed": False,
            "rollout_restart_allowed": False,
            "endpoint_change_allowed": False,
            "configmap_patch_allowed": False,
            "active_probe_allowed": False,
            "external_health_probe_allowed": False,
            "live_benchmark_allowed": False,
            "provider_switch_allowed": False,
            "paid_api_call_allowed": False,
            "secret_read_allowed": False,
            "secret_plaintext_allowed": False,
            "notification_send_allowed": False,
            "workflow_trigger_allowed": False,
            "deploy_trigger_allowed": False,
            "reload_trigger_allowed": False,
            "runtime_execution_allowed": False,
        },
        "approval_boundaries": {
            "service_restart_approved": False,
            "endpoint_change_approved": False,
            "configmap_patch_approved": False,
            "active_probe_approved": False,
            "secret_read_approved": False,
            "notification_send_approved": False,
            "workflow_trigger_approved": False,
            "provider_switch_approved": False,
            "cost_change_approved": False,
        },
    }
    _refresh_rollups(payload)
    return payload


def _target(target_id: str, kind: str, status: str, risk_level: str, freshness_status: str) -> dict:
    return {
        "target_id": target_id,
        "display_name": target_id,
        "kind": kind,
        "status": status,
        "risk_level": risk_level,
        "freshness_status": freshness_status,
        "health_contract": "只讀健康合約。",
        "endpoint_contract": "不修改 endpoint。",
        "evidence_refs": ["docs/reference/SERVICE-ENDPOINTS.md"],
        "next_action": "只補證據，不執行。",
    }


def _gap(gap_id: str, target_ids: list[str]) -> dict:
    return {
        "gap_id": gap_id,
        "display_name": gap_id,
        "status": "action_required",
        "severity": "high",
        "summary": "只讀缺口。",
        "target_ids": target_ids,
        "evidence_refs": ["docs/reference/SERVICE-ENDPOINTS.md"],
        "next_action": "只整理 owner attestation。",
    }


def _endpoint(endpoint_id: str) -> dict:
    return {
        "endpoint_id": endpoint_id,
        "display_name": endpoint_id,
        "status": "action_required",
        "severity": "medium",
        "stale_ref": "舊端點參考。",
        "current_truth": "只標記 source drift，不判定 live down。",
        "evidence_refs": ["docs/reference/SERVICE-ENDPOINTS.md"],
        "next_action": "只整理 source cleanup。",
    }


def _refresh_rollups(payload: dict) -> None:
    targets = payload["service_health_targets"]
    gaps = payload["health_gaps"]
    endpoints = payload["stale_endpoints"]
    payload["rollups"] = {
        "total_targets": len(targets),
        "by_kind": _count_by(targets, "kind"),
        "by_status": _count_by(targets, "status"),
        "by_freshness_status": _count_by(targets, "freshness_status"),
        "target_ids_requiring_action": sorted(
            target["target_id"] for target in targets if target["status"] == "action_required"
        ),
        "health_gap_ids": sorted(gap["gap_id"] for gap in gaps),
        "stale_endpoint_ids": sorted(endpoint["endpoint_id"] for endpoint in endpoints),
        "critical_target_ids": sorted(
            target["target_id"] for target in targets if target["risk_level"] == "critical"
        ),
        "read_only_denials_total": 10,
        "service_restart_allowed_count": 0,
        "endpoint_change_allowed_count": 0,
        "active_probe_allowed_count": 0,
        "notification_send_allowed_count": 0,
        "runtime_execution_allowed_count": 0,
    }


def _count_by(items: list[dict], key: str) -> dict[str, int]:
    counts: dict[str, int] = {}
    for item in items:
        value = item[key]
        counts[value] = counts.get(value, 0) + 1
    return counts
