"""Auth domain - FastAPI router (HTTP layer only)."""
from __future__ import annotations
from typing import Optional, Union
from uuid import UUID

from fastapi import APIRouter, Depends, Query, Request, Response
from fastapi.responses import JSONResponse
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from pydantic import BaseModel
from sqlalchemy.ext.asyncio import AsyncSession

from src.core.app_url import resolve_app_base_url
from src.core.database import get_db
from src.core.exceptions import UnauthorizedError, ValidationError
from src.core.redis import get_redis
from src.core.turnstile import verify_turnstile
from src.domain.auth.challenge_store import RedisChallengeStore
from src.domain.auth.request_throttle import enforce_auth_email_limits
from src.domain.auth.mfa_repository import TotpSecretRepository, WebAuthnCredentialRepository
from src.domain.auth.models import User
from src.domain.auth.repository import MagicLinkRepository, SessionRepository, UserRepository
from src.domain.auth.schemas import (
    MagicLinkRequest,
    MagicLinkVerify,
    MagicLinkVerifySuccessResponse,
    MfaRequiredResponse,
    PasswordLoginRequest,
    RefreshRequest,
    SignupRequest,
    TokenResponse,
    UserResponse,
    magic_verify_success_envelope,
)
from src.config import get_settings
from src.domain.auth.service import AuthService, TRUSTED_DEVICE_COOKIE
from src.domain.admin.repository import AdminRepository
from src.domain.auth.totp_service import TotpService
from src.domain.auth.bootstrap_service import AuthBootstrapService
from src.domain.auth.signup_service import SignupService
from src.domain.auth.promotion_service import PromotionService
from src.domain.auth.webauthn_service import WebAuthnService
from src.domain.billing.repository import SubscriptionRepository
from src.domain.workspaces.repository import (
    MembershipRepository,
    RoleRepository,
    WorkspaceInviteRepository,
    WorkspaceRepository,
    WorkspaceSettingsRepository,
)
from src.integrations.email_provider import email_transport_configured, get_email_provider

router = APIRouter(prefix="/api/auth", tags=["auth"])
security = HTTPBearer(auto_error=False)

# Realm for 401 WWW-Authenticate (Bearer); visible in browsers / proxies / client logs.
_AUTH_BEARER_REALM = "P2HS"

MagicVerifyResponse = Union[MagicLinkVerifySuccessResponse, MfaRequiredResponse]


def _me_unauthorized_response(
    *,
    code: str,
    message: str,
    bearer_error: str,
    bearer_error_description: str,
) -> JSONResponse:
    """401 for ``GET /me`` with JSON body + standard and explicit headers (why auth failed)."""
    www = (
        f'Bearer realm="{_AUTH_BEARER_REALM}", error="{bearer_error}", '
        f'error_description="{bearer_error_description}"'
    )
    return JSONResponse(
        status_code=401,
        content={
            "success": False,
            "authenticated": False,
            "error": {"code": code, "message": message},
        },
        headers={
            "WWW-Authenticate": www,
            "X-Auth-Error-Code": code,
            "X-Auth-Error-Reason": bearer_error_description,
        },
    )


async def get_token_store():
    """Get Redis token store for MFA flows. Yields None if Redis is unavailable."""
    store = None
    try:
        async for redis_client in get_redis():
            from src.domain.auth.token_store import RedisTokenStore

            store = RedisTokenStore(redis_client)
            break
    except Exception:
        pass
    yield store


async def get_promotion_service(db: AsyncSession = Depends(get_db)) -> PromotionService:
    """Get PromotionService for pending → user promotion."""
    from src.domain.auth.repository import PendingRegistrationRepository
    from src.domain.billing.repository import StripeCustomerRepository, SubscriptionRepository
    from src.domain.workspaces.repository import (
        MembershipRepository,
        RoleRepository,
        WorkspaceInviteRepository,
        WorkspaceRepository,
        WorkspaceSettingsRepository,
    )
    return PromotionService(
        pending_repo=PendingRegistrationRepository(db),
        user_repo=UserRepository(db),
        workspace_repo=WorkspaceRepository(db),
        membership_repo=MembershipRepository(db),
        role_repo=RoleRepository(db),
        invite_repo=WorkspaceInviteRepository(db),
        settings_repo=WorkspaceSettingsRepository(db),
        stripe_customer_repo=StripeCustomerRepository(db),
        subscription_repo=SubscriptionRepository(db),
    )


