# grantflow/tests/test_integration_readiness_ready.py

from types import SimpleNamespace

from fastapi.testclient import TestClient

import grantflow.api.readiness_service as readiness_service
from grantflow.api import runtime_state
from grantflow.api.app import app
from grantflow.core.config import config

client = TestClient(app)


def test_ready_endpoint():
    response = client.get("/ready")
    assert response.status_code == 200
    body = response.json()
    assert body["status"] == "ready"
    checks = body["checks"]
    assert checks["vector_store"]["backend"] in {"chroma", "memory"}
    assert checks["vector_store"]["ready"] is True
    assert checks["job_runner"]["mode"] in {"background_tasks", "inmemory_queue", "redis_queue"}
    assert isinstance(checks["job_runner"]["ready"], bool)
    assert isinstance(checks["job_runner"]["queue"]["queue_size"], int)
    preflight_policy = checks["preflight_grounding_policy"]
    assert preflight_policy["mode"] in {"warn", "strict", "off"}
    thresholds = preflight_policy["thresholds"]
    assert 0.0 <= float(thresholds["high_risk_coverage_threshold"]) <= 1.0
    assert 0.0 <= float(thresholds["medium_risk_coverage_threshold"]) <= 1.0
    assert int(thresholds["min_uploads"]) >= 1
    runtime_gate = checks["runtime_grounded_quality_gate"]
    assert runtime_gate["mode"] in {"warn", "strict", "off"}
    runtime_thresholds = runtime_gate["thresholds"]
    assert int(runtime_thresholds["min_citations_for_gate"]) >= 0
    assert 0.0 <= float(runtime_thresholds["max_non_retrieval_citation_rate"]) <= 1.0
    assert int(runtime_thresholds["min_retrieval_grounded_citations"]) >= 0
    mel_policy = checks["mel_grounding_policy"]
    assert mel_policy["mode"] in {"warn", "strict", "off"}
    mel_thresholds = mel_policy["thresholds"]
    assert int(mel_thresholds["min_mel_citations"]) >= 1
    assert 0.0 <= float(mel_thresholds["min_claim_support_rate"]) <= 1.0
    assert 0.0 <= float(mel_thresholds["min_traceability_complete_rate"]) <= 1.0
    assert 0.0 <= float(mel_thresholds["max_traceability_gap_rate"]) <= 1.0
    export_policy = checks["export_grounding_policy"]
    assert export_policy["mode"] in {"warn", "strict", "off"}
    export_thresholds = export_policy["thresholds"]
    assert int(export_thresholds["min_architect_citations"]) >= 1
    assert 0.0 <= float(export_thresholds["min_claim_support_rate"]) <= 1.0
    assert 0.0 <= float(export_thresholds["min_traceability_complete_rate"]) <= 1.0
    assert 0.0 <= float(export_thresholds["max_traceability_gap_rate"]) <= 1.0
    export_contract_policy = checks["export_contract_policy"]
    assert export_contract_policy["mode"] in {"warn", "strict", "off"}
    export_runtime_gate_policy = checks["export_runtime_grounded_gate_policy"]
    assert isinstance(export_runtime_gate_policy["require_pass"], bool)
    runtime_compatibility_policy = checks["runtime_compatibility_policy"]
    assert runtime_compatibility_policy["mode"] in {"warn", "strict", "off"}
    runtime_status = runtime_compatibility_policy["status"]
    assert isinstance(runtime_status.get("python_version"), str)
    assert runtime_status.get("supported_range") == "3.11-3.13"
    assert isinstance(runtime_status.get("supported"), bool)
    assert isinstance(runtime_compatibility_policy.get("blocking"), bool)
    assert isinstance(runtime_compatibility_policy.get("alerts"), list)
    tenant_policy = checks["tenant_authz_configuration_policy"]
    assert tenant_policy["mode"] in {"warn", "strict", "off"}
    tenant_status = tenant_policy["status"]
    assert isinstance(tenant_status.get("enabled"), bool)
    assert isinstance(tenant_status.get("allowed_tenant_count"), int)
    assert tenant_status.get("default_tenant") is None or isinstance(tenant_status.get("default_tenant"), str)
    assert isinstance(tenant_status.get("issues"), list)
    assert isinstance(tenant_status.get("valid"), bool)
    assert isinstance(tenant_policy.get("blocking"), bool)
    assert isinstance(tenant_policy.get("alerts"), list)
    assert isinstance(checks.get("configuration_warnings"), list)
    dispatcher_policy = checks["job_runner"]["dispatcher_worker_heartbeat_policy"]
    assert dispatcher_policy["mode"] in {"off", "warn", "strict"}
    dispatcher_heartbeat = checks["job_runner"]["dispatcher_worker_heartbeat"]
    assert dispatcher_heartbeat is None or isinstance(dispatcher_heartbeat, dict)


