"""Tests for ConnectionsResource."""

from __future__ import annotations

from typing import Any
from unittest.mock import AsyncMock
from uuid import uuid4

import pytest

from mcpgateway_sdk.models.connection import (
    BasicConnection,
    Connection,
    ConnectionCreated,
    OAuthAuthorization,
)
from mcpgateway_sdk.resources.connections import ConnectionsResource


@pytest.fixture
def http() -> AsyncMock:
    """Mock HTTPClient."""
    return AsyncMock()


@pytest.fixture
def connections(http: AsyncMock) -> ConnectionsResource:
    """ConnectionsResource with mocked HTTP."""
    return ConnectionsResource(http)


def _connection_data(**overrides: Any) -> dict[str, Any]:
    return {
        "id": str(uuid4()),
        "user_id": str(uuid4()),
        "server_id": str(uuid4()),
        "provider_name": "github",
        "provider_email": "user@example.com",
        "token_type": "Bearer",
        "expires_at": None,
        "scopes": ["repo"],
        "is_valid": True,
        "health_status": "healthy",
        "created_at": "2026-01-01T00:00:00Z",
        "updated_at": "2026-01-01T00:00:00Z",
        **overrides,
    }


def _connection_created_data(**overrides: Any) -> dict[str, Any]:
    return {
        "id": str(uuid4()),
        "server_id": str(uuid4()),
        "user_id": str(uuid4()),
        "credential_type": "api_key",
        "alias": "My API Key",
        "header_name": "Authorization",
        "prefix": "Bearer ",
        "credential_masked": "••••••••abcdef",
        "is_valid": True,
        "created_at": "2026-01-01T00:00:00Z",
        "updated_at": "2026-01-01T00:00:00Z",
        **overrides,
    }


def _basic_connection_data(**overrides: Any) -> dict[str, Any]:
    return {
        "id": str(uuid4()),
        "server_id": str(uuid4()),
        "user_id": str(uuid4()),
        "username": "admin",
        "auth_method": "basic",
        "connection_data": {"account_sid": "AC123"},
        "is_valid": True,
        "created_at": "2026-01-01T00:00:00Z",
        "updated_at": "2026-01-01T00:00:00Z",
        **overrides,
    }


class TestCreate:
    """Tests for ConnectionsResource.create."""

    async def test_sends_post_with_required_fields(
        self, connections: ConnectionsResource, http: AsyncMock
    ) -> None:
        sid = uuid4()
        http.request.return_value = _connection_created_data()

        await connections.create(sid, "api_key", "sk-abc123")

        http.request.assert_awaited_once_with(
            "POST",
            "/api/v1/connections",
            json={
                "server_id": str(sid),
                "credential_type": "api_key",
                "credential_value": "sk-abc123",
                "header_name": "Authorization",
                "prefix": "",
            },
        )

    async def test_includes_optional_alias(
        self, connections: ConnectionsResource, http: AsyncMock
    ) -> None:
        http.request.return_value = _connection_created_data()

        await connections.create(uuid4(), "bearer", "tok", alias="My Token")

        body = http.request.call_args.kwargs["json"]
        assert body["alias"] == "My Token"

    async def test_returns_connection_created(
        self, connections: ConnectionsResource, http: AsyncMock
    ) -> None:
        http.request.return_value = _connection_created_data()

        result = await connections.create(uuid4(), "api_key", "sk-abc123")

        assert isinstance(result, ConnectionCreated)
        assert result.credential_masked == "••••••••abcdef"


class TestCreateBasic:
    """Tests for ConnectionsResource.create_basic."""

    async def test_sends_post_with_credentials(
        self, connections: ConnectionsResource, http: AsyncMock
    ) -> None:
        sid = uuid4()
        http.request.return_value = _basic_connection_data()

        await connections.create_basic(sid, "admin", "secret")

        http.request.assert_awaited_once_with(
            "POST",
            "/api/v1/connections/basic",
            json={
                "server_id": str(sid),
                "username": "admin",
                "password": "secret",
            },
        )

    async def test_includes_connection_data(
        self, connections: ConnectionsResource, http: AsyncMock
    ) -> None:
        http.request.return_value = _basic_connection_data()

        await connections.create_basic(
            uuid4(), "admin", "secret", connection_data={"account_sid": "AC123"}
        )

        body = http.request.call_args.kwargs["json"]
        assert body["connection_data"] == {"account_sid": "AC123"}

    async def test_returns_basic_connection(
        self, connections: ConnectionsResource, http: AsyncMock
    ) -> None:
        http.request.return_value = _basic_connection_data()

        result = await connections.create_basic(uuid4(), "admin", "secret")

        assert isinstance(result, BasicConnection)
        assert result.username == "admin"
        assert result.auth_method == "basic"


