"""API v2 authentication endpoints with enhanced security."""

from typing import Annotated, Optional
from uuid import UUID

from fastapi import APIRouter, Depends, HTTPException, status, Request, Header
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.ext.asyncio import AsyncSession

from src.api.deps import get_db
from src.api.v2.rate_limiter import TieredRateLimit
from src.core.security import (
    verify_access_token,
    verify_refresh_token,
    create_access_token,
    create_refresh_token,
)
from src.core.config import settings
from src.core.audit_logger import (
    audit_logger,
    AuditEventType,
    log_login,
    log_password_change,
)
from src.core.monitoring import metrics
from src.schemas.user import (
    UserCreate,
    UserLogin,
    UserResponse,
    AuthResponse,
    TokenRefresh,
    TokenResponse,
    PasswordChange,
)
from src.services.auth_service import (
    register_user,
    authenticate_user,
    change_password,
    get_user_by_id,
    create_tokens_for_user,
    EmailAlreadyExistsError,
    InvalidCredentialsError,
    InvalidPasswordError,
)


router = APIRouter()
security = HTTPBearer()
rate_limiter = TieredRateLimit()


async def get_current_user_v2(
    credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)],
    session: AsyncSession = Depends(get_db),
) -> UserResponse:
    """Get current authenticated user from JWT token (v2).

    Enhanced version with better error handling.
    """
    token = credentials.credentials
    payload = verify_access_token(token)

    if not payload:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid or expired token",
            headers={"WWW-Authenticate": "Bearer"},
        )

    user_id = payload.get("sub")
    if not user_id:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid token payload",
            headers={"WWW-Authenticate": "Bearer"},
        )

    user = await get_user_by_id(session, UUID(user_id))

    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="User not found",
            headers={"WWW-Authenticate": "Bearer"},
        )

    if not user.is_active:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="User account is disabled",
            headers={"WWW-Authenticate": "Bearer"},
        )

    return UserResponse.model_validate(user)


@router.post(
    "/register",
    response_model=AuthResponse,
    status_code=status.HTTP_201_CREATED,
    summary="Register new user",
    description="Register a new user account.",
    responses={
        201: {"description": "User registered successfully"},
        400: {"description": "Email already exists or validation error"},
        429: {"description": "Rate limit exceeded"},
    },
)
async def register(
    request: Request,
    user_data: UserCreate,
    session: AsyncSession = Depends(get_db),
):
    """Register a new user.

    Creates a new user account with email and password.
    """
    # Rate limiting (strict for registration)
    await rate_limiter.check_rate_limit(request, None, tier="free", burst=3)

    try:
        user = await register_user(
            session=session,
            email=user_data.email,
            password=user_data.password,
            full_name=user_data.full_name,
        )

        # Track metrics
        metrics.increment_counter("users_registered_total")
        metrics.increment_counter(
            "auth_attempts_total",
            labels={"type": "register", "success": "true"},
        )

        # Audit log
        audit_logger.log_auth_event(
            event_type=AuditEventType.USER_REGISTERED,
            user_id=user.id,
            user_email=user.email,
            ip_address=request.client.host if request.client else None,
            user_agent=request.headers.get("user-agent"),
        )

        # Create tokens
        access_token, refresh_token = create_tokens_for_user(user)

        return AuthResponse(
            user=UserResponse.model_validate(user),
            access_token=access_token,
            refresh_token=refresh_token,
        )

    except EmailAlreadyExistsError:
        metrics.increment_counter(
            "auth_attempts_total",
            labels={"type": "register", "success": "false"},
        )
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Email already registered",
        )
    except ValueError as e:
        raise HTTPException(
            status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
            detail=str(e),
        )


