"""
FastAPI Authentication Dependencies pentru ROA2WEB

Acest modul oferă dependency functions pentru FastAPI care pot fi folosite
pentru a proteja endpoint-urile și a obține informații despre utilizatorul curent.

Dependencies disponibile:
- get_current_user: Obține utilizatorul curent (obligatoriu)
- get_optional_user: Obține utilizatorul curent (opțional)
- require_company_access: Verifică accesul la o firmă specifică
- require_permissions: Verifică permisiunile necesare
- get_current_company: Obține firma curentă din context
"""

import logging
from typing import Optional, List, Callable, Any
from functools import wraps

from fastapi import Depends, HTTPException, status, Request
from fastapi.security import HTTPAuthorizationCredentials

from .middleware import security_required, security_optional
from .jwt_handler import jwt_handler, TokenData
from .auth_service import auth_service
from .models import CurrentUser, PermissionType, AuthError

logger = logging.getLogger(__name__)


class AuthenticationRequired(Exception):
    """Excepție pentru când autentificarea este obligatorie"""
    pass


class InsufficientPermissions(Exception):
    """Excepție pentru permisiuni insuficiente"""
    pass


class CompanyAccessDenied(Exception):
    """Excepție pentru acces refuzat la firmă"""
    pass


async def get_current_user_from_token(
    credentials: HTTPAuthorizationCredentials = Depends(security_required)
) -> CurrentUser:
    """
    Extrage și validează utilizatorul curent din token JWT
    
    Args:
        credentials: Credențialele HTTP de autentificare din header
        
    Returns:
        Utilizatorul curent autentificat
        
    Raises:
        HTTPException: Dacă token-ul este invalid sau utilizatorul nu există
    """
    if not credentials:
        logger.warning("No credentials provided for protected endpoint")
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Authentication required",
            headers={"WWW-Authenticate": "Bearer"},
        )
    
    # Validează token-ul
    token_data = jwt_handler.verify_token(credentials.credentials)
    
    if not token_data:
        logger.warning("Invalid token provided")
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid authentication token",
            headers={"WWW-Authenticate": "Bearer"},
        )
    
    if token_data.token_type != "access":
        logger.warning(f"Invalid token type: {token_data.token_type}")
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid token type",
            headers={"WWW-Authenticate": "Bearer"},
        )
    
    # Creează obiectul CurrentUser
    current_user = CurrentUser(
        username=token_data.username,
        user_id=token_data.user_id,
        companies=token_data.companies,
        permissions=token_data.permissions
    )
    
    logger.debug(f"Successfully authenticated user: {current_user.username}")
    return current_user


async def get_current_user_from_request(request: Request) -> CurrentUser:
    """
    Obține utilizatorul curent din request state (setat de middleware)
    
    Args:
        request: Request-ul HTTP curent
        
    Returns:
        Utilizatorul curent autentificat
        
    Raises:
        HTTPException: Dacă utilizatorul nu este autentificat
    """
    if not hasattr(request.state, 'is_authenticated') or not request.state.is_authenticated:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Authentication required",
            headers={"WWW-Authenticate": "Bearer"},
        )
    
    if not hasattr(request.state, 'user') or not request.state.user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="User not found in request",
            headers={"WWW-Authenticate": "Bearer"},
        )

    return request.state.user


async def get_optional_user_from_request(request: Request) -> Optional[CurrentUser]:
    """
    Obține utilizatorul curent din request (opțional)
    
    Args:
        request: Request-ul HTTP curent
        
    Returns:
        Utilizatorul curent sau None dacă nu este autentificat
    """
    if (hasattr(request.state, 'is_authenticated') and 
        request.state.is_authenticated and 
        hasattr(request.state, 'user')):
        return request.state.user
    
    return None


async def get_optional_user_from_token(
    credentials: Optional[HTTPAuthorizationCredentials] = Depends(security_optional)
) -> Optional[CurrentUser]:
    """
    Extrage utilizatorul curent din token (opțional)
    
    Args:
        credentials: Credențialele HTTP Bearer (opționale)
        
    Returns:
        Utilizatorul curent sau None
    """
    if not credentials:
        return None
    
    try:
        return await get_current_user_from_token(credentials)
    except HTTPException:
        return None


def require_company_access(company_code: str):
    """
    Dependency factory care verifică accesul la o firmă specifică
    
    Args:
        company_code: Codul firmei la care se verifică accesul
        
    Returns:
        Dependency function pentru FastAPI
    """
    async def check_company_access(
        current_user: CurrentUser = Depends(get_current_user_from_request)
    ) -> CurrentUser:
        """
        Verifică dacă utilizatorul curent are acces la firma specificată
        
        Args:
            current_user: Utilizatorul curent autentificat
            
        Returns:
            Utilizatorul curent dacă are acces
            
        Raises:
            HTTPException: Dacă nu are acces la firmă
        """
        if company_code not in current_user.companies:
            logger.warning(
                f"User {current_user.username} attempted to access "
                f"unauthorized company {company_code}"
            )
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=f"Access denied to company {company_code}"
            )
        
        # Verifică și în baza de date pentru siguranță
        has_access = await auth_service.validate_user_company_access(
            current_user.username, company_code
        )
        
        if not has_access:
            logger.error(
                f"Database access check failed for user {current_user.username} "
                f"and company {company_code}"
            )
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=f"Database access denied to company {company_code}"
            )
        
        logger.debug(f"User {current_user.username} granted access to company {company_code}")
        return current_user
    
    return check_company_access


