from __future__ import annotations

import json

from fastapi import FastAPI
from fastapi.testclient import TestClient

from src.api.v1 import agents
from src.api.v1.agents import router
from src.services import stockplatform_public_api_runtime_readback as runtime_readback
from src.services.stockplatform_public_api_runtime_readback import (
    load_latest_stockplatform_public_api_runtime_readback,
)


def test_stockplatform_public_api_runtime_readback_blocks_live_502():
    payload = load_latest_stockplatform_public_api_runtime_readback(
        probe=_probe_public_web_ok_api_502
    )

    assert payload["schema_version"] == "stockplatform_public_api_runtime_readback_v1"
    assert payload["priority"] == "P0-006"
    assert payload["status"] == "blocked_stockplatform_public_api_runtime_drift"
    assert payload["runtime_ready"] is False
    assert payload["live_drift_from_committed_scorecard"] is True
    assert payload["readback"]["api_health_http_status"] == 502
    assert payload["readback"]["freshness_http_status"] == 502
    assert payload["readback"]["ingestion_http_status"] == 502
    assert payload["readback"]["committed_freshness_status"] == "ok"
    assert payload["readback"]["committed_ingestion_status"] == "ok"
    assert payload["readback"]["live_drift_from_committed_scorecard"] is True
    assert payload["rollups"]["public_web_health_ok"] is True
    assert payload["rollups"]["public_api_health_ok"] is False
    assert payload["rollups"]["http_502_count"] == 3
    assert payload["rollups"]["committed_stockplatform_freshness_ok"] is True
    assert payload["rollups"]["committed_stockplatform_ingestion_ok"] is True
    assert payload["rollups"]["live_drift_from_committed_scorecard"] is True
    assert payload["rollups"]["data_dependency_classification"] == "none"
    assert payload["rollups"]["postgres_not_ready"] is False
    assert payload["safe_next_step"] == (
        "recover_stockplatform_api_container_runtime_or_docker_control_path_"
        "without_daemon_restart_then_rerun_public_api_readback"
    )
    assert payload["active_blockers"] == [
        "stockplatform_public_api_healthz_http_502",
        "stockplatform_freshness_http_502",
        "stockplatform_ingestion_http_502",
        "stockplatform_postgres_not_ready",
        "stockplatform_post_apply_public_api_verifier_not_green",
    ]
    assert (
        payload["recovery_control_path"]["status"]
        == "api_upstream_recovered_data_readiness_not_green"
    )
    assert payload["recovery_control_path"]["receipt"]["source_of_truth_diff"][
        "gitea_main_sha"
    ].startswith("080bc2b")
    assert payload["rollups"]["recovery_control_path_blocker_count"] == 2
    assert (
        payload["operation_boundaries"]["read_only_public_https_probe"] is True
    )
    assert payload["operation_boundaries"]["ssh_used"] is False
    assert payload["operation_boundaries"]["docker_restart_performed"] is False
    assert payload["operation_boundaries"]["docker_daemon_restart_performed"] is False
    assert payload["operation_boundaries"]["host_reboot_performed"] is False
    assert payload["operation_boundaries"]["database_write_or_restore_performed"] is False
    assert payload["operation_boundaries"]["secret_value_collection_allowed"] is False


def test_stockplatform_public_api_runtime_readback_ready_when_live_green():
    payload = load_latest_stockplatform_public_api_runtime_readback(probe=_probe_ok)

    assert payload["status"] == "stockplatform_public_api_runtime_ready"
    assert payload["runtime_ready"] is True
    assert payload["live_drift_from_committed_scorecard"] is False
    assert payload["active_blockers"] == []
    assert payload["readback"]["freshness_status"] == "ok"
    assert payload["readback"]["ingestion_status"] == "ok"
    assert payload["rollups"]["http_502_count"] == 0


def test_stockplatform_public_api_runtime_readback_retries_slow_data_endpoints():
    calls: dict[str, int] = {}

    def probe(url: str, timeout_seconds: float) -> dict:
        assert timeout_seconds == (
            10.0
            if url.endswith(("/api/v1/system/freshness", "/api/v1/system/ingestion"))
            else 4.0
        )
        calls[url] = calls.get(url, 0) + 1
        if url.endswith(("/api/v1/system/freshness", "/api/v1/system/ingestion")):
            if calls[url] == 1:
                return {"http_status": None, "body": "", "error": "TimeoutError"}
            return {
                "http_status": 200,
                "body": json.dumps({"status": "ok", "blockers": []}),
                "error": "",
            }
        return {"http_status": 200, "body": "ok", "error": ""}

    payload = load_latest_stockplatform_public_api_runtime_readback(probe=probe)

    assert payload["status"] == "stockplatform_public_api_runtime_ready"
    assert payload["runtime_ready"] is True
    assert payload["active_blockers"] == []
    assert payload["probes"]["freshness"]["attempt_count"] == 2
    assert payload["probes"]["ingestion"]["attempt_count"] == 2
    assert payload["data_endpoint_timeout_seconds"] == 10.0
    assert payload["probe_attempts"] == 2


