"""
Authentication utilities and dependencies for FastAPI.
"""

import jwt
from datetime import datetime, timedelta, timezone
from typing import Optional, Union, TYPE_CHECKING
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.orm import Session
from sqlalchemy import select
from src.apps.merchants.models.merchant import Merchant
from src.apps.merchants.models.merchant_users import MerchantUsers

# Use TYPE_CHECKING to avoid circular imports
if TYPE_CHECKING:
    from src.apps.users.models.user import User
    from src.apps.auth.models.auth_session import AuthSession

from src.core.database import get_db
from src.apps.auth.utils.jwt import jwt_manager

# HTTP Bearer token security scheme
security = HTTPBearer()


async def get_current_user_optional(
    credentials: Optional[HTTPAuthorizationCredentials] = Depends(security),
    db: Session = Depends(get_db)
) -> Optional["User"]:
    """
    Get current user from JWT token (optional - returns None if no token or invalid).
    
    Args:
        credentials: HTTP Bearer token credentials
        db: Database session
        
    Returns:
        User model or None if not authenticated
    """
    if not credentials:
        return None
    
    return await _verify_token_and_get_user(credentials.credentials, db)


async def get_current_user(
    credentials: HTTPAuthorizationCredentials = Depends(security),
    db: Session = Depends(get_db)
) -> "User":
    """
    Get current user from JWT token (required - raises exception if not authenticated).
    
    Args:
        credentials: HTTP Bearer token credentials
        db: Database session
        
    Returns:
        User model
        
    Raises:
        HTTPException: If authentication fails
    """
    user = await _verify_token_and_get_user(credentials.credentials, db)
    
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Could not validate credentials",
            headers={"WWW-Authenticate": "Bearer"},
        )
    
    return user


async def get_current_active_user(
    current_user: "User" = Depends(get_current_user)
) -> "User":
    """
    Get current active user (must be authenticated and active).
    
    Args:
        current_user: Current authenticated user
        
    Returns:
        Active user model
        
    Raises:
        HTTPException: If user is inactive
    """
    if not current_user.is_user_active:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Inactive user"
        )
    
    return current_user


async def get_current_verified_user(
    current_user: "User" = Depends(get_current_active_user)
) -> "User":
    """
    Get current verified user (must be authenticated, active, and verified).
    
    Args:
        current_user: Current active user
        
    Returns:
        Verified user model
        
    Raises:
        HTTPException: If user is not verified
    """
    if not current_user.is_user_verified:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Email not verified"
        )
    
    return current_user


async def get_current_superuser(
    current_user: "User" = Depends(get_current_active_user)
) -> "User":
    """
    Get current superuser (must be authenticated, active, and superuser).
    
    Args:
        current_user: Current active user
        
    Returns:
        Superuser model
        
    Raises:
        HTTPException: If user is not a superuser
    """
    if not current_user.is_superuser:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Not enough permissions"
        )
    
    return current_user


async def _verify_token_and_get_user(token: str, db: Session) -> Optional["User"]:
    """
    Verify JWT token and return the associated user.
    
    Args:
        token: JWT token string
        db: Database session
        
    Returns:
        User model or None if invalid
    """
    # Import models here to avoid circular imports
    from src.apps.users.models.user import User
    from src.apps.auth.models.auth_session import AuthSession
    
    # Verify JWT token
    payload = jwt_manager.verify_token(token)
    if not payload:
        return None
    
    # Extract user information from token payload
    user_data = payload.get("user")
    
    if not user_data:
        return None
    
    user_id = user_data.get("user_id")
    if not user_id:
        return None
    
    # Check if session exists and is active using the actual token - SQLAlchemy 2.0 syntax
    stmt = select(AuthSession).where(
        AuthSession.access_token == token,
        AuthSession.is_active == True,
        AuthSession.is_revoked == False
    )
    session = db.execute(stmt).scalar_one_or_none()
    
    if not session:
        return None
    
    # Check if session is expired
    if session.is_expired():
        session.revoke()
        db.commit()
        return None
    
    # Get user - SQLAlchemy 2.0 syntax
    user_stmt = select(User).where(User.id == user_id)
    user = db.execute(user_stmt).scalar_one_or_none()
    if not user:
        return None
    
    # Update session activity
    session.update_activity()
    db.commit()
    
    return user


async def get_current_merchant(
    current_user: "User" = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """
    Get current merchant from authenticated user.
    
    Args:
        current_user: Current authenticated user
        db: Database session
        
    Returns:
        Merchant model associated with the user
        
    Raises:
        HTTPException: If no merchant found for user
    """
    # Get merchant associated with user through MerchantUsers junction table
    stmt = (
        select(Merchant)
        .join(MerchantUsers, Merchant.id == MerchantUsers.merchant_id)
        .where(MerchantUsers.user_id == current_user.id)
    )
    merchant = db.execute(stmt).scalar_one_or_none()
    
    if not merchant:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="No merchant found for current user"
        )
    
    return merchant


def require_auth(func):
    """
    Decorator to require authentication for a function.
    
    Usage:
        @require_auth
        def my_protected_function(current_user: User = Depends(get_current_user)):
            pass
    """
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper
