"""Tests for MCP server storage CRUD operations."""

from __future__ import annotations

import uuid
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from turnstone.core.storage._sqlite import SQLiteBackend


def _make_id() -> str:
    return uuid.uuid4().hex


class TestMcpServerStorage:
    def test_create_and_get(self, db: SQLiteBackend) -> None:
        sid = _make_id()
        db.create_mcp_server(
            server_id=sid,
            name="test-server",
            transport="stdio",
            command="echo",
            args='["hello"]',
        )
        s = db.get_mcp_server(sid)
        assert s is not None
        assert s["name"] == "test-server"
        assert s["transport"] == "stdio"
        assert s["command"] == "echo"
        assert s["args"] == '["hello"]'
        assert s["enabled"] is True
        assert s["auto_approve"] is False

    def test_get_by_name(self, db: SQLiteBackend) -> None:
        sid = _make_id()
        db.create_mcp_server(server_id=sid, name="named-srv", transport="stdio")
        s = db.get_mcp_server_by_name("named-srv")
        assert s is not None
        assert s["server_id"] == sid

    def test_get_by_name_not_found(self, db: SQLiteBackend) -> None:
        assert db.get_mcp_server_by_name("nope") is None

    def test_get_not_found(self, db: SQLiteBackend) -> None:
        assert db.get_mcp_server("nonexistent") is None

    def test_list_empty(self, db: SQLiteBackend) -> None:
        assert db.list_mcp_servers() == []

    def test_list_all(self, db: SQLiteBackend) -> None:
        db.create_mcp_server(server_id=_make_id(), name="alpha", transport="stdio")
        db.create_mcp_server(
            server_id=_make_id(), name="beta", transport="streamable-http", url="http://x"
        )
        servers = db.list_mcp_servers()
        assert len(servers) == 2
        assert servers[0]["name"] == "alpha"  # ordered by name
        assert servers[1]["name"] == "beta"

    def test_list_enabled_only(self, db: SQLiteBackend) -> None:
        sid1 = _make_id()
        sid2 = _make_id()
        db.create_mcp_server(server_id=sid1, name="enabled-srv", transport="stdio", enabled=True)
        db.create_mcp_server(server_id=sid2, name="disabled-srv", transport="stdio", enabled=False)
        enabled = db.list_mcp_servers(enabled_only=True)
        assert len(enabled) == 1
        assert enabled[0]["name"] == "enabled-srv"

    def test_update_basic_fields(self, db: SQLiteBackend) -> None:
        sid = _make_id()
        db.create_mcp_server(server_id=sid, name="orig", transport="stdio", command="echo")
        ok = db.update_mcp_server(sid, name="renamed", command="cat")
        assert ok is True
        s = db.get_mcp_server(sid)
        assert s is not None
        assert s["name"] == "renamed"
        assert s["command"] == "cat"

    def test_update_boolean_conversion(self, db: SQLiteBackend) -> None:
        sid = _make_id()
        db.create_mcp_server(server_id=sid, name="booltest", transport="stdio")
        db.update_mcp_server(sid, auto_approve=True, enabled=False)
        s = db.get_mcp_server(sid)
        assert s is not None
        assert s["auto_approve"] is True
        assert s["enabled"] is False

    def test_update_not_found(self, db: SQLiteBackend) -> None:
        ok = db.update_mcp_server("nonexistent", name="x")
        assert ok is False

    def test_update_ignores_disallowed_fields(self, db: SQLiteBackend) -> None:
        sid = _make_id()
        db.create_mcp_server(server_id=sid, name="guard", transport="stdio", created_by="admin")
        original = db.get_mcp_server(sid)
        assert original is not None
        original_created = original["created"]
        # created_by and created are not in the mutable allowlist
        db.update_mcp_server(sid, created_by="evil", created="2000-01-01T00:00:00")
        s = db.get_mcp_server(sid)
        assert s is not None
        assert s["created_by"] == "admin"  # unchanged
        assert s["created"] == original_created  # unchanged

    def test_delete(self, db: SQLiteBackend) -> None:
        sid = _make_id()
        db.create_mcp_server(server_id=sid, name="delme", transport="stdio")
        ok = db.delete_mcp_server(sid)
        assert ok is True
        assert db.get_mcp_server(sid) is None

    def test_delete_not_found(self, db: SQLiteBackend) -> None:
        ok = db.delete_mcp_server("nonexistent")
        assert ok is False

    def test_create_duplicate_name(self, db: SQLiteBackend) -> None:
        db.create_mcp_server(server_id=_make_id(), name="unique", transport="stdio")
        # Second create with same name but different ID should be no-op (OR IGNORE)
        sid2 = _make_id()
        db.create_mcp_server(server_id=sid2, name="unique", transport="stdio")
        # OR IGNORE silently drops the conflicting insert
        assert db.get_mcp_server(sid2) is None

    def test_create_idempotent_same_id(self, db: SQLiteBackend) -> None:
        sid = _make_id()
        db.create_mcp_server(server_id=sid, name="idem", transport="stdio", command="v1")
        db.create_mcp_server(server_id=sid, name="idem", transport="stdio", command="v2")
        s = db.get_mcp_server(sid)
        assert s is not None
        assert s["command"] == "v1"  # original preserved, second ignored

    def test_http_transport_fields(self, db: SQLiteBackend) -> None:
        sid = _make_id()
        db.create_mcp_server(
            server_id=sid,
            name="http-srv",
            transport="streamable-http",
            url="https://example.com/mcp",
            headers='{"Authorization":"Bearer xyz"}',
        )
        s = db.get_mcp_server(sid)
        assert s is not None
        assert s["transport"] == "streamable-http"
        assert s["url"] == "https://example.com/mcp"
        assert "Authorization" in s["headers"]

    # -- Registry columns -------------------------------------------------------

    def test_create_with_registry_columns(self, db: SQLiteBackend) -> None:
        sid = _make_id()
        db.create_mcp_server(
            server_id=sid,
            name="reg-server",
            transport="streamable-http",
            url="https://example.com/mcp",
            registry_name="io.example/mcp-server",
            registry_version="1.0.0",
            registry_meta='{"description":"test"}',
        )
        s = db.get_mcp_server(sid)
        assert s is not None
        assert s["registry_name"] == "io.example/mcp-server"
        assert s["registry_version"] == "1.0.0"
        assert s["registry_meta"] == '{"description":"test"}'

    def test_create_without_registry_columns(self, db: SQLiteBackend) -> None:
        """Non-registry servers should have None/empty defaults."""
        sid = _make_id()
        db.create_mcp_server(server_id=sid, name="plain", transport="stdio")
        s = db.get_mcp_server(sid)
        assert s is not None
        assert s["registry_name"] is None
        assert s["registry_version"] == ""
        assert s["registry_meta"] == "{}"

    def test_get_by_registry_name(self, db: SQLiteBackend) -> None:
        sid = _make_id()
        db.create_mcp_server(
            server_id=sid,
            name="reg-test",
            transport="stdio",
            registry_name="io.example/test",
        )
        s = db.get_mcp_server_by_registry_name("io.example/test")
        assert s is not None
        assert s["server_id"] == sid

    def test_get_by_registry_name_not_found(self, db: SQLiteBackend) -> None:
        assert db.get_mcp_server_by_registry_name("nonexistent") is None

    def test_null_registry_name_no_conflict(self, db: SQLiteBackend) -> None:
        """Multiple servers with NULL registry_name should coexist."""
        db.create_mcp_server(server_id=_make_id(), name="a", transport="stdio")
        db.create_mcp_server(server_id=_make_id(), name="b", transport="stdio")
        servers = db.list_mcp_servers()
        assert len(servers) == 2

    def test_update_registry_columns(self, db: SQLiteBackend) -> None:
        sid = _make_id()
        db.create_mcp_server(
            server_id=sid,
            name="upgradable",
            transport="stdio",
            registry_name="io.example/up",
            registry_version="1.0.0",
        )
        ok = db.update_mcp_server(sid, registry_version="2.0.0")
        assert ok is True
        s = db.get_mcp_server(sid)
        assert s is not None
        assert s["registry_version"] == "2.0.0"