def require_permissions(required_permissions: List[PermissionType]):
    """
    Dependency factory care verifică permisiunile necesare
    
    Args:
        required_permissions: Lista permisiunilor necesare
        
    Returns:
        Dependency function pentru FastAPI
    """
    async def check_permissions(
        current_user: CurrentUser = Depends(get_current_user_from_request)
    ) -> CurrentUser:
        """
        Verifică dacă utilizatorul are permisiunile necesare
        
        Args:
            current_user: Utilizatorul curent autentificat
            
        Returns:
            Utilizatorul curent dacă are permisiunile
            
        Raises:
            HTTPException: Dacă nu are permisiunile necesare
        """
        user_permissions = set(current_user.permissions)
        missing_permissions = [
            perm for perm in required_permissions 
            if perm not in user_permissions
        ]
        
        if missing_permissions:
            logger.warning(
                f"User {current_user.username} missing permissions: {missing_permissions}"
            )
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=f"Missing required permissions: {missing_permissions}"
            )
        
        logger.debug(f"User {current_user.username} has required permissions")
        return current_user
    
    return check_permissions


def require_company_and_permissions(
    company_code: str, 
    required_permissions: List[PermissionType]
):
    """
    Dependency factory care verifică atât accesul la firmă cât și permisiunile
    
    Args:
        company_code: Codul firmei
        required_permissions: Lista permisiunilor necesare
        
    Returns:
        Dependency function pentru FastAPI
    """
    async def check_company_and_permissions(
        current_user: CurrentUser = Depends(get_current_user_from_request)
    ) -> CurrentUser:
        """
        Verifică accesul la firmă și permisiunile
        
        Args:
            current_user: Utilizatorul curent
            
        Returns:
            Utilizatorul curent dacă are acces și permisiuni
        """
        # Verifică accesul la firmă
        company_checker = require_company_access(company_code)
        await company_checker(current_user)
        
        # Verifică permisiunile
        permissions_checker = require_permissions(required_permissions)
        await permissions_checker(current_user)
        
        return current_user
    
    return check_company_and_permissions


async def get_current_company_from_header(
    request: Request,
    current_user: CurrentUser = Depends(get_current_user_from_request)
) -> str:
    """
    Obține codul firmei curente din header-ul X-Company-Code
    
    Args:
        request: Request-ul HTTP
        current_user: Utilizatorul curent
        
    Returns:
        Codul firmei curente
        
    Raises:
        HTTPException: Dacă header-ul lipsește sau utilizatorul nu are acces
    """
    company_code = request.headers.get("X-Company-Code")
    
    if not company_code:
        # Folosește prima firmă ca default dacă nu este specificată
        if current_user.companies:
            company_code = current_user.companies[0]
            logger.debug(f"Using default company {company_code} for user {current_user.username}")
        else:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Company code required (X-Company-Code header or user default)"
            )
    
    # Verifică accesul
    if company_code not in current_user.companies:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail=f"Access denied to company {company_code}"
        )
    
    return company_code


# Aliasuri pentru folosire mai ușoară
get_current_user = get_current_user_from_request
get_optional_user = get_optional_user_from_request

# Dependency-uri predefinite pentru permisiuni comune
require_read_permission = require_permissions([PermissionType.READ])
require_write_permission = require_permissions([PermissionType.WRITE])
require_admin_permission = require_permissions([PermissionType.ADMIN])
require_reports_permission = require_permissions([PermissionType.REPORTS])

# Decorator pentru validarea companiei în funcții
def validate_company_access(company_param: str = "company"):
    """
    Decorator pentru validarea automată a accesului la firmă
    
    Args:
        company_param: Numele parametrului care conține codul firmei
        
    Returns:
        Decorator function
    """
    def decorator(func: Callable) -> Callable:
        @wraps(func)
        async def wrapper(*args, **kwargs):
            # Caută utilizatorul curent în argumentele funcției
            current_user = None
            for arg in args:
                if isinstance(arg, CurrentUser):
                    current_user = arg
                    break
            
            if not current_user:
                # Caută în kwargs
                current_user = kwargs.get('current_user')
            
            if not current_user:
                raise ValueError("CurrentUser not found in function arguments")
            
            # Obține codul firmei
            company_code = kwargs.get(company_param)
            if not company_code:
                raise ValueError(f"Company parameter '{company_param}' not found")
            
            # Validează accesul
            if company_code not in current_user.companies:
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail=f"Access denied to company {company_code}"
                )
            
            return await func(*args, **kwargs)
        
        return wrapper
    return decorator