"""
test_tool_health.py — Smoke tests sprawdzające czy narzędzia i usługi działają.

Uruchom: pytest tests/test_tool_health.py -v
Lub:     make test-smoke
"""
import shutil
import subprocess
import os

import pytest

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

def _is_port_open(host: str, port: int, timeout: float = 2.0) -> bool:
    import socket
    try:
        with socket.create_connection((host, port), timeout=timeout):
            return True
    except (ConnectionRefusedError, OSError, TimeoutError):
        return False


def _http_get(url: str, timeout: float = 3.0):
    """Simple GET — returns (status_code, json | None)."""
    import httpx
    try:
        r = httpx.get(url, timeout=timeout, follow_redirects=True)
        try:
            return r.status_code, r.json()
        except Exception:
            return r.status_code, None
    except httpx.ConnectError:
        return None, None


PROXY_BASE = os.getenv("PROXYM_URL", "http://localhost:4000")
VSCODE_BASE = os.getenv("VSCODE_URL", "http://localhost:8080")
PROXY_KEY = os.getenv("PROXYM_MASTER_KEY", "sk-proxym-changeme")

# ===========================================================================
# 1. Narzędzia lokalne — czy zainstalowane
# ===========================================================================

class TestLocalTools:
    """Sprawdź czy wymagane CLI narzędzia są w PATH."""

    def test_python_version(self):
        result = subprocess.run(
            ["python3", "--version"], capture_output=True, text=True
        )
        assert result.returncode == 0
        version = result.stdout.strip()
        major, minor = version.split()[1].split(".")[:2]
        assert int(major) >= 3 and int(minor) >= 11, (
            f"Python >= 3.11 required, got {version}"
        )

    def test_docker_available(self):
        assert shutil.which("docker"), "docker not in PATH"
        result = subprocess.run(
            ["docker", "info"], capture_output=True, timeout=10
        )
        assert result.returncode == 0, "docker daemon not running"

    def test_docker_compose_available(self):
        result = subprocess.run(
            ["docker", "compose", "version"],
            capture_output=True, timeout=10,
        )
        assert result.returncode == 0, "docker compose not available"

    def test_git_available(self):
        assert shutil.which("git"), "git not in PATH"

    def test_ruff_available(self):
        assert shutil.which("ruff"), "ruff not in PATH — pip install ruff"

    @pytest.mark.skipif(
        not shutil.which("code2llm"),
        reason="code2llm not installed (optional)"
    )
    def test_code2llm_version(self):
        result = subprocess.run(
            ["code2llm", "-h"], capture_output=True, text=True
        )
        assert result.returncode == 0

    @pytest.mark.skipif(
        not shutil.which("aider"),
        reason="aider not installed (optional)"
    )
    def test_aider_available(self):
        result = subprocess.run(
            ["aider", "--version"], capture_output=True, text=True, timeout=10
        )
        assert result.returncode == 0

    @pytest.mark.skipif(
        not shutil.which("claude"),
        reason="claude-code not installed (optional)"
    )
    def test_claude_code_available(self):
        result = subprocess.run(
            ["claude", "--version"], capture_output=True, text=True, timeout=10
        )
        assert result.returncode == 0


# ===========================================================================
# 2. Proxy — czy nasłuchuje i odpowiada
# ===========================================================================