@router.post(
    "/login",
    response_model=TokenResponse,
    summary="User login",
    description="Authenticate user and get access tokens.",
    responses={
        200: {"description": "Login successful"},
        401: {"description": "Invalid credentials"},
        429: {"description": "Rate limit exceeded"},
    },
)
async def login(
    request: Request,
    credentials: UserLogin,
    session: AsyncSession = Depends(get_db),
):
    """Login with email and password.

    Returns access and refresh tokens on success.
    """
    # Rate limiting (strict for login)
    await rate_limiter.check_rate_limit(request, None, tier="free", burst=5)

    try:
        user = await authenticate_user(
            session=session,
            email=credentials.email,
            password=credentials.password,
        )

        if not user:
            # Track failed attempt
            metrics.increment_counter(
                "auth_attempts_total",
                labels={"type": "login", "success": "false"},
            )

            # Audit log
            log_login(
                user_id=None,
                user_email=credentials.email,
                ip_address=request.client.host if request.client else None,
                user_agent=request.headers.get("user-agent"),
                success=False,
                failure_reason="Invalid credentials",
            )

            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid email or password",
                headers={"WWW-Authenticate": "Bearer"},
            )

        # Track success
        metrics.increment_counter(
            "auth_attempts_total",
            labels={"type": "login", "success": "true"},
        )

        # Audit log
        log_login(
            user_id=user.id,
            user_email=user.email,
            ip_address=request.client.host if request.client else None,
            user_agent=request.headers.get("user-agent"),
            success=True,
        )

        access_token, refresh_token = create_tokens_for_user(user)

        return TokenResponse(
            access_token=access_token,
            refresh_token=refresh_token,
        )

    except InvalidCredentialsError:
        metrics.increment_counter(
            "auth_attempts_total",
            labels={"type": "login", "success": "false"},
        )
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid email or password",
            headers={"WWW-Authenticate": "Bearer"},
        )


@router.post(
    "/refresh",
    response_model=TokenResponse,
    summary="Refresh token",
    description="Get new access token using refresh token.",
    responses={
        200: {"description": "Token refreshed successfully"},
        401: {"description": "Invalid refresh token"},
    },
)
async def refresh_token(
    request: Request,
    token_data: TokenRefresh,
    session: AsyncSession = Depends(get_db),
):
    """Refresh access token using refresh token."""
    payload = verify_refresh_token(token_data.refresh_token)

    if not payload:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid or expired refresh token",
            headers={"WWW-Authenticate": "Bearer"},
        )

    user_id = payload.get("sub")
    user = await get_user_by_id(session, UUID(user_id))

    if not user or not user.is_active:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="User not found or inactive",
            headers={"WWW-Authenticate": "Bearer"},
        )

    # Audit log
    audit_logger.log_auth_event(
        event_type=AuditEventType.TOKEN_REFRESH,
        user_id=user.id,
        user_email=user.email,
        ip_address=request.client.host if request.client else None,
    )

    access_token, refresh_token = create_tokens_for_user(user)

    return TokenResponse(
        access_token=access_token,
        refresh_token=refresh_token,
    )


@router.get(
    "/me",
    response_model=UserResponse,
    summary="Get current user",
    description="Get information about the currently authenticated user.",
)
async def get_me(
    current_user: Annotated[UserResponse, Depends(get_current_user_v2)],
):
    """Get current user information."""
    return current_user


@router.post(
    "/change-password",
    status_code=status.HTTP_200_OK,
    summary="Change password",
    description="Change current user password.",
    responses={
        200: {"description": "Password changed successfully"},
        400: {"description": "Current password incorrect"},
        401: {"description": "Not authenticated"},
    },
)
async def change_user_password(
    request: Request,
    password_data: PasswordChange,
    current_user: Annotated[UserResponse, Depends(get_current_user_v2)],
    session: AsyncSession = Depends(get_db),
):
    """Change current user password."""
    try:
        await change_password(
            session=session,
            user_id=UUID(current_user.id),
            old_password=password_data.old_password,
            new_password=password_data.new_password,
        )

        # Audit log
        log_password_change(
            user_id=UUID(current_user.id),
            user_email=current_user.email,
            ip_address=request.client.host if request.client else None,
        )

        return {"message": "Password changed successfully"}

    except InvalidPasswordError:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Current password is incorrect",
        )


@router.post(
    "/logout",
    status_code=status.HTTP_200_OK,
    summary="Logout",
    description="Logout current user (client should discard tokens).",
)
async def logout(
    request: Request,
    current_user: Annotated[UserResponse, Depends(get_current_user_v2)],
):
    """Logout current user.

    Note: This endpoint is for client convenience. Actual logout is handled
    by discarding tokens on the client side.
    """
    # Audit log
    audit_logger.log_auth_event(
        event_type=AuditEventType.LOGOUT,
        user_id=UUID(current_user.id),
        user_email=current_user.email,
        ip_address=request.client.host if request.client else None,
        user_agent=request.headers.get("user-agent"),
    )

    return {"message": "Logged out successfully"}
