"""
BIBIVIDI — Dashboard API endpoint tests
Tests for /bibividi/api/* routes in bvd/core/api.py.
"""
from __future__ import annotations

from unittest.mock import patch

import pytest
from httpx import ASGITransport, AsyncClient

from bvd.core.proxy import app
from bvd.engine.library import Library

# ── Fixture ───────────────────────────────────────────────────────────────────

@pytest.fixture
async def lib(tmp_path):
    """Initialised Library, also patched into the API router singleton."""
    instance = Library()
    with (
        patch("bvd.config.settings.db_path", tmp_path / "api_test.db"),
        patch("bvd.core.api.library", instance),
    ):
        await instance.init()
        yield instance
        await instance.close()


# ── /bibividi/api/stats ───────────────────────────────────────────────────────

@pytest.mark.asyncio
async def test_get_stats_empty(lib):
    """Fresh DB returns all-zero stats."""
    async with AsyncClient(
        transport=ASGITransport(app=app), base_url="http://test"
    ) as client:
        resp = await client.get("/bibividi/api/stats")

    assert resp.status_code == 200
    data = resp.json()
    assert data["total_calls"] == 0
    assert data["tokens_saved"] == 0
    assert "estimated_usd_saved" in data


@pytest.mark.asyncio
async def test_get_stats_includes_usd_estimate(lib):
    """Stats response always includes estimated_usd_saved."""
    async with AsyncClient(
        transport=ASGITransport(app=app), base_url="http://test"
    ) as client:
        resp = await client.get("/bibividi/api/stats")

    assert "estimated_usd_saved" in resp.json()


# ── /bibividi/api/scripts ─────────────────────────────────────────────────────

@pytest.mark.asyncio
async def test_get_scripts_empty(lib):
    """Empty library returns empty list."""
    async with AsyncClient(
        transport=ASGITransport(app=app), base_url="http://test"
    ) as client:
        resp = await client.get("/bibividi/api/scripts")

    assert resp.status_code == 200
    assert resp.json() == []


@pytest.mark.asyncio
async def test_get_scripts_returns_active_only(lib):
    """Pending skills are excluded from /scripts."""
    code = 'print("x")'
    await lib.import_bss({"id": "active-1", "name": "A", "description": "d"}, code)
    await lib.import_bss(
        {"id": "pending-1", "name": "P", "description": "d"}, code, status="pending"
    )

    async with AsyncClient(
        transport=ASGITransport(app=app), base_url="http://test"
    ) as client:
        resp = await client.get("/bibividi/api/scripts")

    ids = [s["id"] for s in resp.json()]
    assert "active-1" in ids
    assert "pending-1" not in ids


@pytest.mark.asyncio
async def test_get_scripts_no_code_field(lib):
    """Scripts list does not expose the code field."""
    await lib.import_bss({"id": "s1", "name": "S1", "description": "d"}, 'print("x")')

    async with AsyncClient(
        transport=ASGITransport(app=app), base_url="http://test"
    ) as client:
        resp = await client.get("/bibividi/api/scripts")

    assert "code" not in resp.json()[0]


# ── /bibividi/api/pending ─────────────────────────────────────────────────────

@pytest.mark.asyncio
async def test_get_pending(lib):
    """Returns pending skills with the code field for review."""
    await lib.import_bss(
        {"id": "pending-2", "name": "P2", "description": "d"},
        'print("secret")',
        status="pending",
    )

    async with AsyncClient(
        transport=ASGITransport(app=app), base_url="http://test"
    ) as client:
        resp = await client.get("/bibividi/api/pending")

    data = resp.json()
    assert any(s["id"] == "pending-2" for s in data)
    assert "code" in data[0]


# ── /bibividi/api/pending/{id}/approve ────────────────────────────────────────

@pytest.mark.asyncio
async def test_approve_skill(lib):
    """POST approve activates a pending skill."""
    await lib.import_bss(
        {"id": "to-approve", "name": "TA", "description": "d"},
        'print("ok")',
        status="pending",
    )

    async with AsyncClient(
        transport=ASGITransport(app=app), base_url="http://test"
    ) as client:
        resp = await client.post("/bibividi/api/pending/to-approve/approve")

    assert resp.status_code == 200
    assert resp.json()["ok"] is True
    script = await lib.get("to-approve")
    assert script is not None
    assert script.status == "active"


@pytest.mark.asyncio
async def test_approve_nonexistent_returns_404(lib):
    """Approving a non-existent skill returns 404."""
    async with AsyncClient(
        transport=ASGITransport(app=app), base_url="http://test"
    ) as client:
        resp = await client.post("/bibividi/api/pending/ghost-skill/approve")

    assert resp.status_code == 404


# ── /bibividi/api/pending/{id}/reject ─────────────────────────────────────────

@pytest.mark.asyncio
async def test_reject_skill(lib):
    """POST reject deletes a pending skill."""
    await lib.import_bss(
        {"id": "to-reject", "name": "TR", "description": "d"},
        'print("nope")',
        status="pending",
    )

    async with AsyncClient(
        transport=ASGITransport(app=app), base_url="http://test"
    ) as client:
        resp = await client.post("/bibividi/api/pending/to-reject/reject")

    assert resp.status_code == 200
    assert resp.json()["ok"] is True
    script = await lib.get("to-reject")
    assert script is None


@pytest.mark.asyncio
async def test_reject_nonexistent_returns_404(lib):
    """Rejecting a non-existent skill returns 404."""
    async with AsyncClient(
        transport=ASGITransport(app=app), base_url="http://test"
    ) as client:
        resp = await client.post("/bibividi/api/pending/ghost-skill/reject")

    assert resp.status_code == 404