async def get_auth_service(
    db: AsyncSession = Depends(get_db),
    token_store=Depends(get_token_store),
    promotion_service: PromotionService = Depends(get_promotion_service),
) -> AuthService:
    """Get AuthService with repository dependencies (InTalk-aligned)."""
    from src.config import get_settings
    from src.domain.auth.backup_code_service import BackupCodeService
    from src.domain.auth.repository import PendingRegistrationRepository
    from src.domain.auth.trusted_device_service import TrustedDeviceRepository

    settings = get_settings()
    email_provider = get_email_provider() if email_transport_configured(settings) else None
    return AuthService(
        user_repo=UserRepository(db),
        session_repo=SessionRepository(db),
        magic_link_repo=MagicLinkRepository(db),
        totp_repo=TotpSecretRepository(db),
        email_provider=email_provider,
        trusted_device_repo=TrustedDeviceRepository(db),
        backup_code_service=BackupCodeService(db),
        token_store=token_store,
        pending_repo=PendingRegistrationRepository(db),
        promotion_service=promotion_service,
    )


async def get_challenge_store():
    """Get Redis-backed WebAuthn challenge store."""
    async for redis_client in get_redis():
        yield RedisChallengeStore(redis_client)
        return


async def get_webauthn_service(
    db: AsyncSession = Depends(get_db),
    challenge_store: RedisChallengeStore = Depends(get_challenge_store),
) -> WebAuthnService:
    """Get WebAuthnService. challenge_store from Redis if available."""
    return WebAuthnService(
        credential_repo=WebAuthnCredentialRepository(db),
        challenge_store=challenge_store,
    )


async def get_totp_service(db: AsyncSession = Depends(get_db)) -> TotpService:
    return TotpService(TotpSecretRepository(db))


async def get_signup_service(
    db: AsyncSession = Depends(get_db),
    auth_service: AuthService = Depends(get_auth_service),
) -> SignupService:
    from src.domain.admin.repository import SubscriptionPlanRepository
    from src.domain.auth.repository import PendingRegistrationRepository
    from src.domain.workspaces.repository import WorkspaceInviteRepository
    return SignupService(
        user_repo=UserRepository(db),
        pending_repo=PendingRegistrationRepository(db),
        invite_repo=WorkspaceInviteRepository(db),
        auth_service=auth_service,
        plan_repo=SubscriptionPlanRepository(db),
    )


async def get_current_user(
    credentials: Optional[HTTPAuthorizationCredentials] = Depends(security),
    auth_service: AuthService = Depends(get_auth_service),
) -> User:
    """Dependency for authenticated user."""
    if not credentials:
        raise UnauthorizedError("Missing or invalid authorization header")
    user = await auth_service.get_current_user(credentials.credentials)
    if not user:
        raise UnauthorizedError("Invalid or expired token")
    return user


async def get_current_user_optional(
    credentials: Optional[HTTPAuthorizationCredentials] = Depends(security),
    auth_service: AuthService = Depends(get_auth_service),
) -> Optional[User]:
    """Like ``get_current_user`` but returns ``None`` for guests (invalid/missing token ignored)."""
    if not credentials or not getattr(credentials, "credentials", None):
        return None
    return await auth_service.get_current_user(credentials.credentials)


