from __future__ import annotations

from types import SimpleNamespace
from typing import Any

import httpx
import pytest

from src.services import local_code_review_service as review_module
from src.services.local_code_review_service import LocalCodeReviewService


class _FakeResponse:
    status_code = 200

    def json(self) -> dict[str, str]:
        return {"response": "✅ Push 品質正常"}


class _FakeClient:
    def __init__(self, *, fail: bool = False) -> None:
        self.fail = fail
        self.posted_urls: list[str] = []

    async def post(self, url: str, **kwargs: Any) -> _FakeResponse:
        self.posted_urls.append(url)
        if self.fail:
            raise httpx.TimeoutException("timeout")
        return _FakeResponse()


async def _noop_save(*args: Any, **kwargs: Any) -> None:
    return None


def _fake_ollama_order(_workload_type: str) -> tuple[SimpleNamespace, ...]:
    return (
        SimpleNamespace(url="http://gcp-a:11434", provider_name="ollama_gcp_a"),
        SimpleNamespace(url="http://gcp-b:11434", provider_name="ollama_gcp_b"),
        SimpleNamespace(url="http://local-111:11434", provider_name="ollama_local"),
    )


@pytest.mark.asyncio
async def test_large_pr_uses_local_ollama_when_gemini_fallback_disabled(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    monkeypatch.setattr(
        review_module.settings,
        "LOCAL_CODE_REVIEW_ALLOW_GEMINI_FALLBACK",
        False,
    )
    monkeypatch.setattr(
        review_module,
        "resolve_ollama_order",
        _fake_ollama_order,
    )

    client = _FakeClient()
    service = LocalCodeReviewService()

    async def _get_http() -> _FakeClient:
        return client

    async def _fail_gemini(*args: Any, **kwargs: Any) -> None:
        raise AssertionError("Gemini fallback should stay disabled")

    monkeypatch.setattr(service, "_get_http", _get_http)
    monkeypatch.setattr(service, "_save_to_db", _noop_save)
    monkeypatch.setattr(service, "_review_with_gemini", _fail_gemini)

    result = await service.review_pr(
        pr_id="pr-1",
        repo="wooo/awoooi",
        title="large diff",
        diff="x" * (60 * 1024),
    )

    assert result is not None
    assert result["provider"] == "ollama_gcp_a"
    assert client.posted_urls == ["http://gcp-a:11434/api/generate"]


@pytest.mark.asyncio
async def test_ollama_failure_does_not_fall_back_to_gemini_by_default(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    monkeypatch.setattr(
        review_module.settings,
        "LOCAL_CODE_REVIEW_ALLOW_GEMINI_FALLBACK",
        False,
    )
    monkeypatch.setattr(
        review_module,
        "resolve_ollama_order",
        _fake_ollama_order,
    )

    client = _FakeClient(fail=True)
    service = LocalCodeReviewService()

    async def _get_http() -> _FakeClient:
        return client

    async def _fail_gemini(*args: Any, **kwargs: Any) -> None:
        raise AssertionError("Gemini fallback should stay disabled")

    monkeypatch.setattr(service, "_get_http", _get_http)
    monkeypatch.setattr(service, "_save_to_db", _noop_save)
    monkeypatch.setattr(service, "_review_with_gemini", _fail_gemini)

    result = await service.review_pr(
        pr_id="pr-2",
        repo="wooo/awoooi",
        title="ollama unavailable",
        diff="small diff",
    )

    assert result is not None
    assert result["provider"] == "ollama_unavailable"
    assert result["cloud_fallback_skipped"] is True
    assert client.posted_urls == [
        "http://gcp-a:11434/api/generate",
        "http://gcp-b:11434/api/generate",
        "http://local-111:11434/api/generate",
    ]


@pytest.mark.asyncio
async def test_gemini_fallback_requires_explicit_flag(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    monkeypatch.setattr(
        review_module.settings,
        "LOCAL_CODE_REVIEW_ALLOW_GEMINI_FALLBACK",
        True,
    )

    service = LocalCodeReviewService()

    async def _gemini_result(*args: Any, **kwargs: Any) -> dict[str, Any]:
        return {"review_text": "ok", "issues_count": 0, "model": "gemini", "provider": "gemini"}

    monkeypatch.setattr(service, "_save_to_db", _noop_save)
    monkeypatch.setattr(service, "_review_with_gemini", _gemini_result)

    result = await service.review_pr(
        pr_id="pr-3",
        repo="wooo/awoooi",
        title="large diff explicit cloud",
        diff="x" * (60 * 1024),
    )

    assert result is not None
    assert result["provider"] == "gemini"