def test_stockplatform_public_api_runtime_readback_routes_postgres_not_ready():
    payload = load_latest_stockplatform_public_api_runtime_readback(
        probe=_probe_public_api_ok_postgres_not_ready
    )

    assert payload["status"] == "blocked_stockplatform_public_api_runtime_drift"
    assert payload["runtime_ready"] is False
    assert payload["readback"]["api_health_http_status"] == 200
    assert payload["readback"]["freshness_status"] == "not_configured"
    assert payload["readback"]["ingestion_status"] == "not_configured"
    assert payload["readback"]["freshness_blockers"] == ["postgres_not_ready"]
    assert payload["readback"]["ingestion_blockers"] == ["postgres_not_ready"]
    assert payload["readback"]["data_dependency_classification"] == (
        "postgres_not_ready"
    )
    assert payload["readback"]["postgres_not_ready"] is True
    assert payload["rollups"]["http_502_count"] == 0
    assert payload["rollups"]["postgres_not_ready"] is True
    assert (
        payload["rollups"][
            "postgres_readiness_or_data_source_readback_present"
        ]
        is True
    )
    assert (
        payload["rollups"][
            "postgres_readiness_or_data_source_receipt_provided"
        ]
        is True
    )
    data_readiness = payload["data_readiness_control_readback"]
    assert data_readiness["schema_version"] == (
        "stockplatform_postgres_readiness_or_data_source_readback_v1"
    )
    assert data_readiness["receipt_key"] == (
        "stockplatform_postgres_readiness_or_data_source_readback"
    )
    assert data_readiness["status"] == "blocked_stockplatform_postgres_not_ready"
    assert data_readiness["readback_present"] is True
    assert data_readiness["receipt_provided"] is True
    assert data_readiness["postgres_ready"] is False
    assert data_readiness["postgres_not_ready"] is True
    assert data_readiness["operation_boundaries"]["public_https_probe_only"] is True
    assert data_readiness["operation_boundaries"]["database_write_or_restore_performed"] is False
    assert data_readiness["operation_boundaries"]["secret_value_collection_allowed"] is False
    assert "stockplatform_postgres_readiness_or_data_source_readback" in payload[
        "recovery_control_path"
    ]["provided_receipts"]
    assert "stockplatform_postgres_readiness_or_data_source_readback" not in payload[
        "recovery_control_path"
    ]["missing_receipts"]
    assert payload["recovery_control_path"]["missing_receipts"] == [
        "post_apply_public_api_verifier_green"
    ]
    assert payload["safe_next_step"] == (
        "run_stockplatform_production_migration_path_after_control_channel_"
        "readback_then_rerun_public_api_readback"
    )
    assert "stockplatform_freshness_postgres_not_ready" in payload[
        "active_blockers"
    ]
    assert "stockplatform_ingestion_postgres_not_ready" in payload[
        "active_blockers"
    ]