async def get_bootstrap_service(db: AsyncSession = Depends(get_db)) -> AuthBootstrapService:
    return AuthBootstrapService(
        workspace_repo=WorkspaceRepository(db),
        membership_repo=MembershipRepository(db),
        role_repo=RoleRepository(db),
        subscription_repo=SubscriptionRepository(db),
        totp_repo=TotpSecretRepository(db),
        webauthn_repo=WebAuthnCredentialRepository(db),
        admin_repo=AdminRepository(db),
        invite_repo=WorkspaceInviteRepository(db),
    )


# --- Signup & Magic link ---

def _client_ip(request: Request) -> Optional[str]:
    forwarded = request.headers.get("CF-Connecting-IP") or request.headers.get("X-Forwarded-For")
    if forwarded:
        return forwarded.split(",", 1)[0].strip()
    return request.client.host if request.client else None

@router.post("/signup")
async def signup(
    body: SignupRequest,
    request: Request,
    signup_service: SignupService = Depends(get_signup_service),
    redis=Depends(get_redis),
):
    """Signup with profile + workspace bootstrap. Sends magic link. Idempotent."""
    try:
        client_ip = _client_ip(request)
        await verify_turnstile(body.turnstile_token, action="signup", remote_ip=client_ip)
        await enforce_auth_email_limits(redis, email=str(body.email), ip_address=client_ip)
        result = await signup_service.signup(
            body.email,
            first_name=body.first_name,
            last_name=body.last_name,
            phone=body.phone,
            company=body.company,
            workspace_name=body.workspace_name,
            plan_slug=body.plan_slug,
            ip_address=request.client.host if request.client else None,
            app_base_url=resolve_app_base_url(request),
        )
        return result
    except ValueError as e:
        raise ValidationError(str(e))


@router.post("/magic/request")
async def request_magic_link(
    body: MagicLinkRequest,
    request: Request,
    auth_service: AuthService = Depends(get_auth_service),
    redis=Depends(get_redis),
):
    """Request magic link. Sends email if the configured transport is available."""
    settings = get_settings()
    client_ip = _client_ip(request)
    await verify_turnstile(body.turnstile_token, action="login", remote_ip=client_ip)
    await enforce_auth_email_limits(redis, email=str(body.email), ip_address=client_ip)
    token = await auth_service.request_magic_link(
        body.email,
        ip_address=client_ip,
        app_base_url=resolve_app_base_url(request),
    )
    out: dict = {"message": "Magic link sent"}
    if settings.auth_expose_magic_token_for_dev or settings.debug:
        out["token_preview"] = f"{token[:8]}..."
    return out


@router.post("/password/login", response_model=MagicLinkVerifySuccessResponse)
async def password_login(
    body: PasswordLoginRequest,
    request: Request,
    auth_service: AuthService = Depends(get_auth_service),
    bootstrap: AuthBootstrapService = Depends(get_bootstrap_service),
):
    """Password login for explicitly provisioned reviewer/service accounts."""
    tokens, user = await auth_service.password_login(
        str(body.email),
        body.password,
        ip_address=_client_ip(request),
        user_agent=request.headers.get("user-agent"),
    )
    payload = await bootstrap.build_bootstrap(user)
    return magic_verify_success_envelope(tokens, payload)


@router.post("/mobile/magic/request")
async def request_mobile_magic_link(
    body: MagicLinkRequest,
    request: Request,
    auth_service: AuthService = Depends(get_auth_service),
):
    """Send a magic link that returns only to the configured P2HS mobile app."""
    settings = get_settings()
    token = await auth_service.request_mobile_magic_link(
        body.email,
        ip_address=request.client.host if request.client else None,
    )
    out: dict = {"message": "Magic link sent"}
    if settings.auth_expose_magic_token_for_dev or settings.debug:
        out["token_preview"] = f"{token[:8]}..."
    return out


