"""
Authentication router.

This is a base implementation that provides the structure for auth endpoints.
Users should extend this to work with their specific User model.
"""

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

from apex.api.v1.schemas.auth import Token, TokenRefresh, TokenRefreshResponse, UserLogin
from apex.api.v1.schemas.password_reset import (
    ChangePasswordRequest,
    ForgotPasswordRequest,
    PasswordResetResponse,
    ResetPasswordRequest,
)
from apex.core.authentication.dependencies import get_current_active_user
from apex.domain.models.user import BaseUser
from apex.domain.services.auth import AuthService
from apex.domain.services.password_reset import PasswordResetService
from apex.domain.services.password_reset_sendgrid import PasswordResetWithEmailService
from apex.domain.services.user import UserService
from apex.infrastructure.database.session import get_db

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


# Note: Users should override this dependency in their application
# Example:
# def get_auth_service(db: AsyncSession = Depends(get_db)) -> AuthService:
#     from app.models import User
#     return AuthService(session=db, user_model=User)
#
# app.dependency_overrides[get_auth_service] = get_auth_service

def get_auth_service(
    db: AsyncSession = Depends(get_db),
) -> AuthService:
    """
    Dependency to get auth service.

    This is a placeholder. Users MUST override this to provide their User model.
    See module docstring for example.
    """
    raise HTTPException(
        status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
        detail="Auth service not configured. Please override get_auth_service dependency with your User model.",
    )


@router.post("/login", response_model=Token, status_code=status.HTTP_200_OK)
async def login(
    form_data: OAuth2PasswordRequestForm = Depends(),
    auth_service: AuthService = Depends(get_auth_service),
):
    """
    Login endpoint.

    This is a base implementation. Users should override get_auth_service
    to provide their User model.

    Args:
        form_data: OAuth2 form data (username=email, password)
        auth_service: Auth service instance

    Returns:
        Token response with access and refresh tokens

    Raises:
        HTTPException: If credentials are invalid
    """
    user = await auth_service.authenticate_user(form_data.username, form_data.password)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect email or password",
            headers={"WWW-Authenticate": "Bearer"},
        )

    tokens = await auth_service.create_tokens(user)
    return tokens


@router.post("/login/json", response_model=Token, status_code=status.HTTP_200_OK)
async def login_json(
    credentials: UserLogin,
    auth_service: AuthService = Depends(get_auth_service),
):
    """
    Login endpoint (JSON body).

    Args:
        credentials: Login credentials
        auth_service: Auth service instance

    Returns:
        Token response with access and refresh tokens

    Raises:
        HTTPException: If credentials are invalid
    """
    user = await auth_service.authenticate_user(credentials.email, credentials.password)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect email or password",
            headers={"WWW-Authenticate": "Bearer"},
        )

    tokens = await auth_service.create_tokens(user)
    return tokens


@router.post("/refresh", response_model=TokenRefreshResponse, status_code=status.HTTP_200_OK)
async def refresh_token(
    token_data: TokenRefresh,
    auth_service: AuthService = Depends(get_auth_service),
):
    """
    Refresh access token.

    Args:
        token_data: Refresh token
        auth_service: Auth service instance

    Returns:
        New access token

    Raises:
        HTTPException: If refresh token is invalid
    """
    tokens = await auth_service.refresh_access_token(token_data.refresh_token)
    if not tokens:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid refresh token",
        )

    return tokens


@router.post("/logout", status_code=status.HTTP_200_OK)
async def logout(
    current_user: dict = Depends(get_current_active_user),
):
    """
    Logout endpoint.

    Note: With JWT tokens, logout is typically handled client-side by
    removing the token. This endpoint can be used for token blacklisting
    if implemented.

    Args:
        current_user: Current authenticated user

    Returns:
        Success message
    """
    # In a production system, you might want to blacklist the token here
    return {"message": "Successfully logged out"}


@router.get("/me", status_code=status.HTTP_200_OK)
async def get_current_user_info(
    current_user: dict = Depends(get_current_active_user),
):
    """
    Get current user information.

    Args:
        current_user: Current authenticated user

    Returns:
        Current user information
    """
    return current_user


@router.post("/forgot-password", response_model=PasswordResetResponse)
async def forgot_password(
    request: ForgotPasswordRequest,
    db: AsyncSession = Depends(get_db),
):
    """
    Request password reset via email.

    Sends a password reset link to the user's email using SendGrid.
    Always returns success to prevent email enumeration attacks.

    Args:
        request: Forgot password request with email
        db: Database session

    Returns:
        Success message (always returns success for security)
    """
    # Create password reset service with SendGrid integration
    # Note: Users should override get_user_model() to provide their User model
    try:
        service = PasswordResetWithEmailService(
            session=db,
            user_model=BaseUser,  # Users should override with their User model
        )
        
        # Request password reset (sends email via SendGrid)
        await service.request_password_reset(request.email)
    except Exception as e:
        # Log error but don't expose it (security: prevent enumeration)
        print(f"Password reset error: {str(e)}")
    
    # Always return success to prevent email enumeration
    return PasswordResetResponse(
        message="If the email exists, a password reset link has been sent",
        success=True,
    )


@router.post("/reset-password", response_model=PasswordResetResponse)
async def reset_password(
    request: ResetPasswordRequest,
    db: AsyncSession = Depends(get_db),
):
    """
    Reset password using token from email.

    Validates the reset token and updates the user's password.

    Args:
        request: Reset password request with token and new password
        db: Database session

    Returns:
        Success or error message

    Raises:
        HTTPException: If token is invalid or expired
    """
    # Create password reset service
    service = PasswordResetWithEmailService(
        session=db,
        user_model=BaseUser,  # Users should override with their User model
    )
    
    # Attempt to reset password
    success = await service.reset_password(request.token, request.new_password)
    
    if success:
        return PasswordResetResponse(
            message="Password has been reset successfully. You can now login with your new password.",
            success=True,
        )
    else:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Invalid or expired reset token. Please request a new password reset.",
        )


@router.post("/change-password", response_model=PasswordResetResponse)
async def change_password(
    request: ChangePasswordRequest,
    current_user: dict = Depends(get_current_active_user),
    db: AsyncSession = Depends(get_db),
):
    """
    Change user password.

    This is a base implementation. Users should override this to
    provide their User model.

    Args:
        request: Change password request
        current_user: Current authenticated user
        db: Database session

    Returns:
        Success or error message
    """
    # Users need to implement this with their User model
    return PasswordResetResponse(
        message="This endpoint needs to be configured with your User model",
        success=False,
    )

