"""
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 uuid import UUID as UUIDType

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from pydantic import EmailStr

from apex.app.core.dependencies import get_organization_model, get_user_model
from apex.app.models import BaseOrganization
from apex.api.v1.schemas.auth import (
    LoginResponse,
    LogoutRequest,
    LogoutResponse,
    SignupResponse,
    UserLogin,
    UserRegister,
)
from apex.api.v1.schemas.user import UserResponse
from apex.api.v1.schemas.password_reset import (
    ChangePasswordRequest,
    ForgotPasswordRequest,
    PasswordResetResponse,
    ResetPasswordRequest,
)
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(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.",
    )


def get_user_service(
    db: AsyncSession = Depends(get_db),
) -> UserService:
    """
    Dependency to get user service.

    Users MUST override this dependency to provide their concrete User model.
    """
    raise HTTPException(
        status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
        detail="User service not configured. Please override get_user_service dependency with your User model.",
    )


@router.post("/signup", response_model=SignupResponse, status_code=status.HTTP_201_CREATED)
async def signup(
    payload: UserRegister,
    user_service: UserService = Depends(get_user_service),
    organization_model: type[BaseOrganization] = Depends(get_organization_model),
):
    """
    Signup endpoint.

    Creates a new user record. Applications should override the `get_user_service`
    dependency to provide their concrete `User` model as well as handle any
    organization logic they require.
    """
    existing_user = await user_service.get_user_by_email(payload.email)
    if existing_user:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="User with this email already exists.",
        )

    organization_id: UUIDType | None = None
    if payload.organization_name:
        stmt = select(organization_model).where(organization_model.name == payload.organization_name)
        result = await user_service.session.execute(stmt)
        organization = result.scalar_one_or_none()
        if not organization:
            organization = organization_model(name=payload.organization_name)
            user_service.session.add(organization)
            await user_service.session.flush()
        organization_id = organization.id

    username_value = payload.first_name or payload.last_name or payload.email.split("@")[0]

    user = await user_service.create_user(
        email=payload.email,
        password=payload.password,
        first_name=payload.first_name,
        last_name=payload.last_name,
        phone=payload.phone,
        country=payload.country,
        username=username_value,
        organization_id=organization_id,
    )

    await user_service.session.commit()
    await user_service.session.refresh(user)
    return SignupResponse(
        success=True,
        message="Signup successful",
        user_id=user.id,
        email=user.email,
        username=user.username,
    )


@router.post("/login", response_model=LoginResponse, status_code=status.HTTP_200_OK)
async def login(
    credentials: UserLogin,
    auth_service: AuthService = Depends(get_auth_service),
):
    """
    Login endpoint using JSON payload only.
    """
    user = await auth_service.authenticate_user(credentials.email, credentials.password)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid email or password",
        )

    return LoginResponse(success=True, message="Login successful", user_id=user.id)


@router.post("/logout", response_model=LogoutResponse, status_code=status.HTTP_200_OK)
async def logout(
    request: LogoutRequest,
    user_service: UserService = Depends(get_user_service),
):
    """
    Logout endpoint.

    Returns a simple success message after verifying the user exists.
    """
    user = await user_service.get_user_by_email(request.email)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User not found.",
        )

    return LogoutResponse(success=True, message="User logged out successfully.")


@router.get("/me", response_model=UserResponse, status_code=status.HTTP_200_OK)
async def get_current_user_info(
    email: EmailStr,
    user_service: UserService = Depends(get_user_service),
):
    """
    Get current user information using an email identifier.
    """
    user = await user_service.get_user_by_email(email)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User not found.",
        )
    return user


@router.post("/forgot-password", response_model=PasswordResetResponse)
async def forgot_password(
    request: ForgotPasswordRequest,
    db: AsyncSession = Depends(get_db),
    user_model: type[BaseUser] = Depends(get_user_model),
):
    """
    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=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),
    user_model: type[BaseUser] = Depends(get_user_model),
):
    """
    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=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,
    user_service: UserService = Depends(get_user_service),
):
    """
    Change a user's password using email + current password verification.
    """
    user = await user_service.get_user_by_email(request.email)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="User not found.",
        )

    changed = await user_service.change_password(user, request.old_password, request.new_password)
    if not changed:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Old password is incorrect.",
        )

    await user_service.session.commit()

    return PasswordResetResponse(
        message="Password changed successfully.",
        success=True,
    )