@router.post("/magic/verify", response_model=MagicVerifyResponse)
async def verify_magic_link_post(
    body: MagicLinkVerify,
    request: Request,
    auth_service: AuthService = Depends(get_auth_service),
    bootstrap: AuthBootstrapService = Depends(get_bootstrap_service),
):
    """Verify magic link token.

    **Success (no TOTP):** JWTs at top level + under ``credentials``, plus ``user``, ``session``
    (flags), ``workspaces``, ``routing`` — same data as ``GET /api/auth/me`` after login.

    **TOTP required:** No JWT yet — use ``mfa_token`` with ``POST /api/auth/mfa/totp/verify``.
    """
    device_id = request.cookies.get(TRUSTED_DEVICE_COOKIE)
    result = await auth_service.verify_magic_link(
        token=body.token,
        ip_address=request.client.host if request.client else None,
        user_agent=request.headers.get("user-agent"),
        device_id=device_id,
    )
    if isinstance(result, tuple):
        tokens, user = result
        payload = await bootstrap.build_bootstrap(user)
        return magic_verify_success_envelope(tokens, payload)
    return result


@router.get("/magic/verify", response_model=MagicVerifyResponse)
async def verify_magic_link_get(
    request: Request,
    token: str = Query(...),
    auth_service: AuthService = Depends(get_auth_service),
    bootstrap: AuthBootstrapService = Depends(get_bootstrap_service),
):
    """Verify magic link via GET (email link compatibility, InTalk-style). Same body shape as POST."""
    device_id = request.cookies.get(TRUSTED_DEVICE_COOKIE)
    result = await auth_service.verify_magic_link(
        token=token,
        ip_address=request.client.host if request.client else None,
        user_agent=request.headers.get("user-agent"),
        device_id=device_id,
    )
    if isinstance(result, tuple):
        tokens, user = result
        payload = await bootstrap.build_bootstrap(user)
        return magic_verify_success_envelope(tokens, payload)
    return result


@router.post("/refresh", response_model=TokenResponse)
async def refresh(
    body: RefreshRequest,
    auth_service: AuthService = Depends(get_auth_service),
):
    """Refresh access token using refresh token.

    Returns a new refresh token JWT alongside the access token; treat the refresh token as
    rotated and replace the client-stored value on each successful call.
    """
    try:
        return await auth_service.refresh_tokens(body.refresh_token)
    except Exception as e:
        if hasattr(e, "message"):
            raise UnauthorizedError(e.message)
        raise


class LogoutRequest(BaseModel):
    refresh_token: Optional[str] = None


@router.post("/logout")
async def logout(
    body: Optional[LogoutRequest] = None,
    auth_service: AuthService = Depends(get_auth_service),
):
    """Logout - revoke session."""
    refresh_token = body.refresh_token if body else None
    await auth_service.logout(refresh_token=refresh_token)
    return {"message": "Logged out"}


@router.get("/me")
async def get_me(
    credentials: Optional[HTTPAuthorizationCredentials] = Depends(security),
    auth_service: AuthService = Depends(get_auth_service),
    bootstrap: AuthBootstrapService = Depends(get_bootstrap_service),
):
    """Canonical frontend bootstrap — structured JSON for auth failures (no stack traces).

    **Unauthenticated:** ``401`` with ``success: false`` and ``error.code`` (missing or bad token).

    **Authenticated:** ``200`` with ``success: true`` plus user, session, workspaces, routing.
    """
    if not credentials or not getattr(credentials, "credentials", None):
        return _me_unauthorized_response(
            code="missing_authorization",
            message="Send Authorization: Bearer <access_token>.",
            bearer_error="invalid_request",
            bearer_error_description="Authorization Bearer access token was not sent",
        )
    user = await auth_service.get_current_user(credentials.credentials)
    if not user:
        return _me_unauthorized_response(
            code="invalid_or_expired_token",
            message="Invalid or expired access token.",
            bearer_error="invalid_token",
            bearer_error_description="The access token is invalid or has expired",
        )
    payload = await bootstrap.build_bootstrap(user)
    payload["success"] = True
    return payload


# --- TOTP MFA ---

class TotpSetupResponse(BaseModel):
    provisioning_uri: str
    secret: str


class TotpVerifyRequest(BaseModel):
    code: str


