"""
Tests for Ollama (local / self-hosted) provider support.

Two groups:
  1. Cost accounting — pure unit tests, no Ollama needed. Verify local models
     are billed at $0 and the cost source label reflects the real provider.
  2. Live provider — skipped automatically unless an Ollama server is reachable
     at http://127.0.0.1:11434. Run a local model first, e.g.:
         ollama pull qwen2.5:7b && ollama serve
"""

import urllib.request

import pytest

from agentpaas.engine.sandbox import _compute_cost_usd


# ============================================================
# 1. Cost accounting (no Ollama required)
# ============================================================

class TestOllamaCost:

    def test_ollama_costs_zero(self):
        """Local Ollama models incur no per-token API cost."""
        usage = {"input_tokens": 10_000, "output_tokens": 5_000, "provider": "ollama"}
        assert _compute_cost_usd(usage) == 0.0

    def test_claude_still_billed(self):
        """Regression: non-local providers keep their pricing."""
        usage = {"input_tokens": 1_000_000, "output_tokens": 0, "provider": "claude"}
        assert _compute_cost_usd(usage) == pytest.approx(3.00)

    def test_qwen_still_billed(self):
        usage = {"input_tokens": 1_000_000, "output_tokens": 0, "provider": "qwen"}
        assert _compute_cost_usd(usage) == pytest.approx(5.56)


# ============================================================
# 2. Live provider (skipped unless Ollama is up)
# ============================================================

_OLLAMA_URL = "http://127.0.0.1:11434/api/tags"


def _ollama_models():
    """Return the list of locally available Ollama model names, or [] if down."""
    try:
        with urllib.request.urlopen(_OLLAMA_URL, timeout=3) as resp:
            import json
            data = json.loads(resp.read())
        return [m["name"] for m in data.get("models", [])]
    except Exception:
        return []


_MODELS = _ollama_models()
_HAS_OLLAMA = bool(_MODELS)
# Prefer a small model if present, else use whatever is available.
_MODEL = next((m for m in _MODELS if "7b" in m or "3b" in m), _MODELS[0] if _MODELS else "qwen2.5:7b")

ollama_required = pytest.mark.skipif(
    not _HAS_OLLAMA, reason="No Ollama server reachable at 127.0.0.1:11434"
)


@ollama_required
class TestOllamaLive:

    def test_chat_roundtrip(self):
        from lambdagent.providers import create_provider
        p = create_provider("ollama", model=_MODEL, timeout=120)
        assert p.provider_name == "ollama"
        assert p.base_url.startswith("http://127.0.0.1:11434")
        out = p.chat([{"role": "user", "content": "Reply with exactly: OLLAMA_OK"}])
        assert "OLLAMA_OK" in out

    def test_usage_accumulates(self):
        from lambdagent.providers import create_provider, ChatMessage
        p = create_provider("ollama", model=_MODEL, timeout=120)
        r = p.chat_typed([ChatMessage(role="user", content="Say hi.")], max_tokens=16)
        assert r.text
        assert r.input_tokens > 0
        usage = p.get_usage()
        assert usage["provider"] == "ollama"
        assert usage["input_tokens"] > 0

    def test_full_stack_run_costs_zero(self):
        """A simple agent run on Ollama produces artifacts and bills $0."""
        import asyncio, tempfile, os, json, shutil
        from agentpaas.engine.sandbox import Sandbox

        config = {
            "name": "ollama-smoke", "type": "simple",
            "systemPrompt": "You are concise.",
            "model": {"provider": "ollama", "name": _MODEL,
                      "temperature": 0.0, "maxTokens": 64},
        }
        agent_dir = tempfile.mkdtemp(prefix="ollama_test_")
        try:
            sb = Sandbox(level=0)
            r = asyncio.run(sb.execute(
                config, "Reply with exactly: FULLSTACK_OK",
                timeout=120, agent_dir=agent_dir, run_id="run_olltest",
            ))
            assert r.status == "completed", r.error
            ws = r.workspace_path
            for fn in ("output.json", "cost.json", "manifest.json"):
                assert os.path.isfile(os.path.join(ws, fn))
            cost = json.load(open(os.path.join(ws, "cost.json")))
            assert cost["cost_usd"] == 0.0
        finally:
            shutil.rmtree(agent_dir, ignore_errors=True)