def test_stockplatform_public_api_runtime_readback_routes_data_dependency_without_postgres():
    payload = load_latest_stockplatform_public_api_runtime_readback(
        probe=_probe_public_api_ok_data_dependency_blocked
    )

    assert payload["status"] == "blocked_stockplatform_public_api_runtime_drift"
    assert payload["readback"]["api_health_http_status"] == 200
    assert payload["readback"]["freshness_status"] == "blocked"
    assert payload["readback"]["ingestion_status"] == "unknown"
    assert payload["readback"]["freshness_latest_trading_date"] == "2026-07-02"
    assert payload["readback"]["freshness_sla_source_count"] == 2
    assert payload["readback"]["freshness_source_count"] == 3
    assert payload["readback"]["freshness_non_ok_source_count"] == 2
    assert payload["readback"]["data_dependency_classification"] == (
        "freshness_or_ingestion_not_ready"
    )
    assert payload["readback"]["postgres_not_ready"] is False
    assert payload["safe_next_step"] == (
        "run_stockplatform_data_dependency_source_contract_readback_then_"
        "rerun_public_api_verifier"
    )
    assert payload["data_readiness_control_readback"]["status"] == (
        "blocked_stockplatform_data_dependency_not_ready"
    )
    assert payload["data_readiness_control_readback"][
        "freshness_latest_trading_date"
    ] == "2026-07-02"
    assert payload["data_readiness_control_readback"][
        "freshness_sla_source_count"
    ] == 2
    assert payload["data_readiness_control_readback"]["receipt_provided"] is True
    source_contract = payload["source_contract_control_readback"]
    assert source_contract["schema_version"] == (
        "stockplatform_source_contract_control_readback_v1"
    )
    assert source_contract["receipt_key"] == "stockplatform_source_contract_readback"
    assert source_contract["status"] == "blocked_stockplatform_source_contract_not_ready"
    assert source_contract["receipt_provided"] is True
    assert source_contract["latest_trading_date"] == "2026-07-02"
    assert source_contract["source_count"] == 3
    assert source_contract["sla_source_count"] == 2
    assert source_contract["non_ok_source_count"] == 2
    assert source_contract["blocking_sources"][0]["source"] == (
        "core.margin_short_daily"
    )
    assert source_contract["operation_boundaries"]["public_https_probe_only"] is True
    assert source_contract["operation_boundaries"]["database_write_or_restore_performed"] is False
    assert source_contract["operation_boundaries"]["secret_value_collection_allowed"] is False
    assert payload["readback"]["source_contract_control_readback_status"] == (
        "blocked_stockplatform_source_contract_not_ready"
    )
    assert payload["readback"]["source_contract_receipt_provided"] is True
    assert payload["readback"]["source_contract_non_ok_source_count"] == 2
    assert payload["rollups"]["source_contract_readback_present"] is True
    assert payload["rollups"]["source_contract_receipt_provided"] is True
    assert payload["rollups"]["source_contract_non_ok_source_count"] == 2
    assert "stockplatform_source_contract_readback" in payload[
        "recovery_control_path"
    ]["provided_receipts"]
    assert "stockplatform_postgres_not_ready" not in payload["active_blockers"]
    assert payload["recovery_control_path"]["active_blockers"] == [
        "stockplatform_post_apply_public_api_verifier_not_green"
    ]
    assert payload["recovery_control_path"]["missing_receipts"] == [
        "post_apply_public_api_verifier_green"
    ]


def test_stockplatform_public_api_runtime_readback_routes_ai_stale_after_core_recovered():
    payload = load_latest_stockplatform_public_api_runtime_readback(
        probe=_probe_public_api_ok_ai_recommendations_stale_after_core_recovered
    )

    assert payload["status"] == "blocked_stockplatform_public_api_runtime_drift"
    assert payload["readback"]["api_health_http_status"] == 200
    assert payload["readback"]["freshness_status"] == "blocked"
    assert payload["readback"]["ingestion_status"] == "ok"
    assert payload["readback"]["freshness_latest_trading_date"] == "2026-07-02"
    assert payload["readback"]["freshness_blockers"] == [
        "ai_recommendations_stale"
    ]
    assert payload["readback"]["data_dependency_classification"] == (
        "ai_recommendations_stale_after_core_recovered"
    )
    assert payload["readback"][
        "ai_recommendations_stale_after_core_recovered"
    ] is True
    assert payload["safe_next_step"] == (
        "run_stockplatform_ai_recommendation_pipeline_readback_then_rerun_"
        "public_api_verifier"
    )
    assert payload["data_readiness_control_readback"]["status"] == (
        "blocked_stockplatform_ai_recommendations_stale_after_core_recovered"
    )
    assert payload["source_contract_control_readback"]["status"] == (
        "stockplatform_source_contract_core_ready_ai_recommendations_stale"
    )
    assert payload["rollups"]["data_dependency_classification"] == (
        "ai_recommendations_stale_after_core_recovered"
    )
    assert payload["rollups"][
        "ai_recommendations_stale_after_core_recovered"
    ] is True
    assert "stockplatform_freshness_core_margin_short_daily_missing" not in payload[
        "active_blockers"
    ]
    assert "stockplatform_ingestion_core.margin_short_daily_incomplete" not in payload[
        "active_blockers"
    ]
    assert "stockplatform_postgres_not_ready" not in payload["active_blockers"]
    assert "stockplatform_freshness_ai_recommendations_stale" in payload[
        "active_blockers"
    ]
    assert "stockplatform_post_apply_public_api_verifier_not_green" in payload[
        "active_blockers"
    ]


def test_stockplatform_public_api_runtime_endpoint_returns_readback(monkeypatch):
    monkeypatch.setattr(
        agents,
        "load_latest_stockplatform_public_api_runtime_readback",
        lambda: load_latest_stockplatform_public_api_runtime_readback(
            probe=_probe_public_web_ok_api_502
        ),
    )
    app = FastAPI()
    app.include_router(router, prefix="/api/v1")
    client = TestClient(app)

    response = client.get("/api/v1/agents/stockplatform-public-api-runtime-readback")

    assert response.status_code == 200
    data = response.json()
    assert data["status"] == "blocked_stockplatform_public_api_runtime_drift"
    assert data["active_blocker_count"] == 5


