"""Shared FastAPI dependencies for policy enforcement."""
from __future__ import annotations
from uuid import UUID

from fastapi import Depends, Path, Response

DEPRECATION_MSG = (
    "Deprecated. Use project-rooted /api/projects/{project_id}/... instead. "
    "See OpenAPI docs for migration path."
)


def add_deprecation_header(response: Response, migration_path: str = "") -> None:
    """Add X-Deprecation header to response. Call at start of deprecated endpoints."""
    msg = DEPRECATION_MSG
    if migration_path:
        msg += f" Migrate to: {migration_path}"
    response.headers["X-Deprecation"] = msg
from sqlalchemy.ext.asyncio import AsyncSession

from src.core.database import get_db
from src.core.exceptions import NotFoundError
from src.domain.auth.mfa_repository import TotpSecretRepository, WebAuthnCredentialRepository
from src.domain.auth.models import User
from src.domain.auth.router import get_current_user
from src.domain.billing.access_policy import BillingAccessPolicy
from src.domain.billing.productive_access_policy import ProductiveAccessPolicy
from src.domain.billing.repository import SubscriptionRepository
from src.domain.deployments.repository import DeployRepository
from src.domain.previews.repository import PreviewRepository
from src.domain.projects.repository import ProjectRepository
from src.domain.workspaces.repository import MembershipRepository, WorkspaceRepository
from src.core.exceptions import ForbiddenError


async def require_workspace_codegen_admin(
    workspace_id: UUID = Path(..., description="Workspace ID"),
    user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
) -> UUID:
    """Require workspace-management permission for Codegen settings and credentials."""
    if not await MembershipRepository(db).has_permission(workspace_id, user.id, "workspaces.manage"):
        raise ForbiddenError("Workspace admin permission required to manage Codegen settings")
    return workspace_id


async def require_project_codegen_admin(
    project: UUID = Path(..., description="Project ID"),
    user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
) -> UUID:
    """Require workspace-management permission for a project's Codegen override."""
    project_row = await ProjectRepository(db).get_by_id(project)
    if not project_row:
        raise NotFoundError("Project not found")
    if not await MembershipRepository(db).has_permission(project_row.workspace_id, user.id, "workspaces.manage"):
        raise ForbiddenError("Workspace admin permission required to manage Codegen settings")
    return project


async def get_billing_access_policy(db: AsyncSession = Depends(get_db)) -> BillingAccessPolicy:
    """Reusable billing-only policy (402). Prefer ``get_productive_access_policy`` for project routes."""
    return BillingAccessPolicy(SubscriptionRepository(db), WorkspaceRepository(db))


async def get_productive_access_policy(db: AsyncSession = Depends(get_db)) -> ProductiveAccessPolicy:
    """Billing (paid or free tier) + MFA only for paid workspaces when policy requires it."""
    return ProductiveAccessPolicy(
        billing=BillingAccessPolicy(SubscriptionRepository(db), WorkspaceRepository(db)),
        workspace_repo=WorkspaceRepository(db),
        membership_repo=MembershipRepository(db),
        totp_repo=TotpSecretRepository(db),
        webauthn_repo=WebAuthnCredentialRepository(db),
    )


async def require_active_billing_for_workspace(
    ws: UUID = Path(..., description="Workspace ID"),
    user: User = Depends(get_current_user),
    policy: ProductiveAccessPolicy = Depends(get_productive_access_policy),
) -> UUID:
    """
    Billing gate aligned with bootstrap: paid (resolved subscription) or **free** tier without
    outstanding payment. Raises 402 when payment is still required.

    MFA (403) applies only to **paid** workspaces when the workspace security policy requires it.
    """
    await policy.require_for_workspace(ws, user.id)
    return ws


async def require_active_billing_for_project(
    project: UUID = Path(..., description="Project ID"),
    user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
    policy: ProductiveAccessPolicy = Depends(get_productive_access_policy),
) -> UUID:
    """Resolve project → workspace; require paid + MFA policy."""
    proj = await ProjectRepository(db).get_by_id(project)
    if not proj:
        raise NotFoundError("Project not found")
    await policy.require_for_workspace(proj.workspace_id, user.id)
    return project


async def require_active_billing_for_project_id(
    project_id: UUID = Path(..., description="Project ID"),
    user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
    policy: ProductiveAccessPolicy = Depends(get_productive_access_policy),
) -> UUID:
    """Same as ``require_active_billing_for_project`` for ``project_id`` path param."""
    proj = await ProjectRepository(db).get_by_id(project_id)
    if not proj:
        raise NotFoundError("Project not found")
    await policy.require_for_workspace(proj.workspace_id, user.id)
    return project_id


async def require_active_billing_for_workspace_id(
    workspace_id: UUID = Path(..., description="Workspace ID"),
    user: User = Depends(get_current_user),
    policy: ProductiveAccessPolicy = Depends(get_productive_access_policy),
) -> UUID:
    """Billing + MFA gate for workspace-rooted endpoints using ``workspace_id`` path param."""
    await policy.require_for_workspace(workspace_id, user.id)
    return workspace_id


async def require_active_billing_for_deploy(
    deploy: UUID = Path(..., description="Deploy ID"),
    user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
    policy: ProductiveAccessPolicy = Depends(get_productive_access_policy),
) -> UUID:
    """Resolve deploy → workspace; require paid + MFA policy."""
    deploy_obj = await DeployRepository(db).get_by_id(deploy)
    if not deploy_obj:
        raise NotFoundError("Deploy not found")
    proj = await ProjectRepository(db).get_by_id(deploy_obj.project_id)
    if not proj:
        raise NotFoundError("Project not found")
    await policy.require_for_workspace(proj.workspace_id, user.id)
    return deploy


async def require_active_billing_for_preview(
    preview: UUID = Path(..., description="Preview ID"),
    user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
    policy: ProductiveAccessPolicy = Depends(get_productive_access_policy),
) -> UUID:
    """Resolve preview → workspace; require paid + MFA policy."""
    prev = await PreviewRepository(db).get_by_id(preview)
    if not prev:
        raise NotFoundError("Preview not found")
    proj = await ProjectRepository(db).get_by_id(prev.project_id)
    if not proj:
        raise NotFoundError("Project not found")
    await policy.require_for_workspace(proj.workspace_id, user.id)
    return preview