class TestList:
    """Tests for ConnectionsResource.list."""

    async def test_sends_get_with_defaults(
        self, connections: ConnectionsResource, http: AsyncMock
    ) -> None:
        http.request.return_value = {"items": []}

        await connections.list()

        http.request.assert_awaited_once_with(
            "GET",
            "/api/v1/connections",
            params={"include_server": False, "include_revoked": False},
        )

    async def test_sends_get_with_flags(
        self, connections: ConnectionsResource, http: AsyncMock
    ) -> None:
        http.request.return_value = {"items": []}

        await connections.list(include_server=True, include_revoked=True)

        params = http.request.call_args.kwargs["params"]
        assert params["include_server"] is True
        assert params["include_revoked"] is True

    async def test_returns_list_of_connections(
        self, connections: ConnectionsResource, http: AsyncMock
    ) -> None:
        http.request.return_value = {"items": [_connection_data()]}

        result = await connections.list()

        assert len(result) == 1
        assert isinstance(result[0], Connection)


class TestGet:
    """Tests for ConnectionsResource.get."""

    async def test_sends_get_with_id(
        self, connections: ConnectionsResource, http: AsyncMock
    ) -> None:
        cid = uuid4()
        http.request.return_value = _connection_data()

        await connections.get(cid)

        http.request.assert_awaited_once_with("GET", f"/api/v1/connections/{cid}")

    async def test_returns_connection(
        self, connections: ConnectionsResource, http: AsyncMock
    ) -> None:
        http.request.return_value = _connection_data()

        result = await connections.get(uuid4())

        assert isinstance(result, Connection)


class TestRevoke:
    """Tests for ConnectionsResource.revoke."""

    async def test_sends_delete(self, connections: ConnectionsResource, http: AsyncMock) -> None:
        cid = uuid4()
        http.request.return_value = None

        await connections.revoke(cid)

        http.request.assert_awaited_once_with("DELETE", f"/api/v1/connections/{cid}")


class TestRefresh:
    """Tests for ConnectionsResource.refresh."""

    async def test_sends_post(self, connections: ConnectionsResource, http: AsyncMock) -> None:
        cid = uuid4()
        http.request.return_value = _connection_data()

        result = await connections.refresh(cid)

        http.request.assert_awaited_once_with("POST", f"/api/v1/connections/{cid}/refresh")
        assert isinstance(result, Connection)


class TestInitiateOAuth:
    """Tests for ConnectionsResource.initiate_oauth."""

    async def test_sends_post_with_oauth_app_id(
        self, connections: ConnectionsResource, http: AsyncMock
    ) -> None:
        sid, oid = uuid4(), uuid4()
        http.request.return_value = {
            "authorization_url": "https://github.com/login/oauth/authorize?...",
            "state": "abc123",
        }

        await connections.initiate_oauth(sid, oid)

        http.request.assert_awaited_once_with(
            "POST",
            f"/api/v1/servers/{sid}/oauth/authorize",
            json={"oauth_app_id": str(oid)},
        )

    async def test_includes_optional_params(
        self, connections: ConnectionsResource, http: AsyncMock
    ) -> None:
        http.request.return_value = {
            "authorization_url": "https://example.com",
            "state": "xyz",
        }

        await connections.initiate_oauth(
            uuid4(),
            uuid4(),
            redirect_uri="https://myapp.com/callback",
            scopes=["read", "write"],
            connection_data={"instance_url": "https://my.salesforce.com"},
        )

        body = http.request.call_args.kwargs["json"]
        assert body["redirect_uri"] == "https://myapp.com/callback"
        assert body["scopes"] == ["read", "write"]
        assert body["connection_data"] == {"instance_url": "https://my.salesforce.com"}

    async def test_returns_oauth_authorization(
        self, connections: ConnectionsResource, http: AsyncMock
    ) -> None:
        http.request.return_value = {
            "authorization_url": "https://github.com/login/oauth/authorize?...",
            "state": "abc123",
        }

        result = await connections.initiate_oauth(uuid4(), uuid4())

        assert isinstance(result, OAuthAuthorization)
        assert result.state == "abc123"
        assert "github.com" in result.authorization_url