class TotpCompleteMfaRequest(BaseModel):
    mfa_token: Optional[str] = None
    login_token: Optional[str] = None  # InTalk alias
    code: str
    remember_device: bool = False


@router.post("/mfa/totp/setup", response_model=TotpSetupResponse)
async def totp_setup(
    user: User = Depends(get_current_user),
    totp_service: TotpService = Depends(get_totp_service),
):
    """Start TOTP setup. Returns QR provisioning URI. Call /enable with first code to activate."""
    data = await totp_service.setup(user.id, account_name=user.email)
    return TotpSetupResponse(
        provisioning_uri=data["provisioning_uri"],
        secret=data["secret"],
    )


@router.post("/mfa/totp/enable")
async def totp_enable(
    body: TotpVerifyRequest,
    user: User = Depends(get_current_user),
    totp_service: TotpService = Depends(get_totp_service),
    db: AsyncSession = Depends(get_db),
):
    """Verify first TOTP code and enable MFA. Returns backup codes (InTalk-aligned)."""
    ok = await totp_service.enable(user.id, body.code)
    if not ok:
        raise UnauthorizedError("Invalid TOTP code")
    from src.domain.auth.backup_code_service import BackupCodeService
    backup_codes = await BackupCodeService(db).regenerate_for_user(user.id)
    return {"message": "TOTP enabled", "backup_codes": backup_codes}


@router.post("/mfa/totp/verify")
async def totp_verify_complete_mfa(
    body: TotpCompleteMfaRequest,
    request: Request,
    response: Response,
    auth_service: AuthService = Depends(get_auth_service),
):
    """Complete MFA after magic link. Supports TOTP, backup codes, remember device (InTalk-aligned)."""
    token = body.mfa_token or body.login_token
    if not token:
        raise UnauthorizedError("mfa_token or login_token required")
    result = await auth_service.complete_totp_mfa(
        mfa_token=token,
        code=body.code,
        remember_device=body.remember_device,
        device_id=request.cookies.get(TRUSTED_DEVICE_COOKIE),
        user_agent=request.headers.get("user-agent"),
        ip_address=request.client.host if request.client else None,
    )
    if not result:
        raise UnauthorizedError("Invalid MFA token or code")
    tokens, trusted_device_id, used_backup_code, backup_codes_remaining = result
    settings = get_settings()
    if trusted_device_id:
        response.set_cookie(
            key=TRUSTED_DEVICE_COOKIE,
            value=trusted_device_id,
            httponly=False,
            secure=not settings.is_dev,
            samesite="lax",
            max_age=60 * 60 * 24 * 30,
        )
    out = {
        "access_token": tokens.access_token,
        "refresh_token": tokens.refresh_token,
        "token_type": tokens.token_type,
        "expires_in": tokens.expires_in,
        "issued_at": tokens.issued_at.isoformat() if tokens.issued_at else None,
        "two_factor_verified": True,
    }
    if used_backup_code:
        out["used_backup_code"] = True
    if backup_codes_remaining is not None:
        out["backup_codes_remaining"] = backup_codes_remaining
    if trusted_device_id:
        out["trusted_device_token"] = trusted_device_id
    return out


@router.delete("/mfa/totp")
async def totp_disable(
    body: TotpVerifyRequest,
    user: User = Depends(get_current_user),
    totp_service: TotpService = Depends(get_totp_service),
):
    """Disable TOTP. Requires current TOTP code."""
    ok = await totp_service.disable(user.id, body.code)
    if not ok:
        raise UnauthorizedError("Invalid TOTP code")
    return {"message": "TOTP disabled"}


@router.get("/mfa/totp/status")
async def totp_status(
    user: User = Depends(get_current_user),
    totp_service: TotpService = Depends(get_totp_service),
    db: AsyncSession = Depends(get_db),
):
    """Check if TOTP is enabled and backup codes remaining (InTalk-aligned)."""
    enabled = await totp_service.is_enabled(user.id)
    from src.domain.auth.backup_code_service import BackupCodeService
    remaining = await BackupCodeService(db).remaining_count(user.id) if enabled else 0
    return {"enabled": enabled, "backup_codes_remaining": remaining}