class TestProxyRunning:
    """Sprawdź czy proxym proxy działa."""

    @pytest.fixture(autouse=True)
    def _require_proxy(self):
        if not _is_port_open("localhost", 4000):
            pytest.skip("Proxy not running on :4000 — start with: make up")

    def test_health_ok(self):
        code, data = _http_get(f"{PROXY_BASE}/health")
        assert code == 200
        assert data and data.get("status") == "ok"

    def test_models_registered(self):
        code, data = _http_get(f"{PROXY_BASE}/v1/models")
        assert code == 200
        models = data.get("data", [])
        assert len(models) >= 5, (
            f"Only {len(models)} models — check provider keys in .env"
        )

    def test_budget_endpoint(self):
        code, data = _http_get(f"{PROXY_BASE}/v1/budget")
        assert code == 200
        assert "daily" in str(data) or "remaining" in str(data)

    def test_dashboard_html_loads(self):
        import httpx
        r = httpx.get(f"{PROXY_BASE}/dashboard", follow_redirects=True, timeout=5)
        assert r.status_code == 200
        assert "text/html" in r.headers.get("content-type", "")

    def test_dashboard_jsx_loads(self):
        code, _ = _http_get(f"{PROXY_BASE}/dashboard/panel.jsx")
        assert code == 200

    def test_tickets_endpoint(self):
        code, data = _http_get(f"{PROXY_BASE}/dashboard/tickets")
        assert code == 200
        assert isinstance(data, list)

    def test_diagnostics_status(self):
        code, data = _http_get(f"{PROXY_BASE}/tests/status")
        assert code == 200
        # Powinien zwrócić status usług
        assert isinstance(data, dict)


# ===========================================================================
# 3. VS Code Web — czy kontener działa
# ===========================================================================

class TestVSCodeWebRunning:
    """Sprawdź czy VS Code Web odpowiada."""

    @pytest.fixture(autouse=True)
    def _require_vscode(self):
        if not _is_port_open("localhost", 8080):
            pytest.skip("VS Code Web not running on :8080")

    def test_login_page_loads(self):
        import httpx
        r = httpx.get(VSCODE_BASE, follow_redirects=True, timeout=10)
        assert r.status_code == 200

    def test_healthz(self):
        code, _ = _http_get(f"{VSCODE_BASE}/healthz")
        # code-server zwraca 200 na /healthz
        assert code == 200


# ===========================================================================
# 4. Providery — czy skonfigurowane
# ===========================================================================

class TestProviderConfig:
    """Sprawdź czy co najmniej jeden provider jest skonfigurowany."""

    @pytest.fixture(autouse=True)
    def _require_proxy(self):
        if not _is_port_open("localhost", 4000):
            pytest.skip("Proxy not running")

    def test_at_least_one_provider(self):
        code, data = _http_get(f"{PROXY_BASE}/dashboard/system-status")
        assert code == 200
        providers = data.get("providers", {})
        configured = [
            name for name, info in providers.items()
            if info.get("configured")
        ]
        assert len(configured) >= 1, (
            "No providers configured. Add at least one API key to .env:\n"
            "  ANTHROPIC_API_KEY=sk-ant-...\n"
            "  OPENROUTER_API_KEY=sk-or-...\n"
            "  GOOGLE_API_KEY=..."
        )

    def test_routing_dry_run(self):
        """Sprawdź czy proxy potrafi routować request."""
        code, data = _http_get(f"{PROXY_BASE}/tests/proxy")
        assert code == 200


# ===========================================================================
# 5. Docker containers — status
# ===========================================================================

class TestDockerContainers:
    """Sprawdź status kontenerów Docker."""

    @pytest.fixture(autouse=True)
    def _require_docker(self):
        if not shutil.which("docker"):
            pytest.skip("docker not available")

    def _running_containers(self) -> list[str]:
        result = subprocess.run(
            ["docker", "ps", "--format", "{{.Names}}"],
            capture_output=True, text=True, timeout=10,
        )
        return result.stdout.strip().split("\n") if result.stdout.strip() else []

    def test_proxy_container_running(self):
        containers = self._running_containers()
        proxy = [c for c in containers if "proxym" in c.lower()]
        if not proxy:
            pytest.skip("proxym container not running (may be running natively)")

    def test_vscode_container_running(self):
        containers = self._running_containers()
        vscode = [c for c in containers if "vscode" in c.lower() or "code-server" in c.lower()]
        if not vscode:
            pytest.skip("vscode container not running")


