from __future__ import annotations

import pytest

import v1.auth.gateway as auth_gateway_module
from v1.auth.schemas import (
    AuthTokenResponse,
    AuthResendCodeResponse,
    AuthSignupStartResponse,
    AuthUserByEmailResponse,
    AuthUser,
    LoginRequest,
    RefreshRequest,
    SignupResendRequest,
    SignupStartRequest,
    SignupVerifyRequest,
)
from v1.auth.service import AuthService, AuthServiceGateway


class FakeGateway(AuthServiceGateway):
    def __init__(self, response: AuthTokenResponse) -> None:
        self._response = response

    async def signup_start(
        self, request: SignupStartRequest
    ) -> AuthSignupStartResponse:
        return AuthSignupStartResponse(email=request.email)

    async def signup_verify(self, request: SignupVerifyRequest) -> AuthTokenResponse:
        return self._response

    async def signup_resend(
        self, request: SignupResendRequest
    ) -> AuthResendCodeResponse:
        return AuthResendCodeResponse()

    async def login(self, request: LoginRequest) -> AuthTokenResponse:
        return self._response

    async def refresh(self, request: RefreshRequest) -> AuthTokenResponse:
        return self._response

    async def logout(self, refresh_token: str | None) -> None:
        return None

    async def get_user_by_email(self, email: str) -> AuthUserByEmailResponse:
        return AuthUserByEmailResponse(
            id="user-1",
            email=email,
            created_at="2026-02-24T00:00:00Z",
            email_confirmed_at=None,
        )


@pytest.mark.asyncio
async def test_signup_maps_response() -> None:
    user = AuthUser(id="user-1", email="user@example.com")
    token_response = AuthTokenResponse(
        access_token="access",
        refresh_token="refresh",
        expires_in=3600,
        token_type="bearer",
        user=user,
    )
    service = AuthService(gateway=FakeGateway(token_response))

    start_result = await service.signup_start(
        SignupStartRequest(
            username="demo", email="user@example.com", password="secret123"
        )
    )
    assert start_result.status == "pending_verification"
    assert start_result.email == "user@example.com"

    result = await service.signup_verify(
        SignupVerifyRequest(email="user@example.com", token="123456")
    )

    assert result.access_token == "access"
    assert result.refresh_token == "refresh"
    assert result.user.id == "user-1"


class LogoutAssertingGateway(AuthServiceGateway):
    def __init__(self, expected_refresh_token: str) -> None:
        self._expected_refresh_token = expected_refresh_token

    async def signup_start(
        self, request: SignupStartRequest
    ) -> AuthSignupStartResponse:
        raise NotImplementedError

    async def signup_verify(self, request: SignupVerifyRequest) -> AuthTokenResponse:
        raise NotImplementedError

    async def signup_resend(
        self, request: SignupResendRequest
    ) -> AuthResendCodeResponse:
        raise NotImplementedError

    async def login(self, request: LoginRequest) -> AuthTokenResponse:
        raise NotImplementedError

    async def refresh(self, request: RefreshRequest) -> AuthTokenResponse:
        raise NotImplementedError

    async def logout(self, refresh_token: str | None) -> None:
        assert refresh_token == self._expected_refresh_token

    async def get_user_by_email(self, email: str) -> AuthUserByEmailResponse:
        raise NotImplementedError


@pytest.mark.asyncio
async def test_logout_forwards_refresh_token() -> None:
    service = AuthService(gateway=LogoutAssertingGateway("refresh-token"))

    await service.logout("refresh-token")


@pytest.mark.asyncio
async def test_get_user_by_email_forwards_to_gateway() -> None:
    user = AuthUser(id="user-1", email="user@example.com")
    token_response = AuthTokenResponse(
        access_token="access",
        refresh_token="refresh",
        expires_in=3600,
        token_type="bearer",
        user=user,
    )
    service = AuthService(gateway=FakeGateway(token_response))

    result = await service.get_user_by_email("user@example.com")

    assert result.email == "user@example.com"


@pytest.mark.asyncio
async def test_signup_resend_returns_generic_message() -> None:
    user = AuthUser(id="user-1", email="user@example.com")
    token_response = AuthTokenResponse(
        access_token="access",
        refresh_token="refresh",
        expires_in=3600,
        token_type="bearer",
        user=user,
    )
    service = AuthService(gateway=FakeGateway(token_response))

    result = await service.signup_resend(SignupResendRequest(email="user@example.com"))

    assert result.message == "If the email exists, a verification code has been sent"


@pytest.mark.asyncio
async def test_supabase_signup_passes_username_in_metadata(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    captured_payload: dict[str, object] = {}

    class FakeSupabaseAuth:
        def sign_up(self, payload: dict[str, object]) -> object:
            captured_payload.update(payload)

            class _User:
                id = "user-1"
                email = "user@example.com"

            class _Session:
                access_token = "access"
                refresh_token = "refresh"
                expires_in = 3600
                token_type = "bearer"

            class _Response:
                user = _User()
                session = None

            return _Response()

    class FakeClient:
        auth = FakeSupabaseAuth()

    monkeypatch.setattr(auth_gateway_module, "create_client", lambda *_: FakeClient())

    gateway = auth_gateway_module.SupabaseAuthGateway()
    await gateway.signup_start(
        SignupStartRequest(
            username="demo",
            email="user@example.com",
            password="secret123",
        )
    )

    assert captured_payload["data"] == {"username": "demo"}