@router.post("/mfa/totp/recovery")
async def request_totp_recovery(
    body: MagicLinkRequest,
    request: Request,
    auth_service: AuthService = Depends(get_auth_service),
):
    """Send TOTP recovery link (lockout recovery, InTalk-style). Always 200."""
    result = await auth_service.send_totp_recovery_link(
        body.email, app_base_url=resolve_app_base_url(request)
    )
    if result.get("sent"):
        return {"sent": True, "message": "Recovery link sent"}
    reasons = {"account_not_found": "No account found.", "totp_not_enabled": "2FA not enabled.", "recovery_unavailable": "Recovery unavailable."}
    msg = reasons.get(result.get("reason", ""), "Unable to send recovery link.")
    return {"sent": False, "reason": result.get("reason", "unknown"), "message": msg}


@router.get("/mfa/totp/recovery/verify")
async def verify_totp_recovery(
    token: str = Query(...),
    auth_service: AuthService = Depends(get_auth_service),
):
    """Consume recovery token, disable TOTP, return tokens (InTalk-style)."""
    result = await auth_service.verify_totp_recovery(token)
    if not result:
        raise UnauthorizedError("Recovery token invalid or expired")
    user, tokens = result
    return {
        "message": "TOTP reset",
        "totp_reset": True,
        "requires_totp_setup": True,
        "user": {"id": str(user.id), "email": user.email, "totp_enabled": False},
        "access_token": tokens.access_token,
        "refresh_token": tokens.refresh_token,
        "token_type": tokens.token_type,
        "expires_in": tokens.expires_in,
        "issued_at": tokens.issued_at.isoformat() if tokens.issued_at else None,
    }


@router.post("/mfa/totp/recovery/code")
async def request_totp_recovery_code(
    body: MagicLinkRequest,
    auth_service: AuthService = Depends(get_auth_service),
):
    """Send 6-digit TOTP recovery code via email (embedded app style). Always 200."""
    result = await auth_service.send_totp_recovery_code(body.email)
    if result.get("sent"):
        return {"sent": True, "message": "Recovery code sent"}
    return {"sent": False, "reason": result.get("reason", "unknown"), "message": "Unable to send recovery code."}


@router.post("/mfa/totp/recovery/consume")
async def consume_totp_recovery_code(
    body: TotpVerifyRequest,
    auth_service: AuthService = Depends(get_auth_service),
):
    """Verify 6-digit recovery code, disable TOTP, return tokens (embedded app style)."""
    result = await auth_service.verify_totp_recovery_code(body.code)
    if not result:
        raise UnauthorizedError("Recovery code invalid or expired")
    user, tokens = result
    return {
        "message": "TOTP reset",
        "totp_reset": True,
        "requires_totp_setup": True,
        "user": {"id": str(user.id), "email": user.email, "totp_enabled": False},
        "access_token": tokens.access_token,
        "refresh_token": tokens.refresh_token,
        "token_type": tokens.token_type,
        "expires_in": tokens.expires_in,
        "issued_at": tokens.issued_at.isoformat() if tokens.issued_at else None,
    }


