"""Unit tests for preflight and provisioning helpers."""

from __future__ import annotations

import importlib

import pytest

from agent_preflight.preflight import preflight
from agent_preflight.provision import get_available_models, get_available_models_or_empty

preflight_mod = importlib.import_module("agent_preflight.preflight")
provision_mod = importlib.import_module("agent_preflight.provision")


class _FakeResponse:
    def __init__(self, *, status_code=200, json_data=None):
        self.status_code = status_code
        self._json_data = json_data or {}

    def raise_for_status(self):
        if self.status_code >= 400:
            raise RuntimeError(f"http {self.status_code}")

    def json(self):
        return self._json_data


class TestGetAvailableModels:
    def test_returns_empty_dict_for_empty_success(self, monkeypatch):
        def fake_get(*args, **kwargs):
            return _FakeResponse(json_data={"data": []})

        monkeypatch.setattr(provision_mod.httpx, "get", fake_get)
        assert get_available_models("sk-or-test") == {}

    def test_returns_none_on_request_failure(self, monkeypatch):
        def fake_get(*args, **kwargs):
            raise RuntimeError("network down")

        monkeypatch.setattr(provision_mod.httpx, "get", fake_get)
        assert get_available_models("sk-or-test") is None

    def test_returns_none_on_http_error(self, monkeypatch):
        def fake_get(*args, **kwargs):
            return _FakeResponse(status_code=503)

        monkeypatch.setattr(provision_mod.httpx, "get", fake_get)
        assert get_available_models("sk-or-test") is None

    def test_or_empty_helper_returns_empty_dict_on_failure(self, monkeypatch):
        monkeypatch.setattr(provision_mod, "get_available_models", lambda _key: None)
        assert get_available_models_or_empty("sk-or-test") == {}


class TestPreflightToolSupport:
    def test_common_required_params_apply_to_all_models(self, monkeypatch):
        def fake_get_available_models(_or_key):
            return {
                "anthropic/claude-sonnet-4": {
                    "pricing": {"prompt": "0.000003", "completion": "0.000015"},
                    "supported_parameters": ["tools", "temperature"],
                },
                "google/gemini-2.5-flash": {
                    "pricing": {"prompt": "0.000001", "completion": "0.000004"},
                    "supported_parameters": ["temperature"],
                },
            }

        monkeypatch.setattr(preflight_mod, "get_available_models", fake_get_available_models)

        failures = preflight(
            admin_key="admin",
            or_key="or",
            models=["anthropic/claude-sonnet-4", "google/gemini-2.5-flash"],
            budget_per_model=0.01,
            checks=["tool_support"],
            common_required_params=["tools"],
            raise_on_failure=False,
        )

        assert failures == ["Model google/gemini-2.5-flash missing required params: tools"]

    def test_package_level_model_lookup_patch_is_used(self, monkeypatch):
        import agent_preflight as package

        monkeypatch.setattr(
            package,
            "get_available_models",
            lambda _or_key: {
                "anthropic/claude-sonnet-4": {
                    "pricing": {"prompt": "0.000003", "completion": "0.000015"},
                    "supported_parameters": ["tools"],
                },
            },
        )

        failures = preflight(
            admin_key="admin",
            or_key="or",
            models=["anthropic/claude-sonnet-4"],
            budget_per_model=0.01,
            checks=["models", "tool_support"],
            required_params={"anthropic/claude-sonnet-4": ["tools"]},
            raise_on_failure=False,
        )

        assert failures == []

    def test_tool_support_passes(self, monkeypatch):
        calls = {"models": 0}

        def fake_get_available_models(_or_key):
            calls["models"] += 1
            return {
                "anthropic/claude-sonnet-4": {
                    "pricing": {"prompt": "0.000003", "completion": "0.000015"},
                    "supported_parameters": ["tools", "temperature"],
                },
            }

        monkeypatch.setattr(preflight_mod, "get_available_models", fake_get_available_models)

        failures = preflight(
            admin_key="admin",
            or_key="or",
            models=["anthropic/claude-sonnet-4"],
            budget_per_model=0.01,
            checks=["models", "tool_support"],
            required_params={"anthropic/claude-sonnet-4": ["tools"]},
            raise_on_failure=False,
        )

        assert failures == []
        assert calls["models"] == 1

    def test_tool_support_fails_when_required_param_missing(self, monkeypatch):
        def fake_get_available_models(_or_key):
            return {
                "anthropic/claude-sonnet-4": {
                    "pricing": {"prompt": "0.000003", "completion": "0.000015"},
                    "supported_parameters": ["temperature"],
                },
            }

        monkeypatch.setattr(preflight_mod, "get_available_models", fake_get_available_models)

        failures = preflight(
            admin_key="admin",
            or_key="or",
            models=["anthropic/claude-sonnet-4"],
            budget_per_model=0.01,
            checks=["tool_support"],
            required_params={"anthropic/claude-sonnet-4": ["tools"]},
            raise_on_failure=False,
        )

        assert failures == ["Model anthropic/claude-sonnet-4 missing required params: tools"]

    def test_tool_support_reports_fetch_failure(self, monkeypatch):
        monkeypatch.setattr(preflight_mod, "get_available_models", lambda _or_key: None)

        failures = preflight(
            admin_key="admin",
            or_key="or",
            models=["anthropic/claude-sonnet-4"],
            budget_per_model=0.01,
            checks=["tool_support"],
            required_params={"anthropic/claude-sonnet-4": ["tools"]},
            raise_on_failure=False,
        )

        assert failures == ["Could not fetch model list from OpenRouter for parameter support"]

    def test_tool_support_skips_when_no_required_params(self, capsys):
        failures = preflight(
            admin_key="admin",
            or_key="or",
            models=["anthropic/claude-sonnet-4"],
            budget_per_model=0.01,
            checks=["tool_support"],
            raise_on_failure=False,
        )

        out = capsys.readouterr().out
        assert failures == []
        assert "no required_params/common_required_params specified" in out


