import uuid
from collections.abc import Sequence
from typing import Annotated, Any

from fastapi import Depends, HTTPException, Path, status
from sqlalchemy.ext.asyncio import AsyncSession

from tracecat import config
from tracecat.api.common import bootstrap_role
from tracecat.auth.credentials import (
    RoleACL,
)
from tracecat.auth.enums import AuthType
from tracecat.auth.types import Role
from tracecat.settings.constants import AUTH_TYPE_TO_SETTING_KEY
from tracecat.settings.service import get_setting

WorkspaceUserRole = Annotated[
    Role,
    RoleACL(
        allow_user=True,
        allow_service=False,
        allow_api_key=False,
        require_workspace="yes",
    ),
]
"""Dependency for a user role for a workspace.

Sets the `ctx_role` context variable.
"""

WorkspaceActorRole = Annotated[
    Role,
    RoleACL(
        allow_user=True,
        allow_service=False,
        allow_api_key=True,
        require_workspace="yes",
    ),
]
"""Dependency for a user or service-account role for a workspace."""

WorkspaceActorRouteRole = Annotated[
    Role,
    RoleACL(
        allow_user=True,
        allow_service=False,
        allow_api_key=True,
        require_workspace="yes",
        workspace_id_in_path="auto",
    ),
]
"""Dependency for workspace routes that accept path-scoped canonical routes and legacy query-scoped routes."""

WorkspaceUserRouteRole = Annotated[
    Role,
    RoleACL(
        allow_user=True,
        allow_service=False,
        allow_api_key=False,
        require_workspace="yes",
        workspace_id_in_path="auto",
    ),
]
"""Dependency for user-only workspace routes that accept path or legacy query workspace context."""


async def require_workspace_id_path(workspace_id: uuid.UUID = Path(...)) -> uuid.UUID:
    """Validate and document canonical workspace-scoped route prefixes."""
    return workspace_id


WorkspaceServiceAccountRole = Annotated[
    Role,
    RoleACL(
        allow_user=False,
        allow_service=False,
        allow_api_key=True,
        require_workspace="yes",
    ),
]
"""Dependency for a service-account role for a workspace."""

WorkspaceUserPathRole = Annotated[
    Role,
    RoleACL(
        allow_user=True,
        allow_service=False,
        require_workspace="yes",
        workspace_id_in_path=True,
    ),
]
"""Dependency for a user role for a path-scoped workspace.

Sets the `ctx_role` context variable.
"""


ExecutorWorkspaceRole = Annotated[
    Role,
    RoleACL(
        allow_user=False,
        allow_service=False,
        allow_executor=True,
        require_workspace="yes",
    ),
]
"""Dependency for an executor role for a workspace.

Sets the `ctx_role` context variable.
"""

ServiceRole = Annotated[
    Role, RoleACL(allow_user=False, allow_service=True, require_workspace="no")
]
"""Dependency for a service role.

Sets the `ctx_role` context variable.
"""

OrgUserRole = Annotated[
    Role,
    RoleACL(
        allow_user=True,
        allow_service=False,
        allow_api_key=False,
        require_workspace="no",
    ),
]
"""Dependency for a user role at the organization level (no workspace required).

Sets the `ctx_role` context variable.
"""


OrgUserOnlyRole = Annotated[
    Role,
    RoleACL(
        allow_user=True,
        allow_service=False,
        allow_api_key=False,
        require_workspace="no",
    ),
]
"""Dependency for a user-only role at the organization level (no workspace required)."""
OrgActorRole = Annotated[
    Role,
    RoleACL(
        allow_user=True,
        allow_service=False,
        allow_api_key=True,
        require_workspace="no",
    ),
]
"""Dependency for a user or service-account role at the organization level."""

OrganizationServiceAccountRole = Annotated[
    Role,
    RoleACL(
        allow_user=False,
        allow_service=False,
        allow_api_key=True,
        require_workspace="no",
    ),
]
"""Dependency for a service-account role at the organization level."""


async def verify_auth_type(
    auth_type: AuthType,
    *,
    role: Role | None = None,
    session: AsyncSession | None = None,
) -> None:
    """Verify if an auth type is enabled and properly configured.

    Args:
        auth_type: The authentication type to verify
        role: Optional role to use for org-scoped setting lookups
        session: Optional database session to reuse for setting lookups

    Raises:
        HTTPException: If the auth type is not allowed or not enabled
        ValueError: If the auth type is invalid
    """

    # 1. Check that this auth type is allowed
    if auth_type not in config.TRACECAT__AUTH_TYPES:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Auth type not allowed",
        )

    # OIDC/basic availability is platform-configured, not org-setting controlled.
    if auth_type in {AuthType.BASIC, AuthType.OIDC}:
        return

    # 2. Check that the setting is enabled
    key = AUTH_TYPE_TO_SETTING_KEY[auth_type]
    setting_role = role or bootstrap_role()
    # NOTE: These settings were introduced after org settings implemented
    # so no defaults required
    setting = await get_setting(key=key, role=setting_role, session=session)
    if setting is None or not isinstance(setting, bool):
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Invalid setting configuration",
        )
    if not setting:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail=f"Auth type {auth_type} is not enabled",
        )


def require_auth_type_enabled(auth_type: AuthType) -> Any:
    """FastAPI dependency to check if an auth type is enabled.

    Args:
        auth_type: The authentication type to check

    Returns:
        FastAPI dependency that verifies the auth type
    """

    if auth_type not in AUTH_TYPE_TO_SETTING_KEY and auth_type not in {
        AuthType.BASIC,
        AuthType.OIDC,
    }:
        raise ValueError(f"Invalid auth type: {auth_type}")

    async def _check_auth_type_enabled() -> None:
        await verify_auth_type(auth_type)

    return Depends(_check_auth_type_enabled)


def require_any_auth_type_enabled(auth_types: Sequence[AuthType]) -> Any:
    """FastAPI dependency to allow any one of the provided auth types."""
    candidate_types = tuple(dict.fromkeys(auth_types))
    if not candidate_types:
        raise ValueError("auth_types must not be empty")

    async def _check_any_auth_type_enabled() -> None:
        for auth_type in candidate_types:
            if auth_type in config.TRACECAT__AUTH_TYPES:
                await verify_auth_type(auth_type)
                return
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Auth type not allowed",
        )

    return Depends(_check_any_auth_type_enabled)
