"""Permission, role, and rate limiting decorators for FastAPI."""

import time
from collections import defaultdict
from threading import Lock
from typing import Callable

from fastapi import Depends, HTTPException, Request

from ..permissions import has_all_permissions, has_any_permission, has_permission


def require_permission(permission: str):
    """Dependency that requires a specific permission.

    Supports wildcards:
    - User with "vessels:*" can access "vessels:read"
    - User with "*:*" can access anything

    Example:
        @router.get("/vessels")
        async def list_vessels(
            _: None = Depends(require_permission("vessels:read"))
        ):
            ...

    Args:
        permission: Required permission string (e.g., "vessels:read")

    Returns:
        FastAPI dependency
    """

    async def dependency(request: Request) -> None:
        permissions = getattr(request.state, "permissions", None)
        if permissions is None:
            raise HTTPException(status_code=401, detail="Not authenticated")

        if not has_permission(permissions, permission):
            raise HTTPException(
                status_code=403,
                detail=f"Missing required permission: {permission}",
            )

    return Depends(dependency)


def require_any_permission(*permissions: str):
    """Dependency that requires any of the specified permissions.

    Example:
        @router.get("/vessels")
        async def list_vessels(
            _: None = Depends(require_any_permission("vessels:read", "admin:*"))
        ):
            ...

    Args:
        *permissions: Permission strings where at least one is required

    Returns:
        FastAPI dependency
    """

    async def dependency(request: Request) -> None:
        user_permissions = getattr(request.state, "permissions", None)
        if user_permissions is None:
            raise HTTPException(status_code=401, detail="Not authenticated")

        if not has_any_permission(user_permissions, list(permissions)):
            raise HTTPException(
                status_code=403,
                detail=f"Missing required permission. Need one of: {', '.join(permissions)}",
            )

    return Depends(dependency)


def require_all_permissions(*permissions: str):
    """Dependency that requires all of the specified permissions.

    Example:
        @router.delete("/vessels/{id}")
        async def delete_vessel(
            _: None = Depends(require_all_permissions("vessels:read", "vessels:delete"))
        ):
            ...

    Args:
        *permissions: Permission strings that are all required

    Returns:
        FastAPI dependency
    """

    async def dependency(request: Request) -> None:
        user_permissions = getattr(request.state, "permissions", None)
        if user_permissions is None:
            raise HTTPException(status_code=401, detail="Not authenticated")

        if not has_all_permissions(user_permissions, list(permissions)):
            raise HTTPException(
                status_code=403,
                detail=f"Missing required permissions. Need all of: {', '.join(permissions)}",
            )

    return Depends(dependency)


def require_role(role: str):
    """Dependency that requires a specific role.

    Example:
        @router.post("/users")
        async def create_user(
            _: None = Depends(require_role("admin"))
        ):
            ...

    Args:
        role: Required role name/slug

    Returns:
        FastAPI dependency
    """

    async def dependency(request: Request) -> None:
        roles = getattr(request.state, "roles", None)
        if roles is None:
            raise HTTPException(status_code=401, detail="Not authenticated")

        if role not in roles:
            raise HTTPException(
                status_code=403,
                detail=f"Missing required role: {role}",
            )

    return Depends(dependency)


def require_any_role(*roles: str):
    """Dependency that requires any of the specified roles.

    Example:
        @router.get("/admin/dashboard")
        async def admin_dashboard(
            _: None = Depends(require_any_role("admin", "superadmin"))
        ):
            ...

    Args:
        *roles: Role names/slugs where at least one is required

    Returns:
        FastAPI dependency
    """

    async def dependency(request: Request) -> None:
        user_roles = getattr(request.state, "roles", None)
        if user_roles is None:
            raise HTTPException(status_code=401, detail="Not authenticated")

        if not any(role in user_roles for role in roles):
            raise HTTPException(
                status_code=403,
                detail=f"Missing required role. Need one of: {', '.join(roles)}",
            )

    return Depends(dependency)


