"""
Authentication middleware and utilities
Handles LDAP authentication and JWT token management
"""

import jwt
from datetime import datetime, timedelta
from typing import Optional, Dict, Any
from fastapi import HTTPException, status, Depends
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import logging

from config.settings import settings
from ..services.ldap_auth_service import ldap_auth_service

logger = logging.getLogger(__name__)

# Security scheme for JWT tokens
security = HTTPBearer()

class AuthenticationError(Exception):
    """Authentication exception"""
    pass

class AuthorizationError(Exception):
    """Authorization exception"""
    pass

class AuthManager:
    """Authentication and authorization manager"""
    
    def __init__(self):
        self.secret_key = settings.secret_key
        self.algorithm = "HS256"
        self.token_expire_hours = settings.session_timeout_minutes / 60
    
    def create_access_token(self, user_data: Dict[str, Any]) -> str:
        """Create JWT access token"""
        try:
            expire = datetime.utcnow() + timedelta(hours=self.token_expire_hours)
            
            to_encode = {
                "user_id": user_data.get("user_id"),
                "username": user_data.get("username"),
                "email": user_data.get("email"),
                "role": user_data.get("role", "user"),
                "exp": expire,
                "iat": datetime.utcnow(),
                "sub": user_data.get("username")
            }
            
            encoded_jwt = jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm)
            return encoded_jwt
            
        except Exception as e:
            logger.error(f"Token creation failed: {e}")
            raise AuthenticationError("Failed to create access token")
    
    def verify_token(self, token: str) -> Dict[str, Any]:
        """Verify JWT token"""
        try:
            payload = jwt.decode(token, self.secret_key, algorithms=[self.algorithm])
            return payload
            
        except jwt.ExpiredSignatureError:
            raise AuthenticationError("Token has expired")
        except jwt.JWTError:
            raise AuthenticationError("Invalid token")
    
    def authenticate_user(self, username: str, password: str) -> Optional[Dict[str, Any]]:
        """Authenticate user with LDAP and verify group membership"""
        try:
            # Check fallback authentication first if enabled (for development/testing)
            if settings.enable_fallback_auth and self._check_fallback_auth(username, password):
                user_data = {
                    "id": settings.fallback_admin_username,
                    "user_id": settings.fallback_admin_username,
                    "username": settings.fallback_admin_username,
                    "email": settings.fallback_admin_email,
                    "full_name": "System Administrator",
                    "role": settings.fallback_admin_role,
                    "authentication_method": "fallback",
                    "authenticated_at": datetime.utcnow().isoformat()
                }
                
                logger.info(f"User {username} authenticated successfully via fallback method")
                return user_data
            
            # Try LDAP authentication if configured
            if settings.ldap_server:
                try:
                    # Authenticate with LDAP and check group membership
                    success, user_dn, user_info = ldap_auth_service.authenticate_user(username, password)
                    
                    if success and user_info:
                        # Create user data for token
                        user_identifier = user_info.get("username") or user_info.get("email") or user_dn
                        user_data = {
                            "id": user_identifier,
                            "user_id": user_identifier,
                            "username": user_info["username"],
                            "email": user_info["email"],
                            "full_name": user_info["full_name"],
                            "ldap_dn": user_dn,
                            "role": self._determine_user_role(user_info),
                            "authentication_method": "ldap",
                            "authenticated_at": datetime.utcnow().isoformat()
                        }
                        
                        logger.info(f"User {username} authenticated successfully via LDAP")
                        return user_data
                except Exception as ldap_error:
                    logger.warning(f"LDAP authentication failed for {username}: {ldap_error}")
                    # Continue to fallback if enabled
            
            logger.warning(f"Authentication failed for user {username} - no valid authentication method")
            return None
            
        except Exception as e:
            logger.error(f"Authentication error for user {username}: {e}")
            return None
    
    def _check_fallback_auth(self, username: str, password: str) -> bool:
        """Check fallback authentication credentials"""
        return (username == settings.fallback_admin_username and 
                password == settings.fallback_admin_password)
    
    def _determine_user_role(self, user_info: Dict[str, Any]) -> str:
        """Determine user role based on LDAP groups or other criteria"""
        # Default role for all authenticated users
        return "user"
    
    def get_current_user(self, credentials: HTTPAuthorizationCredentials = Depends(security)) -> Dict[str, Any]:
        """Get current authenticated user from JWT token"""
        try:
            payload = self.verify_token(credentials.credentials)
            return payload
            
        except AuthenticationError as e:
            logger.warning(f"Token verification failed: {e}")
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid authentication credentials",
                headers={"WWW-Authenticate": "Bearer"},
            )
    
    def get_current_active_user(self, current_user: Dict[str, Any] = Depends(get_current_user)) -> Dict[str, Any]:
        """Get current active user (with additional checks if needed)"""
        # Add any additional user validation here
        # For now, just return the current user
        return current_user

# Global auth manager instance
auth_manager = AuthManager()

# Dependency for authentication
async def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)) -> Dict[str, Any]:
    """Dependency to get current authenticated user"""
    return auth_manager.get_current_user(credentials)

async def get_current_active_user(current_user: Dict[str, Any] = Depends(get_current_user)) -> Dict[str, Any]:
    """Dependency to get current active user"""
    return auth_manager.get_current_active_user(current_user)

class UserSession:
    """User session management"""
    
    @staticmethod
    async def create_session(user_data: Dict[str, Any]) -> str:
        """Create user session and return token"""
        try:
            token = auth_manager.create_access_token(user_data)
            return token
            
        except Exception as e:
            logger.error(f"Session creation failed: {e}")
            raise AuthenticationError("Failed to create user session")
    
    @staticmethod
    async def validate_session(token: str) -> Dict[str, Any]:
        """Validate user session"""
        try:
            payload = auth_manager.verify_token(token)
            return payload
            
        except Exception as e:
            logger.warning(f"Session validation failed: {e}")
            raise AuthorizationError("Invalid session")
    
    @staticmethod
    async def end_session(user_id: int):
        """End user session (implement session storage cleanup if needed)"""
        # In a more complex setup, you might store sessions in Redis or database
        # For now, JWT tokens handle expiration automatically
        logger.info(f"Session ended for user {user_id}")

# Utility functions
def require_authentication():
    """Decorator to require authentication for endpoints"""
    def decorator(func):
        async def wrapper(*args, **kwargs):
            # The FastAPI dependency system will handle authentication
            # This is just a placeholder for documentation purposes
            return await func(*args, **kwargs)
        return wrapper
    return decorator

def check_user_permissions(current_user: Dict[str, Any], required_role: str = None) -> bool:
    """Check if user has required permissions"""
    if required_role and current_user.get("role") != required_role:
        logger.warning(f"Access denied for user {current_user.get('username')} - insufficient permissions")
        return False
    
    return True

def get_user_display_name(user_data: Dict[str, Any]) -> str:
    """Get display name for user"""
    if user_data.get("full_name"):
        return user_data["full_name"]
    elif user_data.get("email"):
        return user_data["email"]
    else:
        return user_data.get("username", "Unknown User")