"""
Module d'authentification unifié utilisant PostgreSQL.

Ce module remplace web_sentinel/user_auth.py et utilise la base PostgreSQL
pour gérer l'authentification de tous les utilisateurs :
- Site web (connexion email/password)
- Application Windows (connexion email/password OU API key)
- Comptes SYSOP (API key uniquement)
"""

import jwt
import os
from datetime import datetime, timedelta, timezone
from typing import Dict, Optional
from functools import wraps
from flask import request, jsonify
from passlib.context import CryptContext
from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker

# Configuration
JWT_SECRET = os.getenv("WEB_SENTINEL_JWT_SECRET", "your-secret-key-change-in-production")
JWT_ALGORITHM = "HS256"
JWT_EXPIRATION_DAYS = 30

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")


class UnifiedAuthManager:
    """Gestionnaire d'authentification unifié PostgreSQL."""
    
    def __init__(self, database_url: str):
        """
        Initialise le gestionnaire avec connexion PostgreSQL.
        
        Args:
            database_url: URL de connexion PostgreSQL
        """
        self.engine = create_engine(database_url, pool_pre_ping=True)
        self.SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=self.engine)
    
    def hash_password(self, password: str) -> str:
        """Hache un mot de passe avec bcrypt."""
        return pwd_context.hash(password)
    
    def verify_password(self, plain_password: str, hashed_password: str) -> bool:
        """Vérifie un mot de passe contre son hash."""
        return pwd_context.verify(plain_password, hashed_password)
    
    def generate_token(self, user_id: int, email: str, name: str, tier: str, api_key: str = None) -> str:
        """
        Génère un token JWT pour un utilisateur.
        
        Args:
            user_id: ID de l'utilisateur
            email: Email de l'utilisateur
            name: Nom de l'utilisateur
            tier: Tier du compte (free, pro, enterprise, sysop)
            api_key: Clé API (optionnel)
        
        Returns:
            Token JWT encodé
        """
        payload = {
            "user_id": user_id,
            "email": email,
            "name": name,
            "tier": tier,
            "api_key": api_key,
            "exp": datetime.now(timezone.utc) + timedelta(days=JWT_EXPIRATION_DAYS)
        }
        return jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM)
    
    def verify_token(self, token: str) -> Dict:
        """
        Vérifie et décode un token JWT.
        
        Args:
            token: Token JWT à vérifier
        
        Returns:
            Dict avec success et data/error
        """
        try:
            payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM])
            return {
                "success": True,
                "data": payload
            }
        except jwt.ExpiredSignatureError:
            return {
                "success": False,
                "error": "Token expiré"
            }
        except jwt.InvalidTokenError:
            return {
                "success": False,
                "error": "Token invalide"
            }
    
    def create_user(self, name: str, email: str, password: str, tier: str = "free") -> Dict:
        """
        Crée un nouvel utilisateur.
        
        Args:
            name: Nom complet
            email: Adresse email (unique)
            password: Mot de passe en clair
            tier: Type de compte (default: free)
        
        Returns:
            Dict avec success, token, et infos utilisateur
        """
        session = self.SessionLocal()
        
        try:
            # Vérifier si l'email existe déjà
            result = session.execute(
                text("SELECT id FROM users WHERE email = :email"),
                {"email": email}
            )
            
            if result.fetchone():
                return {
                    "success": False,
                    "error": "Cet email est déjà utilisé"
                }
            
            # Générer une clé API unique
            import secrets
            api_key = f"ws_{secrets.token_urlsafe(32)}"
            
            # Hacher le mot de passe
            password_hash = self.hash_password(password)
            
            # Insérer l'utilisateur
            result = session.execute(text("""
                INSERT INTO users (name, email, password_hash, api_key, tier, active, created_at)
                VALUES (:name, :email, :password_hash, :api_key, :tier, TRUE, CURRENT_TIMESTAMP)
                RETURNING id
            """), {
                "name": name,
                "email": email,
                "password_hash": password_hash,
                "api_key": api_key,
                "tier": tier
            })
            
            user_id = result.fetchone()[0]
            session.commit()
            
            # Générer le token JWT
            token = self.generate_token(user_id, email, name, tier, api_key)
            
            return {
                "success": True,
                "token": token,
                "user_id": user_id,
                "name": name,
                "email": email,
                "tier": tier,
                "api_key": api_key
            }
            
        except Exception as e:
            session.rollback()
            return {
                "success": False,
                "error": f"Erreur lors de la création : {str(e)}"
            }
        finally:
            session.close()
    
    def authenticate_user(self, email: str, password: str) -> Dict:
        """
        Authentifie un utilisateur (email/password OU email/api_key).
        
        Cette fonction supporte DEUX modes d'authentification :
        1. Email + mot de passe (pour site web et app Windows)
        2. Email + clé API (pour app Windows et comptes SYSOP)
        
        Args:
            email: Email de l'utilisateur
            password: Mot de passe OU clé API
        
        Returns:
            Dict avec success, token, et infos utilisateur
        """
        session = self.SessionLocal()
        
        try:
            # Récupérer l'utilisateur
            result = session.execute(text("""
                SELECT id, name, email, password_hash, api_key, tier, active
                FROM users
                WHERE email = :email
            """), {"email": email})
            
            user = result.fetchone()
            
            if not user:
                return {
                    "success": False,
                    "error": "Email ou mot de passe incorrect"
                }
            
            user_id, name, email, password_hash, api_key, tier, active = user
            
            # Vérifier si le compte est actif
            if not active:
                return {
                    "success": False,
                    "error": "Compte désactivé"
                }
            
            # MODE 1 : Authentification par clé API (prioritaire pour app Windows et SYSOP)
            if password == api_key:
                # Mettre à jour last_login
                session.execute(text("""
                    UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = :id
                """), {"id": user_id})
                session.commit()
                
                # Générer token
                token = self.generate_token(user_id, email, name, tier, api_key)
                
                return {
                    "success": True,
                    "token": token,
                    "user_id": user_id,
                    "name": name,
                    "email": email,
                    "tier": tier,
                    "api_key": api_key,
                    "auth_method": "api_key"
                }
            
            # MODE 2 : Authentification par mot de passe (pour site web)
            if password_hash and self.verify_password(password, password_hash):
                # Mettre à jour last_login
                session.execute(text("""
                    UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = :id
                """), {"id": user_id})
                session.commit()
                
                # Générer token
                token = self.generate_token(user_id, email, name, tier, api_key)
                
                return {
                    "success": True,
                    "token": token,
                    "user_id": user_id,
                    "name": name,
                    "email": email,
                    "tier": tier,
                    "api_key": api_key,
                    "auth_method": "password"
                }
            
            # Aucune méthode d'authentification n'a fonctionné
            return {
                "success": False,
                "error": "Email ou mot de passe incorrect"
            }
            
        except Exception as e:
            return {
                "success": False,
                "error": f"Erreur lors de l'authentification : {str(e)}"
            }
        finally:
            session.close()
    
    def get_user_by_api_key(self, api_key: str) -> Optional[Dict]:
        """
        Récupère un utilisateur par sa clé API (pour l'app Windows).
        
        Args:
            api_key: Clé API de l'utilisateur
        
        Returns:
            Dict avec infos utilisateur ou None
        """
        session = self.SessionLocal()
        
        try:
            result = session.execute(text("""
                SELECT id, name, email, tier, active
                FROM users
                WHERE api_key = :api_key
            """), {"api_key": api_key})
            
            user = result.fetchone()
            
            if not user or not user[4]:  # not active
                return None
            
            return {
                "user_id": user[0],
                "name": user[1],
                "email": user[2],
                "tier": user[3],
                "api_key": api_key
            }
            
        finally:
            session.close()
    
    def get_user_licenses(self, user_id: int) -> Dict:
        """
        Récupère les licences d'un utilisateur.
        
        Args:
            user_id: ID de l'utilisateur
        
        Returns:
            Dict avec licenses et stats
        """
        session = self.SessionLocal()
        
        try:
            # Récupérer les licences
            result = session.execute(text("""
                SELECT license_key, tier, status, purchased_at, expires_at, active
                FROM licenses
                WHERE user_id = :user_id
                ORDER BY purchased_at DESC
            """), {"user_id": user_id})
            
            licenses = []
            for row in result:
                licenses.append({
                    "key": row[0],
                    "tier": row[1],
                    "status": row[2],
                    "purchased_at": row[3].isoformat() if row[3] else None,
                    "expires_at": row[4].isoformat() if row[4] else None,
                    "active": row[5]
                })
            
            return {
                "success": True,
                "licenses": licenses
            }
            
        except Exception as e:
            return {
                "success": False,
                "error": f"Erreur : {str(e)}",
                "licenses": []
            }
        finally:
            session.close()


def require_auth(f):
    """
    Décorateur pour protéger les routes nécessitant une authentification.
    
    Usage:
        @app.route('/api/protected')
        @require_auth
        def protected_route():
            user = request.current_user
            return jsonify(user)
    """
    @wraps(f)
    def decorated_function(*args, **kwargs):
        # Récupérer le token depuis le header Authorization
        auth_header = request.headers.get('Authorization', '')
        
        if not auth_header.startswith('Bearer '):
            return jsonify({'error': 'Token manquant'}), 401
        
        token = auth_header.split(' ')[1]
        
        # Vérifier le token (en utilisant l'instance globale auth_manager)
        from flask import current_app
        auth_manager = current_app.config.get('AUTH_MANAGER')
        
        if not auth_manager:
            return jsonify({'error': 'Configuration auth manquante'}), 500
        
        result = auth_manager.verify_token(token)
        
        if not result['success']:
            return jsonify({'error': result['error']}), 401
        
        # Ajouter les infos user à la requête
        request.current_user = result['data']
        
        return f(*args, **kwargs)
    
    return decorated_function