def require_tenant_match(tenant_id_param: str = "tenant_id"):
    """Dependency that requires the path parameter tenant_id to match the user's tenant.

    Example:
        @router.get("/tenants/{tenant_id}/vessels")
        async def list_tenant_vessels(
            tenant_id: str,
            _: None = Depends(require_tenant_match())
        ):
            ...

    Args:
        tenant_id_param: Name of the path parameter containing tenant ID

    Returns:
        FastAPI dependency
    """

    async def dependency(request: Request) -> None:
        user_tenant = getattr(request.state, "tenant_id", None)
        if user_tenant is None:
            raise HTTPException(status_code=401, detail="Not authenticated")

        path_tenant = request.path_params.get(tenant_id_param)
        if path_tenant and path_tenant != user_tenant:
            raise HTTPException(
                status_code=403,
                detail="Cannot access resources from another tenant",
            )

    return Depends(dependency)


class RateLimiter:
    """Thread-safe rate limiter using sliding window.

    This rate limiter tracks requests per key (e.g., tenant_id:endpoint)
    and enforces limits within a time window.

    Example:
        limiter = RateLimiter()

        @router.get("/search")
        async def search(
            _: None = Depends(rate_limit(requests=10, window=60, limiter=limiter))
        ):
            ...
    """

    def __init__(self):
        """Initialize rate limiter."""
        self._buckets: dict[str, list[float]] = defaultdict(list)
        self._lock = Lock()

    def check(self, key: str, limit: int, window: int) -> bool:
        """Check if request is allowed.

        Args:
            key: Unique key for rate limiting (e.g., "tenant_id:endpoint")
            limit: Max requests allowed in window
            window: Time window in seconds

        Returns:
            True if allowed, False if rate limited
        """
        now = time.time()

        with self._lock:
            # Clean old entries
            self._buckets[key] = [t for t in self._buckets[key] if now - t < window]

            if len(self._buckets[key]) >= limit:
                return False

            self._buckets[key].append(now)
            return True

    def time_until_allowed(self, key: str, window: int) -> float:
        """Get seconds until next request is allowed.

        Args:
            key: Rate limit key
            window: Time window in seconds

        Returns:
            Seconds until next request is allowed (0 if allowed now)
        """
        now = time.time()

        with self._lock:
            if not self._buckets[key]:
                return 0

            oldest = min(self._buckets[key])
            return max(0, window - (now - oldest))

    def reset(self, key: str) -> None:
        """Reset rate limit for a specific key.

        Args:
            key: Rate limit key to reset
        """
        with self._lock:
            self._buckets[key] = []


_default_limiter = RateLimiter()


def rate_limit(
    requests: int = 100,
    window: int = 60,
    key_func: Callable[[Request], str] | None = None,
    limiter: RateLimiter | None = None,
):
    """Dependency that enforces rate limiting.

    Example:
        @router.get("/api/v1/search")
        async def search(
            _: None = Depends(rate_limit(requests=10, window=60))
        ):
            ...

    Args:
        requests: Max requests allowed in window
        window: Time window in seconds
        key_func: Function to extract rate limit key from request
                  Default: tenant_id:path
        limiter: Custom RateLimiter instance (default: global instance)

    Returns:
        FastAPI dependency
    """
    actual_limiter = limiter or _default_limiter

    def default_key_func(request: Request) -> str:
        tenant_id = getattr(request.state, "tenant_id", "anonymous")
        return f"{tenant_id}:{request.url.path}"

    actual_key_func = key_func or default_key_func

    async def dependency(request: Request) -> None:
        key = actual_key_func(request)

        if not actual_limiter.check(key, requests, window):
            retry_after = int(actual_limiter.time_until_allowed(key, window))
            raise HTTPException(
                status_code=429,
                detail="Rate limit exceeded",
                headers={"Retry-After": str(retry_after)},
            )

    return Depends(dependency)
