from __future__ import annotations

import importlib.util
import io
import json
import sys
import urllib.error
from datetime import datetime, timezone
from types import SimpleNamespace
from pathlib import Path


SCRIPT_PATH = (
    Path(__file__).resolve().parents[3]
    / "scripts"
    / "awooop_source_correlation_apply_smoke.py"
)
SPEC = importlib.util.spec_from_file_location(
    "awooop_source_correlation_apply_smoke",
    SCRIPT_PATH,
)
awooop_source_correlation_apply_smoke = importlib.util.module_from_spec(SPEC)
assert SPEC and SPEC.loader
sys.dont_write_bytecode = True
sys.modules[SPEC.name] = awooop_source_correlation_apply_smoke
SPEC.loader.exec_module(awooop_source_correlation_apply_smoke)


class _JsonResponse:
    def __init__(self, payload: dict[str, object]) -> None:
        self._payload = payload

    def __enter__(self) -> "_JsonResponse":
        return self

    def __exit__(self, *_: object) -> None:
        return None

    def read(self) -> bytes:
        return json.dumps(self._payload).encode("utf-8")


def test_failed_check_summary_lists_preflight_failures() -> None:
    payload = {
        "checks": [
            {
                "name": "source_review_work_item",
                "passed": True,
                "detail": "source_correlation_review",
            },
            {
                "name": "accepted_review_recorded",
                "passed": False,
                "detail": "missing",
            },
            {
                "name": "target_incident_present",
                "passed": False,
                "detail": "missing target_incident_id",
            },
        ],
    }

    assert awooop_source_correlation_apply_smoke._failed_check_summary(payload) == (
        "accepted_review_recorded=missing, "
        "target_incident_present=missing target_incident_id"
    )


def test_source_review_readback_state_detects_accepted_review() -> None:
    recurrence = {
        "items": [
            {
                "work_item": {
                    "kind": "source_correlation_review",
                    "work_item_id": (
                        "source-evidence:sentry:upstream_canary:"
                        "awoooi-source-link-canary-gitea-cd-4294-1"
                    ),
                },
                "source_correlation_review": {
                    "work_item_id": (
                        "source-evidence:sentry:upstream_canary:"
                        "awoooi-source-link-canary-gitea-cd-4294-1"
                    ),
                    "decision": "accepted",
                    "review_id": "review-1",
                },
            }
        ],
    }

    state = awooop_source_correlation_apply_smoke._source_review_readback_state(
        recurrence,
        work_item_id=(
            "source-evidence:sentry:upstream_canary:"
            "awoooi-source-link-canary-gitea-cd-4294-1"
        ),
    )

    assert state == {
        "found": True,
        "decision": "accepted",
        "review_id": "review-1",
    }


def test_source_review_readback_state_reports_missing_review() -> None:
    recurrence = {
        "items": [
            {
                "work_item": {
                    "kind": "source_correlation_review",
                    "work_item_id": "source-evidence:sentry:upstream_canary:item-1",
                },
                "source_correlation_review": None,
            }
        ],
    }

    state = awooop_source_correlation_apply_smoke._source_review_readback_state(
        recurrence,
        work_item_id="source-evidence:sentry:upstream_canary:item-1",
    )

    assert state == {
        "found": True,
        "decision": "missing",
        "review_id": None,
    }


def test_wait_for_review_readback_retries_until_accepted(monkeypatch) -> None:
    work_item_id = "source-evidence:sentry:upstream_canary:item-1"
    calls: list[str] = []
    recurrences = [
        {
            "items": [
                {
                    "work_item": {
                        "kind": "source_correlation_review",
                        "work_item_id": work_item_id,
                    },
                    "source_correlation_review": None,
                }
            ],
        },
        {
            "items": [
                {
                    "work_item": {
                        "kind": "source_correlation_review",
                        "work_item_id": work_item_id,
                    },
                    "source_correlation_review": {
                        "work_item_id": work_item_id,
                        "decision": "accepted",
                        "review_id": "review-2",
                    },
                }
            ],
        },
    ]

    def fake_http_json(url: str, **_: object) -> dict[str, object]:
        calls.append(url)
        return recurrences[min(len(calls) - 1, len(recurrences) - 1)]

    monkeypatch.setattr(awooop_source_correlation_apply_smoke, "_http_json", fake_http_json)
    monkeypatch.setattr(awooop_source_correlation_apply_smoke.time, "sleep", lambda _: None)

    state = awooop_source_correlation_apply_smoke._wait_for_review_readback(
        args=SimpleNamespace(
            api_url="https://awoooi.wooo.work",
            project_id="awoooi",
            provider="sentry",
            limit=300,
            review_readback_attempts=2,
            review_readback_interval_seconds=0,
        ),
        work_item_id=work_item_id,
    )

    assert state == {
        "found": True,
        "decision": "accepted",
        "review_id": "review-2",
    }
    assert len(calls) == 2