class TestPreflightCustomChecks:
    def test_zero_arg_custom_check_still_works(self):
        def check():
            return ("Zero-arg check", True, "ok")

        failures = preflight(
            admin_key="admin",
            or_key="or",
            models=["anthropic/claude-sonnet-4"],
            budget_per_model=0.01,
            checks=[],
            custom_checks=[check],
            raise_on_failure=False,
        )

        assert failures == []

    def test_context_custom_check_receives_prefetched_models(self, monkeypatch):
        calls = {"models": 0}

        def fake_get_available_models(_or_key):
            calls["models"] += 1
            return {
                "anthropic/claude-sonnet-4": {
                    "pricing": {"prompt": "0.000003", "completion": "0.000015"},
                    "supported_parameters": ["tools"],
                },
            }

        monkeypatch.setattr(preflight_mod, "get_available_models", fake_get_available_models)

        seen = {}

        def check(ctx):
            seen.update(ctx)
            assert "anthropic/claude-sonnet-4" in ctx["available_models"]
            assert ctx["required_params"] == {}
            return ("Context check", True, "ok")

        failures = preflight(
            admin_key="admin",
            or_key="or",
            models=["anthropic/claude-sonnet-4"],
            budget_per_model=0.01,
            checks=[],
            custom_checks=[check],
            raise_on_failure=False,
        )

        assert failures == []
        assert seen["models"] == ["anthropic/claude-sonnet-4"]
        assert calls["models"] == 1

    def test_context_custom_check_reuses_models_from_builtin_check(self, monkeypatch):
        calls = {"models": 0}

        def fake_get_available_models(_or_key):
            calls["models"] += 1
            return {
                "anthropic/claude-sonnet-4": {
                    "pricing": {"prompt": "0.000003", "completion": "0.000015"},
                    "supported_parameters": ["tools"],
                },
            }

        monkeypatch.setattr(preflight_mod, "get_available_models", fake_get_available_models)

        def check(ctx):
            assert ctx["available_models"]["anthropic/claude-sonnet-4"]["supported_parameters"] == ["tools"]
            return ("Context check", True, "ok")

        failures = preflight(
            admin_key="admin",
            or_key="or",
            models=["anthropic/claude-sonnet-4"],
            budget_per_model=0.01,
            checks=["models", "tool_support"],
            required_params={"anthropic/claude-sonnet-4": ["tools"]},
            custom_checks=[check],
            raise_on_failure=False,
        )

        assert failures == []
        assert calls["models"] == 1