# ===========================================================================
# 6. Chat — czy LLM odpowiada
# ===========================================================================

class TestChatCompletion:
    """Sprawdź czy chat completion działa end-to-end."""

    @pytest.fixture(autouse=True)
    def _require_proxy(self):
        if not _is_port_open("localhost", 4000):
            pytest.skip("Proxy not running")

    def test_simple_chat(self):
        import httpx
        r = httpx.post(
            f"{PROXY_BASE}/v1/chat/completions",
            json={
                "model": "cheap",  # alias → Haiku
                "messages": [{"role": "user", "content": "Say hello"}],
                "max_tokens": 20,
            },
            headers={"Authorization": f"Bearer {PROXY_KEY}"},
            timeout=30,
        )
        assert r.status_code == 200, f"Chat failed: {r.text}"
        data = r.json()
        assert data.get("choices")
        assert len(data["choices"][0]["message"]["content"]) > 0


# ===========================================================================
# 7. Projekty — czy folder jest widoczny
# ===========================================================================

class TestProjectListing:
    """Sprawdź czy endpoint listowania projektów działa."""

    @pytest.fixture(autouse=True)
    def _require_proxy(self):
        if not _is_port_open("localhost", 4000):
            pytest.skip("Proxy not running")

    def test_projects_endpoint_exists(self):
        code, data = _http_get(f"{PROXY_BASE}/dashboard/projects")
        if code == 404:
            pytest.fail(
                "GET /dashboard/projects not implemented!\n"
                "Add endpoint to list projects from VSCODE_PROJECT_PATH"
            )
        assert code == 200

    def test_projects_returns_list(self):
        code, data = _http_get(f"{PROXY_BASE}/dashboard/projects")
        if code == 404:
            pytest.skip("projects endpoint not implemented yet")
        assert "projects" in data
        assert isinstance(data["projects"], list)


# ===========================================================================
# 8. Tickets & Sprints — CRUD
# ===========================================================================

class TestTicketSystem:
    """Sprawdź system ticketów i sprintów."""

    @pytest.fixture(autouse=True)
    def _require_proxy(self):
        if not _is_port_open("localhost", 4000):
            pytest.skip("Proxy not running")

    def test_list_tickets(self):
        code, data = _http_get(f"{PROXY_BASE}/dashboard/tickets")
        assert code == 200
        assert isinstance(data, list)

    def test_create_and_get_ticket(self):
        import httpx
        # Create
        r = httpx.post(
            f"{PROXY_BASE}/dashboard/tickets",
            json={
                "title": "Smoke test ticket",
                "type": "test",
                "priority": "low",
                "tags": ["smoke-test"],
            },
            headers={"X-Api-Key": PROXY_KEY},
            timeout=5,
        )
        assert r.status_code in (200, 201), f"Create failed: {r.text}"
        ticket = r.json()
        tid = ticket["id"]

        # Get
        code, data = _http_get(f"{PROXY_BASE}/dashboard/tickets/{tid}")
        assert code == 200
        assert data["title"] == "Smoke test ticket"

    def test_assign_tool_to_ticket(self):
        import httpx
        # Create ticket
        r = httpx.post(
            f"{PROXY_BASE}/dashboard/tickets",
            json={"title": "Tool assign test", "type": "test", "priority": "low"},
            headers={"X-Api-Key": PROXY_KEY},
            timeout=5,
        )
        tid = r.json()["id"]

        # Assign tool
        r2 = httpx.post(
            f"{PROXY_BASE}/dashboard/tickets/{tid}/assign-tool",
            json={"tool": "vscode"},
            headers={"X-Api-Key": PROXY_KEY},
            timeout=5,
        )
        assert r2.status_code == 200

    def test_board_view(self):
        code, data = _http_get(f"{PROXY_BASE}/dashboard/board")
        assert code == 200
        assert isinstance(data, dict)