def test_http_json_retries_transient_get_502(monkeypatch) -> None:
    calls: list[str] = []

    def fake_urlopen(request: object, *, timeout: int) -> _JsonResponse:
        calls.append(str(getattr(request, "full_url", "")))
        if len(calls) == 1:
            raise urllib.error.HTTPError(
                url="https://awoooi.wooo.work/api",
                code=502,
                msg="Bad Gateway",
                hdrs=None,
                fp=io.BytesIO(b"<html>bad gateway</html>"),
            )
        return _JsonResponse({"ok": True})

    monkeypatch.setattr(
        awooop_source_correlation_apply_smoke.urllib.request,
        "urlopen",
        fake_urlopen,
    )
    monkeypatch.setattr(awooop_source_correlation_apply_smoke.time, "sleep", lambda _: None)

    assert awooop_source_correlation_apply_smoke._http_json(
        "https://awoooi.wooo.work/api",
    ) == {"ok": True}
    assert len(calls) == 2


def test_http_json_does_not_retry_post_502(monkeypatch) -> None:
    calls: list[str] = []

    def fake_urlopen(request: object, *, timeout: int) -> _JsonResponse:
        calls.append(str(getattr(request, "full_url", "")))
        raise urllib.error.HTTPError(
            url="https://awoooi.wooo.work/api",
            code=502,
            msg="Bad Gateway",
            hdrs=None,
            fp=io.BytesIO(b"<html>bad gateway</html>"),
        )

    monkeypatch.setattr(
        awooop_source_correlation_apply_smoke.urllib.request,
        "urlopen",
        fake_urlopen,
    )

    try:
        awooop_source_correlation_apply_smoke._http_json(
            "https://awoooi.wooo.work/api",
            method="POST",
            payload={"write": True},
        )
    except awooop_source_correlation_apply_smoke.SmokeError as exc:
        assert "HTTP 502" in str(exc)
    else:
        raise AssertionError("POST 502 should fail without retry")
    assert len(calls) == 1


def test_run_uses_fresh_existing_apply_and_keeps_current_canary_as_refresh_candidate(
    monkeypatch,
) -> None:
    current_work_item_id = "source-evidence:sentry:upstream_canary:gitea-cd-4299-1"
    current_provider_event_id = "sentry:upstream_canary:gitea-cd-4299-1"
    calls: list[tuple[str, str]] = []

    recurrence = {
        "items": [
            {
                "work_item": {
                    "kind": "source_correlation_review",
                    "work_item_id": current_work_item_id,
                },
                "latest_provider_event_id": current_provider_event_id,
                "alertname": "AwoooPSourceLinkCanary",
                "source_correlation_review": None,
                "source_correlation_apply": None,
            }
        ],
    }
    status_chain = {
        "source_refs": {
            "correlation": {
                "verification_status": "applied_link_verified",
                "applied_link_total": 3,
                "latest_applied_link_at": datetime.now(timezone.utc).isoformat(),
                "top_candidates": [
                    {
                        "link_state": "applied",
                        "provider_event_id": "sentry:source_correlation_linked:old",
                    }
                ],
            }
        }
    }

    def fake_http_json(url: str, *, method: str = "GET", **_: object) -> dict[str, object]:
        calls.append((method, url))
        if method != "GET":
            raise AssertionError("fresh existing apply should not POST review/apply")
        if "status-chain" in url:
            return status_chain
        return recurrence

    monkeypatch.setattr(awooop_source_correlation_apply_smoke, "_http_json", fake_http_json)

    result = awooop_source_correlation_apply_smoke.run(
        awooop_source_correlation_apply_smoke.parse_args(
            [
                "--api-url",
                "https://awoooi.wooo.work",
                "--target-incident-id",
                "INC-20260505-25E744",
                "--work-item-id",
                current_work_item_id,
                "--expected-source-event-provider-event-id",
                "sentry:source_correlation_linked:gitea-cd-4299-1",
                "--allow-existing-apply",
                "--refresh-if-stale-days",
                "6",
                "--verify-refresh-candidate",
            ]
        )
    )

    assert result["status"] == "already_applied"
    assert result["refresh_candidate_work_item_id"] == current_work_item_id
    assert result["refresh_candidate_latest_provider_event_id"] == current_provider_event_id
    assert [method for method, _ in calls] == ["GET", "GET"]
