"""TOTP MFA service."""
from __future__ import annotations
from typing import Optional
from uuid import UUID

import pyotp

from src.config import get_settings
from src.core.security.encryption import decrypt_value, encrypt_value
from src.domain.auth.mfa_repository import TotpSecretRepository

settings = get_settings()


class TotpService:
    """TOTP setup and verification."""

    def __init__(self, totp_repo: TotpSecretRepository):
        self._repo = totp_repo

    async def get_secret_if_enabled(self, user_id: UUID) -> Optional[str]:
        """Return decrypted secret if user has TOTP enabled."""
        row = await self._repo.get_by_user(user_id)
        if not row or not row.enabled_at:
            return None
        return decrypt_value(row.secret_encrypted)

    async def is_enabled(self, user_id: UUID) -> bool:
        row = await self._repo.get_by_user(user_id)
        return row is not None and row.enabled_at is not None

    async def setup(self, user_id: UUID, account_name: Optional[str] = None) -> dict:
        """Generate a TOTP secret labelled with the customer's recognizable account name."""
        existing = await self._repo.get_by_user(user_id)
        if existing:
            await self._repo.delete(user_id)

        secret = pyotp.random_base32()
        secret_encrypted = encrypt_value(secret)
        await self._repo.create(user_id, secret_encrypted)

        totp = pyotp.TOTP(secret)
        provisioning_uri = totp.provisioning_uri(
            # Keep the UUID fallback for non-dashboard callers, but never expose it
            # to a customer when the authenticated email is available.
            name=(account_name or user_id.hex).strip(),
            issuer_name=settings.webauthn_rp_name,
        )
        return {
            "secret": secret,
            "provisioning_uri": provisioning_uri,
        }

    async def enable(self, user_id: UUID, code: str) -> bool:
        """Verify first code and enable TOTP."""
        row = await self._repo.get_by_user(user_id)
        if not row:
            return False
        secret = decrypt_value(row.secret_encrypted)
        totp = pyotp.TOTP(secret)
        if not totp.verify(code, valid_window=1):
            return False
        await self._repo.enable(user_id)
        return True

    async def verify(self, user_id: UUID, code: str) -> bool:
        """Verify TOTP code for login."""
        secret = await self.get_secret_if_enabled(user_id)
        if not secret:
            return False
        totp = pyotp.TOTP(secret)
        return totp.verify(code, valid_window=1)

    async def disable(self, user_id: UUID, code: str) -> bool:
        """Verify code then remove TOTP."""
        if not await self.verify(user_id, code):
            return False
        await self._repo.delete(user_id)
        return True

    async def disable_by_user_id(self, user_id: UUID) -> None:
        """Remove TOTP without verification (recovery flow only)."""
        await self._repo.delete(user_id)
