"""
app/middleware/auth.py
======================
Middleware autenticazione - decorator per proteggere endpoints.

Responsabilità:
- Estrarre JWT token da header Authorization
- Validare token con AuthService
- Passare tenant_id e user_id alla route
- Gestire errori autenticazione
"""

from functools import wraps
from flask import request, jsonify
import jwt


def require_auth(f):
    """
    Decorator per proteggere endpoint con JWT.
    
    Come usarlo:
        @app.route('/api/v1/protected')
        @require_auth
        def protected_route(tenant_id, user_id):
            return {'tenant': tenant_id, 'user': user_id}
    
    Flow:
    1. Client invia: Authorization: Bearer <token>
    2. Decorator estrae token da header
    3. Valida token con auth_service
    4. Se valido: passa tenant_id e user_id a route
    5. Se invalido: ritorna 401 Unauthorized
    
    Nota: auth_service DEVE essere disponibile in flask.g
    (vedi app.__init__.py per setup)
    """
    @wraps(f)
    def decorated_function(*args, **kwargs):
        # Estrai token da header Authorization
        # Formato: "Authorization: Bearer <token>"
        auth_header = request.headers.get('Authorization')
        
        if not auth_header:
            return jsonify({
                'error': 'Missing authorization header',
                'code': 'MISSING_AUTH'
            }), 401
        
        # Valida formato "Bearer <token>"
        parts = auth_header.split()
        if len(parts) != 2 or parts[0] != 'Bearer':
            return jsonify({
                'error': 'Invalid authorization header format',
                'code': 'INVALID_AUTH_FORMAT',
                'expected': 'Authorization: Bearer <token>'
            }), 401
        
        token = parts[1]
        
        # Valida token
        # ATTENZIONE: auth_service deve essere inizializzato in flask.g
        try:
            from flask import g
            auth_service = g.get('auth_service')
            
            if not auth_service:
                return jsonify({
                    'error': 'Auth service not initialized',
                    'code': 'AUTH_SERVICE_ERROR'
                }), 500
            
            payload, error = auth_service.validate_token(token)
            
            if error:
                return jsonify({
                    'error': error,
                    'code': 'INVALID_TOKEN'
                }), 401
            
            # Token valido: estrai dati critici
            tenant_id = payload.get('tenant_id')
            user_id = payload.get('user_id')
            ruolo = payload.get('ruolo')
            
            # Validazione: tenant_id deve essere presente
            if not tenant_id:
                return jsonify({
                    'error': 'Token missing tenant_id',
                    'code': 'INVALID_TOKEN_PAYLOAD'
                }), 401
            
            # Passa parametri alla route
            # user_id e tenant_id sono "injected" come parametri
            return f(*args, tenant_id=tenant_id, user_id=user_id, 
                    ruolo=ruolo, **kwargs)
        
        except Exception as e:
            return jsonify({
                'error': f'Authentication error: {str(e)}',
                'code': 'AUTH_ERROR'
            }), 500
    
    return decorated_function


def get_tenant_from_token(token: str, jwt_secret: str, jwt_algorithm: str = 'HS256') -> tuple:
    """
    Utility: estrai tenant_id da token senza usare decorator.
    
    Utile per logica non-Flask (es. background jobs).
    
    Args:
        token (str): JWT token
        jwt_secret (str): Secret key
        jwt_algorithm (str): Algoritmo JWT
        
    Returns:
        tuple: (tenant_id, user_id, error)
        
    Esempio:
        >>> tenant_id, user_id, error = get_tenant_from_token(token, 'secret')
        >>> if not error:
        ...     print(f"Token for tenant {tenant_id}, user {user_id}")
    """
    try:
        payload = jwt.decode(token, jwt_secret, algorithms=[jwt_algorithm])
        return payload.get('tenant_id'), payload.get('user_id'), None
    except jwt.ExpiredSignatureError:
        return None, None, "Token expired"
    except jwt.InvalidTokenError as e:
        return None, None, f"Invalid token: {str(e)}"


def optional_auth(f):
    """
    Decorator opzionale: se token presente, valida.
    Se assente, lascia passare con tenant_id=None.
    
    Utile per endpoint pubblici che possono avere user loggato.
    
    Nota: Meno usato, ma utile per future feature.
    """
    @wraps(f)
    def decorated_function(*args, **kwargs):
        auth_header = request.headers.get('Authorization')
        tenant_id = None
        user_id = None
        
        if auth_header:
            parts = auth_header.split()
            if len(parts) == 2 and parts[0] == 'Bearer':
                try:
                    from flask import g
                    auth_service = g.get('auth_service')
                    if auth_service:
                        payload, error = auth_service.validate_token(parts[1])
                        if not error:
                            tenant_id = payload.get('tenant_id')
                            user_id = payload.get('user_id')
                except:
                    pass  # Ignora errori, endpoint è pubblico
        
        return f(*args, tenant_id=tenant_id, user_id=user_id, **kwargs)
    
    return decorated_function

