import os
from pathlib import Path

import httpx
from fastapi.testclient import TestClient

import app
import db
import routers.models
from security import SESSIONS, PIN_ATTEMPTS, RATE_EVENTS


def make_client(tmp_path: Path) -> TestClient:
    os.environ["CAIC_ADMIN_PIN"] = "1234"
    db.DB_PATH = tmp_path / "caic-models.db"
    SESSIONS.clear()
    PIN_ATTEMPTS.clear()
    RATE_EVENTS.clear()
    db.init_db()
    return TestClient(app.app, raise_server_exceptions=False)


def _guest_headers(client: TestClient) -> dict:
    sid = client.post("/api/auth/guest", headers={"Origin": "http://testserver"}).json()["session_id"]
    return {"X-Session-ID": sid, "Origin": "http://testserver"}


class _MockAsyncResponse:
    """Mock for httpx.AsyncClient.get/post that returns a JSON response."""
    def __init__(self, status_code=200, json_data=None):
        self.status_code = status_code
        self._json_data = json_data or {}

    def json(self):
        return self._json_data


async def _mock_get_models(*args, **kwargs):
    return _MockAsyncResponse(json_data={
        "data": [{"id": "llama3.1:latest"}, {"id": "qwen2:latest"}]
    })


async def _mock_get_empty_models(*args, **kwargs):
    return _MockAsyncResponse(json_data={"data": []})


async def _mock_connect_error(*args, **kwargs):
    raise httpx.ConnectError("Connection refused")


async def _mock_show_model(*args, **kwargs):
    return _MockAsyncResponse(json_data={
        "modelfile": "FROM llama3.1", "parameters": {}
    })


async def _mock_search_available(*args, **kwargs):
    return _MockAsyncResponse(status_code=200)


async def _mock_search_unavailable(*args, **kwargs):
    raise httpx.ConnectError("refused")


def test_list_models(tmp_path: Path, monkeypatch):
    monkeypatch.setattr(httpx.AsyncClient, "get", _mock_get_models)
    with make_client(tmp_path) as client:
        resp = client.get("/api/models", headers=_guest_headers(client))
        assert resp.status_code == 200
        models = resp.json()["models"]
        assert len(models) == 2
        assert models[0]["name"] == "llama3.1:latest"


def test_list_models_connect_error(tmp_path: Path, monkeypatch):
    monkeypatch.setattr(httpx.AsyncClient, "get", _mock_connect_error)
    with make_client(tmp_path) as client:
        resp = client.get("/api/models", headers=_guest_headers(client))
        assert resp.status_code == 502


def test_running_models(tmp_path: Path, monkeypatch):
    monkeypatch.setattr(httpx.AsyncClient, "get", _mock_get_models)
    with make_client(tmp_path) as client:
        resp = client.get("/api/ps", headers=_guest_headers(client))
        assert resp.status_code == 200
        assert "data" in resp.json()


def test_running_models_connect_error(tmp_path: Path, monkeypatch):
    monkeypatch.setattr(httpx.AsyncClient, "get", _mock_connect_error)
    with make_client(tmp_path) as client:
        resp = client.get("/api/ps", headers=_guest_headers(client))
        assert resp.status_code == 502


def test_show_model(tmp_path: Path, monkeypatch):
    monkeypatch.setattr(httpx.AsyncClient, "post", _mock_show_model)
    with make_client(tmp_path) as client:
        resp = client.post("/api/show", json={"model": "llama3.1:latest"}, headers=_guest_headers(client))
        assert resp.status_code == 200
        assert resp.json()["modelfile"] == "FROM llama3.1"


def test_show_model_connect_error(tmp_path: Path, monkeypatch):
    monkeypatch.setattr(httpx.AsyncClient, "post", _mock_connect_error)
    with make_client(tmp_path) as client:
        resp = client.post("/api/show", json={"model": "llama3.1:latest"}, headers=_guest_headers(client))
        assert resp.status_code == 502


def test_system_stats(tmp_path: Path, monkeypatch):
    monkeypatch.setattr(routers.models, "get_gpu_stats", lambda: {"gpu_percent": 15, "vram_percent": 30, "available": True})
    with make_client(tmp_path) as client:
        resp = client.get("/api/stats", headers=_guest_headers(client))
        assert resp.status_code == 200
        data = resp.json()
        assert "cpu_percent" in data
        assert "memory_percent" in data
        assert data["gpu_percent"] == 15
        assert data["gpu_available"] is True


def test_search_status_available(tmp_path: Path, monkeypatch):
    monkeypatch.setattr(httpx.AsyncClient, "get", _mock_search_available)
    with make_client(tmp_path) as client:
        resp = client.get("/api/search/status", headers=_guest_headers(client))
        assert resp.status_code == 200
        assert resp.json()["available"] is True


def test_search_status_unavailable(tmp_path: Path, monkeypatch):
    monkeypatch.setattr(httpx.AsyncClient, "get", _mock_search_unavailable)
    with make_client(tmp_path) as client:
        resp = client.get("/api/search/status", headers=_guest_headers(client))
        assert resp.status_code == 200
        assert resp.json()["available"] is False
