"""Paid workspace productive access: active/trialing billing + MFA when policy requires."""
from __future__ import annotations

import structlog
from uuid import UUID

from src.core.exceptions import MfaRequiredError, NotFoundError
from src.domain.auth.mfa_repository import TotpSecretRepository, WebAuthnCredentialRepository
from src.domain.auth.totp_service import TotpService
from src.domain.billing.access_policy import BillingAccessPolicy
from src.domain.billing.normalization import is_paid_billing_status
from src.domain.workspaces.repository import MembershipRepository, WorkspaceRepository

logger = structlog.get_logger()


class ProductiveAccessPolicy:
    """
    Enforces billing + MFA for project-scoped product actions.

    Depends on BillingAccessPolicy for subscription checks (402).
    Raises MfaRequiredError (403) when workspace is paid-active but user has no MFA method.
    """

    def __init__(
        self,
        billing: BillingAccessPolicy,
        workspace_repo: WorkspaceRepository,
        membership_repo: MembershipRepository,
        totp_repo: TotpSecretRepository,
        webauthn_repo: WebAuthnCredentialRepository,
    ):
        self._billing = billing
        self._workspace_repo = workspace_repo
        self._membership_repo = membership_repo
        self._totp_repo = totp_repo
        self._webauthn_repo = webauthn_repo

    async def _user_has_any_mfa(self, user_id: UUID) -> bool:
        totp = TotpService(self._totp_repo)
        if await totp.is_enabled(user_id):
            return True
        return len(await self._webauthn_repo.list_for_user(user_id)) > 0

    async def require_for_workspace(self, workspace_id: UUID, user_id: UUID) -> None:
        await self._billing.require_active_billing(workspace_id)
        if await self._membership_repo.get(workspace_id, user_id) is None:
            logger.info(
                "workspace_blocked_not_a_member",
                workspace_id=str(workspace_id),
                user_id=str(user_id),
            )
            # Same message as missing project to avoid cross-tenant resource enumeration.
            raise NotFoundError("Project not found")
        ws = await self._workspace_repo.get_by_id(workspace_id)
        if not ws:
            return
        billing_status = await self._billing.get_billing_status(workspace_id)
        if not is_paid_billing_status(billing_status):
            return
        if not getattr(ws, "security_policy_require_mfa_for_paid_users", True):
            return
        if await self._user_has_any_mfa(user_id):
            return
        logger.info(
            "workspace_blocked_mfa_required",
            workspace_id=str(workspace_id),
            user_id=str(user_id),
        )
        raise MfaRequiredError(workspace_id=str(workspace_id))
