from datetime import datetime, timezone
from types import SimpleNamespace
from unittest.mock import AsyncMock
from uuid import uuid4

import pytest

from src.core.exceptions import ConflictError
from src.domain.auth.webauthn_service import WebAuthnService
from src.domain.project_auth.service import ProjectAuthService


@pytest.mark.asyncio
async def test_project_auth_upsert_normalizes_legacy_providers_into_explicit_methods():
    now = datetime.now(timezone.utc)
    row = SimpleNamespace()
    row.project_id = uuid4()
    row.enabled = True
    row.providers = ["email", "magic_link", "google"]
    row.auth_methods = []
    row.mfa_methods = []
    row.allow_signup = True
    row.require_email_verify = True
    row.require_mfa = False
    row.google_oauth_mode = None
    row.google_client_id_enc = None
    row.access_token_ttl_seconds = 3600
    row.refresh_token_ttl_days = 30
    row.password_min_length = 8
    row.created_at = now
    row.updated_at = now

    config_repo = AsyncMock()
    config_repo.get_by_project = AsyncMock(return_value=None)

    async def create_side_effect(project_id, **fields):
        for key, value in fields.items():
            setattr(row, key, value)
        return row

    config_repo.create = AsyncMock(side_effect=create_side_effect)
    key_repo = AsyncMock()
    role_repo = AsyncMock()
    role_repo.list_for_project = AsyncMock(return_value=[])
    cloud = AsyncMock()

    svc = ProjectAuthService(
        config_repo=config_repo,
        key_repo=key_repo,
        role_repo=role_repo,
        cloud_client=cloud,
    )

    out = await svc.upsert_config(
        row.project_id,
        type(
            "Body",
            (),
            {
                "enabled": True,
                "providers": ["email", "magic_link", "google"],
                "auth_methods": None,
                "mfa_methods": ["passkey"],
                "allow_signup": True,
                "require_email_verify": True,
                "require_mfa": True,
                "google_oauth_mode": None,
                "google_client_id": None,
                "google_client_secret": None,
                "access_token_ttl_seconds": None,
                "refresh_token_ttl_days": None,
                "password_min_length": None,
            },
        )(),
    )

    create_fields = config_repo.create.await_args.kwargs
    assert create_fields["auth_methods"] == ["passwordless", "google"]
    assert create_fields["mfa_methods"] == ["passkey"]
    assert create_fields["require_mfa"] is True
    assert out.auth_methods == ["passwordless", "google"]
    assert out.mfa_methods == ["passkey"]
    assert out.google_oauth_mode == "platform"


@pytest.mark.asyncio
async def test_project_auth_requires_mfa_method_when_require_mfa_enabled():
    now = datetime.now(timezone.utc)
    row = SimpleNamespace()
    row.project_id = uuid4()
    row.enabled = True
    row.providers = ["password"]
    row.auth_methods = ["password"]
    row.mfa_methods = []
    row.allow_signup = True
    row.require_email_verify = False
    row.require_mfa = False
    row.google_oauth_mode = None
    row.google_client_id_enc = None
    row.access_token_ttl_seconds = 3600
    row.refresh_token_ttl_days = 30
    row.password_min_length = 8
    row.created_at = now
    row.updated_at = now

    config_repo = AsyncMock()
    config_repo.get_by_project = AsyncMock(return_value=row)

    async def update_side_effect(existing_row, **fields):
        for key, value in fields.items():
            setattr(existing_row, key, value)
        return existing_row

    config_repo.update = AsyncMock(side_effect=update_side_effect)

    svc = ProjectAuthService(
        config_repo=config_repo,
        key_repo=AsyncMock(),
        role_repo=AsyncMock(),
        cloud_client=AsyncMock(),
    )

    body = type(
        "Body",
        (),
        {
            "enabled": None,
            "providers": None,
            "auth_methods": None,
            "mfa_methods": [],
            "allow_signup": None,
            "require_email_verify": None,
            "require_mfa": True,
            "google_oauth_mode": None,
            "google_client_id": None,
            "google_client_secret": None,
            "access_token_ttl_seconds": None,
            "refresh_token_ttl_days": None,
            "password_min_length": None,
        },
    )()

    with pytest.raises(ConflictError, match="require_mfa cannot be enabled"):
        await svc.upsert_config(row.project_id, body)


@pytest.mark.asyncio
async def test_webauthn_authentication_options_accept_app_base_url():
    class StubStore:
        async def set(self, key: str, value: str, ttl_seconds: int = 300) -> None:
            self.last = (key, value, ttl_seconds)

        async def get(self, key: str):
            return None

        async def delete(self, key: str) -> None:
            return None

    store = StubStore()
    repo = AsyncMock()
    svc = WebAuthnService(credential_repo=repo, challenge_store=store)

    out = await svc.get_authentication_options(
        user_id=None,
        user_credentials=None,
        app_base_url="https://portal.example.com",
    )

    assert "state_id" in out or "challenge" in out
