"""Tests for auth/provider.py — OAuth Authorization Server Provider."""

import pytest
from cryptography.fernet import Fernet

from mcp.server.auth.provider import AuthorizationParams
from mcp.shared.auth import OAuthClientInformationFull


@pytest.fixture(autouse=True)
def _setup_env(monkeypatch):
    key = Fernet.generate_key().decode()
    monkeypatch.setenv("BYBIT_MCP_SECRET_KEY", key)
    monkeypatch.setenv("BYBIT_MCP_ADMIN_USERNAME", "admin")
    monkeypatch.setenv("BYBIT_MCP_ADMIN_PASSWORD", "testpass")
    monkeypatch.setenv("BYBIT_MCP_ISSUER_URL", "http://localhost:8000")


@pytest.fixture()
def db_path(tmp_path):
    from bybit_mcp_server.auth.db import init_db

    path = str(tmp_path / "test.db")
    init_db(path)
    return path


@pytest.fixture()
def provider(db_path):
    from bybit_mcp_server.auth.provider import BybitMCPAuthProvider

    return BybitMCPAuthProvider(db_path)


@pytest.fixture()
def client_info():
    return OAuthClientInformationFull(
        client_id="test-client",
        redirect_uris=["http://localhost/callback"],
        token_endpoint_auth_method="none",
    )


@pytest.fixture()
def auth_params():
    return AuthorizationParams(
        state="test-state",
        scopes=["mcp:tools"],
        code_challenge="test-challenge",
        redirect_uri="http://localhost/callback",
        redirect_uri_provided_explicitly=True,
    )


class TestClientRegistration:
    async def test_register_and_get(self, provider, client_info):
        await provider.register_client(client_info)
        loaded = await provider.get_client("test-client")
        assert loaded is not None
        assert loaded.client_id == "test-client"

    async def test_get_missing(self, provider):
        assert await provider.get_client("nonexistent") is None


class TestAuthorize:
    async def test_returns_login_url(self, provider, client_info, auth_params):
        await provider.register_client(client_info)
        url = await provider.authorize(client_info, auth_params)
        assert url.startswith("http://localhost:8000/login?request_id=")


class TestVerifyAdminCredentials:
    def test_valid(self):
        from bybit_mcp_server.auth.provider import verify_admin_credentials

        assert verify_admin_credentials("admin", "testpass") is True

    def test_invalid_password(self):
        from bybit_mcp_server.auth.provider import verify_admin_credentials

        assert verify_admin_credentials("admin", "wrong") is False

    def test_invalid_username(self):
        from bybit_mcp_server.auth.provider import verify_admin_credentials

        assert verify_admin_credentials("wrong", "testpass") is False


class TestCompleteAuthorization:
    async def test_full_flow(self, provider, client_info, auth_params, db_path):
        from bybit_mcp_server.auth.provider import complete_authorization

        await provider.register_client(client_info)
        login_url = await provider.authorize(client_info, auth_params)
        request_id = login_url.split("request_id=")[1]

        redirect_url = complete_authorization(db_path, request_id)
        assert redirect_url is not None
        assert "code=" in redirect_url
        assert "state=test-state" in redirect_url

    async def test_invalid_request_id(self, db_path):
        from bybit_mcp_server.auth.provider import complete_authorization

        assert complete_authorization(db_path, "invalid") is None


class TestTokenExchange:
    async def test_exchange_authorization_code(self, provider, client_info, auth_params, db_path):
        from bybit_mcp_server.auth.provider import complete_authorization

        await provider.register_client(client_info)
        login_url = await provider.authorize(client_info, auth_params)
        request_id = login_url.split("request_id=")[1]
        redirect_url = complete_authorization(db_path, request_id)

        # Extract code from redirect URL
        from urllib.parse import parse_qs, urlparse

        parsed = urlparse(redirect_url)
        code = parse_qs(parsed.query)["code"][0]

        auth_code = await provider.load_authorization_code(client_info, code)
        assert auth_code is not None

        token = await provider.exchange_authorization_code(client_info, auth_code)
        assert token.access_token
        assert token.refresh_token
        assert token.token_type == "Bearer"

    async def test_refresh_token_exchange(self, provider, client_info, auth_params, db_path):
        from bybit_mcp_server.auth.provider import complete_authorization

        await provider.register_client(client_info)
        login_url = await provider.authorize(client_info, auth_params)
        request_id = login_url.split("request_id=")[1]
        redirect_url = complete_authorization(db_path, request_id)

        from urllib.parse import parse_qs, urlparse

        code = parse_qs(urlparse(redirect_url).query)["code"][0]
        auth_code = await provider.load_authorization_code(client_info, code)
        token = await provider.exchange_authorization_code(client_info, auth_code)

        # Now refresh
        rt = await provider.load_refresh_token(client_info, token.refresh_token)
        assert rt is not None
        new_token = await provider.exchange_refresh_token(client_info, rt, ["mcp:tools"])
        assert new_token.access_token != token.access_token
        assert new_token.refresh_token != token.refresh_token


class TestAccessTokenVerification:
    async def test_verify_valid_token(self, provider, client_info, auth_params, db_path):
        from bybit_mcp_server.auth.provider import complete_authorization

        await provider.register_client(client_info)
        login_url = await provider.authorize(client_info, auth_params)
        request_id = login_url.split("request_id=")[1]
        redirect_url = complete_authorization(db_path, request_id)

        from urllib.parse import parse_qs, urlparse

        code = parse_qs(urlparse(redirect_url).query)["code"][0]
        auth_code = await provider.load_authorization_code(client_info, code)
        token = await provider.exchange_authorization_code(client_info, auth_code)

        access = await provider.load_access_token(token.access_token)
        assert access is not None
        assert access.client_id == "test-client"

    async def test_verify_invalid_token(self, provider):
        assert await provider.load_access_token("nonexistent") is None


class TestRevocation:
    async def test_revoke_access_token(self, provider, client_info, auth_params, db_path):
        from bybit_mcp_server.auth.provider import complete_authorization

        await provider.register_client(client_info)
        login_url = await provider.authorize(client_info, auth_params)
        request_id = login_url.split("request_id=")[1]
        redirect_url = complete_authorization(db_path, request_id)

        from urllib.parse import parse_qs, urlparse

        code = parse_qs(urlparse(redirect_url).query)["code"][0]
        auth_code = await provider.load_authorization_code(client_info, code)
        token = await provider.exchange_authorization_code(client_info, auth_code)

        access = await provider.load_access_token(token.access_token)
        await provider.revoke_token(access)

        assert await provider.load_access_token(token.access_token) is None
        assert await provider.load_refresh_token(client_info, token.refresh_token) is None
