"""Authentication API endpoints."""

from typing import Annotated

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

from src.core.database import get_db
from src.core.security import verify_access_token, verify_refresh_token
from src.schemas.user import (
    UserCreate,
    UserLogin,
    UserResponse,
    AuthResponse,
    TokenRefresh,
    TokenResponse,
    PasswordChange,
    PasswordResetRequest,
    PasswordReset,
)
from src.services.auth_service import (
    register_user,
    authenticate_user,
    change_password,
    reset_password_request,
    reset_password,
    get_user_by_id,
    create_tokens_for_user,
    EmailAlreadyExistsError,
    InvalidCredentialsError,
    UserNotFoundError,
    InvalidPasswordError,
    InvalidTokenError,
)

router = APIRouter(prefix="/auth", tags=["authentication"])
security = HTTPBearer()


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

    Args:
        credentials: HTTP Authorization credentials with Bearer token
        session: Database session

    Returns:
        UserResponse object

    Raises:
        HTTPException: If token is invalid or user not found
    """
    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"},
        )

    from uuid import UUID

    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,
)
async def register(
    user_data: UserCreate,
    session: AsyncSession = Depends(get_db),
):
    """Register a new user.

    Args:
        user_data: User registration data
        session: Database session

    Returns:
        AuthResponse with user and tokens

    Raises:
        HTTPException: If email already exists or validation fails
    """
    try:
        user = await register_user(
            session=session,
            email=user_data.email,
            password=user_data.password,
            full_name=user_data.full_name,
        )
    except EmailAlreadyExistsError:
        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),
        )

    # 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,
    )


@router.post(
    "/login",
    response_model=TokenResponse,
)
async def login(
    credentials: UserLogin,
    session: AsyncSession = Depends(get_db),
):
    """Login with email and password.

    Args:
        credentials: Login credentials
        session: Database session

    Returns:
        TokenResponse with access and refresh tokens

    Raises:
        HTTPException: If credentials are invalid
    """
    user = await authenticate_user(
        session=session,
        email=credentials.email,
        password=credentials.password,
    )

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

    access_token, refresh_token = create_tokens_for_user(user)

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


@router.post(
    "/refresh",
    response_model=TokenResponse,
)
async def refresh_token(
    token_data: TokenRefresh,
    session: AsyncSession = Depends(get_db),
):
    """Refresh access token using refresh token.

    Args:
        token_data: Refresh token data
        session: Database session

    Returns:
        TokenResponse with new access and refresh tokens

    Raises:
        HTTPException: If refresh token is invalid
    """
    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"},
        )

    from uuid import UUID

    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"},
        )

    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,
)
async def get_me(
    current_user: Annotated[UserResponse, Depends(get_current_user)],
):
    """Get current user information.

    Returns:
        UserResponse with current user data
    """
    return current_user


@router.post(
    "/change-password",
    status_code=status.HTTP_200_OK,
)
async def change_user_password(
    password_data: PasswordChange,
    current_user: Annotated[UserResponse, Depends(get_current_user)],
    session: AsyncSession = Depends(get_db),
):
    """Change current user password.

    Args:
        password_data: Old and new password
        current_user: Current authenticated user
        session: Database session

    Returns:
        Success message

    Raises:
        HTTPException: If old password is incorrect
    """
    from uuid import UUID

    try:
        await change_password(
            session=session,
            user_id=UUID(current_user.id),
            old_password=password_data.old_password,
            new_password=password_data.new_password,
        )
    except InvalidPasswordError:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Current password is incorrect",
        )

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


@router.post(
    "/reset-password-request",
    status_code=status.HTTP_200_OK,
)
async def request_password_reset(
    request_data: PasswordResetRequest,
    session: AsyncSession = Depends(get_db),
):
    """Request a password reset.

    Args:
        request_data: Email for password reset
        session: Database session

    Returns:
        Success message (always returns success for security)
    """
    # Always return success to prevent email enumeration
    await reset_password_request(
        session=session,
        email=request_data.email,
    )

    return {
        "message": "If the email exists, a password reset link has been sent",
    }


@router.post(
    "/reset-password",
    status_code=status.HTTP_200_OK,
)
async def reset_user_password(
    reset_data: PasswordReset,
    session: AsyncSession = Depends(get_db),
):
    """Reset password using token.

    Args:
        reset_data: Token and new password
        session: Database session

    Returns:
        Success message

    Raises:
        HTTPException: If token is invalid
    """
    try:
        await reset_password(
            session=session,
            token=reset_data.token,
            new_password=reset_data.new_password,
        )
    except InvalidTokenError:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Invalid or expired token",
        )
    except UserNotFoundError:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="User not found",
        )

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