@router.post("/mfa/backup-codes/regenerate")
async def regenerate_backup_codes(
    user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """Regenerate backup codes. Requires TOTP-verified session."""
    from src.domain.auth.backup_code_service import BackupCodeService
    codes = await BackupCodeService(db).regenerate_for_user(user.id)
    return {"backup_codes": codes}


# --- WebAuthn passkeys ---

class WebAuthnRegisterVerifyRequest(BaseModel):
    credential: dict


class WebAuthnAuthenticateOptionsRequest(BaseModel):
    email: Optional[str] = None


class WebAuthnAuthenticateVerifyRequest(BaseModel):
    credential: dict
    email: Optional[str] = None
    state_id: Optional[str] = None


@router.post("/webauthn/register/options")
async def webauthn_register_options(
    request: Request,
    user: User = Depends(get_current_user),
    webauthn_service: WebAuthnService = Depends(get_webauthn_service),
):
    """Get WebAuthn registration options. Use with navigator.credentials.create()."""
    opts = await webauthn_service.get_registration_options(
        user.id, user.email, app_base_url=resolve_app_base_url(request)
    )
    return opts


@router.post("/webauthn/register/verify")
async def webauthn_register_verify(
    body: WebAuthnRegisterVerifyRequest,
    request: Request,
    user: User = Depends(get_current_user),
    webauthn_service: WebAuthnService = Depends(get_webauthn_service),
):
    """Verify WebAuthn registration and store passkey."""
    ok = await webauthn_service.verify_registration(
        user.id, body.credential, app_base_url=resolve_app_base_url(request)
    )
    if not ok:
        raise UnauthorizedError("Invalid WebAuthn registration")
    return {"message": "Passkey registered"}


@router.post("/webauthn/authenticate/options")
async def webauthn_authenticate_options(
    body: WebAuthnAuthenticateOptionsRequest,
    request: Request,
    db: AsyncSession = Depends(get_db),
    webauthn_service: WebAuthnService = Depends(get_webauthn_service),
):
    """Get WebAuthn auth options. Pass email for discoverable credentials, omit for usernameless."""
    user_id = None
    user_credentials = None
    if body.email:
        from src.domain.auth.repository import UserRepository
        user = await UserRepository(db).get_by_email(body.email.lower())
        if user:
            user_id = user.id
            creds = await WebAuthnCredentialRepository(db).list_for_user(user.id)
            user_credentials = [{"id": c.credential_id} for c in creds]
    opts = await webauthn_service.get_authentication_options(
        user_id=user_id,
        user_credentials=user_credentials,
        app_base_url=resolve_app_base_url(request),
    )
    return opts


@router.post("/webauthn/authenticate/verify", response_model=TokenResponse)
async def webauthn_authenticate_verify(
    body: WebAuthnAuthenticateVerifyRequest,
    request: Request,
    db: AsyncSession = Depends(get_db),
    auth_service: AuthService = Depends(get_auth_service),
    webauthn_service: WebAuthnService = Depends(get_webauthn_service),
):
    """Verify WebAuthn auth and return tokens. Requires user session creation - handled inside."""
    from src.domain.auth.repository import UserRepository, SessionRepository
    from src.core.security.hashing import generate_refresh_token, hash_token
    from src.core.security.jwt import create_access_token, create_refresh_token
    from datetime import datetime, timedelta, timezone
    from src.config import get_settings

    user_id = None
    if body.email:
        user = await UserRepository(db).get_by_email(body.email.lower())
        user_id = user.id if user else None

    verified_user_id = await webauthn_service.verify_authentication(
        body.credential,
        user_id=user_id,
        state_id=body.state_id,
        app_base_url=resolve_app_base_url(request),
    )
    if not verified_user_id:
        raise UnauthorizedError("Invalid WebAuthn authentication")

    settings = get_settings()
    session_repo = SessionRepository(db)
    refresh_token = generate_refresh_token()
    refresh_hash = hash_token(refresh_token)
    expires_at = datetime.now(timezone.utc) + timedelta(days=settings.jwt_refresh_ttl_days)

    session = await session_repo.create(
        user_id=verified_user_id,
        refresh_token_hash=refresh_hash,
        expires_at=expires_at,
        ip_address=request.client.host if request.client else None,
        user_agent=request.headers.get("user-agent"),
    )

    from datetime import datetime, timezone

    access_token = create_access_token(verified_user_id, session.id)
    refresh_jwt = create_refresh_token(verified_user_id, session.id)
    return TokenResponse(
        access_token=access_token,
        refresh_token=refresh_jwt,
        expires_in=settings.jwt_access_ttl_minutes * 60,
        issued_at=datetime.now(timezone.utc),
    )