def test_http_probe_reads_full_large_json_body(monkeypatch):
    body = json.dumps({"padding": "x" * 9000, "status": "ok"}).encode()

    class Response:
        status = 200

        def __enter__(self):
            return self

        def __exit__(self, exc_type, exc, traceback):
            return False

        def read(self, size: int) -> bytes:
            assert size >= len(body)
            return body

    monkeypatch.setattr(
        runtime_readback.urllib.request,
        "urlopen",
        lambda request, timeout: Response(),
    )

    result = runtime_readback._http_probe(
        "https://stock.wooo.work/api/v1/system/ingestion",
        10,
    )

    assert result["http_status"] == 200
    assert json.loads(result["body"])["status"] == "ok"


def _probe_public_web_ok_api_502(url: str, timeout_seconds: float) -> dict:
    del timeout_seconds
    if url.endswith("/healthz") and "/api/" not in url:
        return {"http_status": 200, "body": "ok", "error": ""}
    return {
        "http_status": 502,
        "body": "<html><h1>502 Bad Gateway</h1></html>",
        "error": "",
    }


def _probe_ok(url: str, timeout_seconds: float) -> dict:
    del timeout_seconds
    if url.endswith("/api/v1/system/freshness"):
        return {"http_status": 200, "body": json.dumps({"status": "ok"}), "error": ""}
    if url.endswith("/api/v1/system/ingestion"):
        return {"http_status": 200, "body": json.dumps({"status": "ok"}), "error": ""}
    return {"http_status": 200, "body": "ok", "error": ""}


def _probe_public_api_ok_postgres_not_ready(
    url: str,
    timeout_seconds: float,
) -> dict:
    del timeout_seconds
    if url.endswith("/api/v1/system/freshness"):
        return {
            "http_status": 200,
            "body": json.dumps(
                {"status": "not_configured", "blockers": ["postgres_not_ready"]}
            ),
            "error": "",
        }
    if url.endswith("/api/v1/system/ingestion"):
        return {
            "http_status": 200,
            "body": json.dumps(
                {"status": "not_configured", "blockers": ["postgres_not_ready"]}
            ),
            "error": "",
        }
    return {"http_status": 200, "body": "ok", "error": ""}


def _probe_public_api_ok_data_dependency_blocked(
    url: str,
    timeout_seconds: float,
) -> dict:
    del timeout_seconds
    if url.endswith("/api/v1/system/freshness"):
        return {
            "http_status": 200,
            "body": json.dumps(
                {
                    "status": "blocked",
                    "blockers": [
                        "core_margin_short_daily_missing",
                        "ai_recommendations_stale",
                    ],
                    "latest_trading_date": "2026-07-02",
                    "sources": [
                        {"source": "core.price_daily", "status": "ok", "sla": {}},
                        {
                            "source": "core.margin_short_daily",
                            "status": "missing",
                            "sla": {},
                        },
                        {
                            "source": "ai.recommendations",
                            "status": "stale",
                        },
                    ],
                }
            ),
            "error": "",
        }
    if url.endswith("/api/v1/system/ingestion"):
        return {
            "http_status": 200,
            "body": json.dumps({"status": "unknown", "blockers": []}),
            "error": "",
        }
    return {"http_status": 200, "body": "ok", "error": ""}


def _probe_public_api_ok_ai_recommendations_stale_after_core_recovered(
    url: str,
    timeout_seconds: float,
) -> dict:
    del timeout_seconds
    if url.endswith("/api/v1/system/freshness"):
        return {
            "http_status": 200,
            "body": json.dumps(
                {
                    "status": "blocked",
                    "blockers": ["ai_recommendations_stale"],
                    "latest_trading_date": "2026-07-02",
                    "sources": [
                        {"source": "core.price_daily", "status": "ok", "sla": {}},
                        {
                            "source": "core.margin_short_daily",
                            "latest_date": "2026-07-02",
                            "status": "ok",
                            "sla": {},
                        },
                        {
                            "source": "ai.recommendations",
                            "latest_date": "2026-07-01",
                            "status": "stale",
                            "sla": {},
                        },
                    ],
                }
            ),
            "error": "",
        }
    if url.endswith("/api/v1/system/ingestion"):
        return {
            "http_status": 200,
            "body": json.dumps(
                {
                    "status": "ok",
                    "blockers": [],
                    "latest_trading_date": "2026-07-02",
                }
            ),
            "error": "",
        }
    return {"http_status": 200, "body": "ok", "error": ""}