def test_ready_endpoint_runtime_compatibility_warn_mode_does_not_block(monkeypatch):
    monkeypatch.setattr(config.graph, "runtime_compatibility_policy_mode", "warn")
    monkeypatch.setattr(runtime_state, "runtime_sys", SimpleNamespace(version_info=(3, 14, 0, "final", 0)))
    monkeypatch.setattr(readiness_service, "_vector_store_readiness", lambda: {"ready": True, "backend": "memory"})

    response = client.get("/ready")
    assert response.status_code == 200
    body = response.json()
    checks = body["checks"]
    policy = checks["runtime_compatibility_policy"]
    assert policy["mode"] == "warn"
    assert policy["status"]["supported"] is False
    assert policy["blocking"] is False
    alerts = policy["alerts"]
    assert any(
        str(item.get("code") or "") == "PYTHON_RUNTIME_COMPATIBILITY_RISK" for item in alerts if isinstance(item, dict)
    )


def test_ready_endpoint_runtime_compatibility_strict_mode_blocks(monkeypatch):
    monkeypatch.setattr(config.graph, "runtime_compatibility_policy_mode", "strict")
    monkeypatch.setattr(runtime_state, "runtime_sys", SimpleNamespace(version_info=(3, 14, 0, "final", 0)))
    monkeypatch.setattr(readiness_service, "_vector_store_readiness", lambda: {"ready": True, "backend": "memory"})

    response = client.get("/ready")
    assert response.status_code == 503
    detail = response.json()["detail"]
    assert detail["status"] == "degraded"
    policy = detail["checks"]["runtime_compatibility_policy"]
    assert policy["mode"] == "strict"
    assert policy["status"]["supported"] is False
    assert policy["blocking"] is True
    alerts = policy["alerts"]
    assert any(
        str(item.get("code") or "") == "PYTHON_RUNTIME_COMPATIBILITY_RISK" for item in alerts if isinstance(item, dict)
    )


def test_ready_endpoint_tenant_authz_policy_warn_mode_does_not_block(monkeypatch):
    monkeypatch.setenv("GRANTFLOW_TENANT_AUTHZ_ENABLED", "true")
    monkeypatch.delenv("GRANTFLOW_ALLOWED_TENANTS", raising=False)
    monkeypatch.delenv("AIDGRAPH_ALLOWED_TENANTS", raising=False)
    monkeypatch.setenv("GRANTFLOW_TENANT_AUTHZ_CONFIGURATION_POLICY_MODE", "warn")
    monkeypatch.setattr(readiness_service, "_vector_store_readiness", lambda: {"ready": True, "backend": "memory"})

    response = client.get("/ready")
    assert response.status_code == 200
    body = response.json()
    policy = body["checks"]["tenant_authz_configuration_policy"]
    assert policy["mode"] == "warn"
    assert policy["status"]["enabled"] is True
    assert policy["status"]["valid"] is False
    assert policy["blocking"] is False
    alerts = policy["alerts"]
    assert any(
        str(item.get("code") or "") == "TENANT_AUTHZ_CONFIGURATION_RISK" for item in alerts if isinstance(item, dict)
    )


def test_ready_endpoint_tenant_authz_policy_strict_mode_blocks(monkeypatch):
    monkeypatch.setenv("GRANTFLOW_TENANT_AUTHZ_ENABLED", "true")
    monkeypatch.delenv("GRANTFLOW_ALLOWED_TENANTS", raising=False)
    monkeypatch.delenv("AIDGRAPH_ALLOWED_TENANTS", raising=False)
    monkeypatch.setenv("GRANTFLOW_TENANT_AUTHZ_CONFIGURATION_POLICY_MODE", "strict")
    monkeypatch.setattr(readiness_service, "_vector_store_readiness", lambda: {"ready": True, "backend": "memory"})

    response = client.get("/ready")
    assert response.status_code == 503
    detail = response.json()["detail"]
    assert detail["status"] == "degraded"
    policy = detail["checks"]["tenant_authz_configuration_policy"]
    assert policy["mode"] == "strict"
    assert policy["status"]["enabled"] is True
    assert policy["status"]["valid"] is False
    assert policy["blocking"] is True
    alerts = policy["alerts"]
    assert any(
        str(item.get("code") or "") == "TENANT_AUTHZ_CONFIGURATION_RISK" for item in alerts if isinstance(item, dict)
    )


def test_ready_endpoint_tenant_authz_policy_strict_mode_blocks_default_tenant_mismatch(monkeypatch):
    monkeypatch.setenv("GRANTFLOW_TENANT_AUTHZ_ENABLED", "true")
    monkeypatch.setenv("GRANTFLOW_ALLOWED_TENANTS", "tenant_alpha")
    monkeypatch.setenv("GRANTFLOW_DEFAULT_TENANT", "tenant_beta")
    monkeypatch.setenv("GRANTFLOW_TENANT_AUTHZ_CONFIGURATION_POLICY_MODE", "strict")
    monkeypatch.setattr(readiness_service, "_vector_store_readiness", lambda: {"ready": True, "backend": "memory"})

    response = client.get("/ready")
    assert response.status_code == 503
    detail = response.json()["detail"]
    assert detail["status"] == "degraded"
    policy = detail["checks"]["tenant_authz_configuration_policy"]
    assert policy["mode"] == "strict"
    assert policy["status"]["enabled"] is True
    assert policy["status"]["valid"] is False
    assert policy["blocking"] is True
    alerts = policy["alerts"]
    assert any(
        str(item.get("code") or "") == "TENANT_AUTHZ_CONFIGURATION_RISK" for item in alerts if isinstance(item, dict)
    )
