/// Service d'Authentification Keycloak
/// Gère l'authentification avec votre instance Keycloak sur port 8180
library keycloak_auth_service;

import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter_appauth/flutter_appauth.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:jwt_decoder/jwt_decoder.dart';
import '../models/user.dart';
import '../models/user_role.dart';
import 'keycloak_role_mapper.dart';
import 'keycloak_webview_auth_service.dart';

/// Configuration Keycloak pour votre instance
class KeycloakConfig {
  /// URL de base de votre Keycloak
  static const String baseUrl = 'http://192.168.1.11:8180';
  
  /// Realm UnionFlow
  static const String realm = 'unionflow';
  
  /// Client ID pour l'application mobile
  static const String clientId = 'unionflow-mobile';
  
  /// URL de redirection après authentification
  static const String redirectUrl = 'dev.lions.unionflow-mobile://auth/callback';
  
  /// Scopes demandés
  static const List<String> scopes = ['openid', 'profile', 'email', 'roles'];
  
  /// Endpoints calculés
  static String get authorizationEndpoint => 
      '$baseUrl/realms/$realm/protocol/openid-connect/auth';
  
  static String get tokenEndpoint => 
      '$baseUrl/realms/$realm/protocol/openid-connect/token';
  
  static String get userInfoEndpoint => 
      '$baseUrl/realms/$realm/protocol/openid-connect/userinfo';
  
  static String get logoutEndpoint => 
      '$baseUrl/realms/$realm/protocol/openid-connect/logout';
}

/// Service d'authentification Keycloak ultra-sophistiqué
class KeycloakAuthService {
  static const FlutterAppAuth _appAuth = FlutterAppAuth();
  static const FlutterSecureStorage _secureStorage = FlutterSecureStorage(
    aOptions: AndroidOptions(
      encryptedSharedPreferences: true,
    ),
    iOptions: IOSOptions(
      accessibility: KeychainAccessibility.first_unlock_this_device,
    ),
  );
  
  // Clés de stockage sécurisé
  static const String _accessTokenKey = 'keycloak_access_token';
  static const String _refreshTokenKey = 'keycloak_refresh_token';
  static const String _idTokenKey = 'keycloak_id_token';
  static const String _userInfoKey = 'keycloak_user_info';
  
  /// Authentification avec Keycloak via WebView (solution HTTP compatible)
  ///
  /// Cette méthode utilise maintenant KeycloakWebViewAuthService pour contourner
  /// les limitations HTTPS de flutter_appauth
  static Future<AuthorizationTokenResponse?> authenticate() async {
    try {
      debugPrint('🔐 Démarrage authentification Keycloak via WebView...');

      // Utiliser le service WebView pour l'authentification
      // Cette méthode retourne null car l'authentification WebView
      // est gérée différemment (via callback)
      debugPrint('💡 Authentification WebView - utilisez authenticateWithWebView() à la place');

      return null;

    } catch (e, stackTrace) {
      debugPrint('💥 Erreur authentification Keycloak: $e');
      debugPrint('Stack trace: $stackTrace');
      return null;
    }
  }
  
  /// Rafraîchit le token d'accès
  static Future<TokenResponse?> refreshToken() async {
    try {
      final String? refreshToken = await _secureStorage.read(
        key: _refreshTokenKey,
      );
      
      if (refreshToken == null) {
        debugPrint('❌ Aucun refresh token disponible');
        return null;
      }
      
      debugPrint('🔄 Rafraîchissement du token...');
      
      final TokenRequest request = TokenRequest(
        KeycloakConfig.clientId,
        KeycloakConfig.redirectUrl,
        refreshToken: refreshToken,
        serviceConfiguration: AuthorizationServiceConfiguration(
          authorizationEndpoint: KeycloakConfig.authorizationEndpoint,
          tokenEndpoint: KeycloakConfig.tokenEndpoint,
        ),
      );
      
      final TokenResponse? result = await _appAuth.token(request);
      
      if (result != null) {
        await _storeTokens(result);
        debugPrint('✅ Token rafraîchi avec succès');
        return result;
      }
      
      debugPrint('❌ Échec du rafraîchissement du token');
      return null;
      
    } catch (e, stackTrace) {
      debugPrint('💥 Erreur rafraîchissement token: $e');
      debugPrint('Stack trace: $stackTrace');
      return null;
    }
  }
  
  /// Récupère l'utilisateur authentifié depuis les tokens
  static Future<User?> getCurrentUser() async {
    try {
      final String? accessToken = await _secureStorage.read(
        key: _accessTokenKey,
      );
      
      final String? idToken = await _secureStorage.read(
        key: _idTokenKey,
      );
      
      if (accessToken == null || idToken == null) {
        debugPrint('❌ Tokens manquants');
        return null;
      }
      
      // Vérifier si les tokens sont expirés
      if (JwtDecoder.isExpired(accessToken)) {
        debugPrint('⏰ Access token expiré, tentative de rafraîchissement...');
        final TokenResponse? refreshResult = await refreshToken();
        if (refreshResult == null) {
          debugPrint('❌ Impossible de rafraîchir le token');
          return null;
        }
      }
      
      // Décoder les tokens JWT
      final Map<String, dynamic> accessTokenPayload = 
          JwtDecoder.decode(accessToken);
      final Map<String, dynamic> idTokenPayload = 
          JwtDecoder.decode(idToken);
      
      debugPrint('🔍 Payload Access Token: $accessTokenPayload');
      debugPrint('🔍 Payload ID Token: $idTokenPayload');
      
      // Extraire les informations utilisateur
      final String userId = idTokenPayload['sub'] ?? '';
      final String email = idTokenPayload['email'] ?? '';
      final String firstName = idTokenPayload['given_name'] ?? '';
      final String lastName = idTokenPayload['family_name'] ?? '';

      
      // Extraire les rôles Keycloak
      final List<String> keycloakRoles = _extractKeycloakRoles(accessTokenPayload);
      debugPrint('🎭 Rôles Keycloak extraits: $keycloakRoles');

      // Si aucun rôle, assigner un rôle par défaut
      if (keycloakRoles.isEmpty) {
        debugPrint('⚠️ Aucun rôle trouvé, assignation du rôle MEMBER par défaut');
        keycloakRoles.add('member');
      }

      // Mapper vers notre système de rôles
      final UserRole primaryRole = KeycloakRoleMapper.mapToUserRole(keycloakRoles);
      final List<String> permissions = KeycloakRoleMapper.mapToPermissions(keycloakRoles);

      debugPrint('🎯 Rôle principal mappé: ${primaryRole.displayName}');
      debugPrint('🔐 Permissions mappées: ${permissions.length} permissions');
      debugPrint('📋 Permissions détaillées: $permissions');
      
      // Créer l'utilisateur
      final User user = User(
        id: userId,
        email: email,
        firstName: firstName,
        lastName: lastName,

        primaryRole: primaryRole,
        organizationContexts: const [], // À implémenter selon vos besoins
        additionalPermissions: permissions,
        revokedPermissions: const [],
        preferences: const UserPreferences(),
        lastLoginAt: DateTime.now(),
        createdAt: DateTime.now(), // À récupérer depuis Keycloak si disponible
        isActive: true,
      );
      
      // Stocker les informations utilisateur
      await _secureStorage.write(
        key: _userInfoKey,
        value: jsonEncode(user.toJson()),
      );
      
      debugPrint('✅ Utilisateur récupéré: ${user.fullName} (${user.primaryRole.displayName})');
      return user;
      
    } catch (e, stackTrace) {
      debugPrint('💥 Erreur récupération utilisateur: $e');
      debugPrint('Stack trace: $stackTrace');
      return null;
    }
  }
  
  /// Déconnexion complète
  static Future<bool> logout() async {
    try {
      debugPrint('🚪 Déconnexion Keycloak...');
      
      final String? idToken = await _secureStorage.read(key: _idTokenKey);
      
      // Déconnexion côté Keycloak si possible
      if (idToken != null) {
        try {
          final EndSessionRequest request = EndSessionRequest(
            idTokenHint: idToken,
            postLogoutRedirectUrl: KeycloakConfig.redirectUrl,
            serviceConfiguration: AuthorizationServiceConfiguration(
              authorizationEndpoint: KeycloakConfig.authorizationEndpoint,
              tokenEndpoint: KeycloakConfig.tokenEndpoint,
              endSessionEndpoint: KeycloakConfig.logoutEndpoint,
            ),
          );
          
          await _appAuth.endSession(request);
          debugPrint('✅ Déconnexion Keycloak côté serveur réussie');
        } catch (e) {
          debugPrint('⚠️ Déconnexion côté serveur échouée: $e');
          // Continue quand même avec la déconnexion locale
        }
      }
      
      // Nettoyage local des tokens
      await _clearTokens();
      
      debugPrint('✅ Déconnexion locale terminée');
      return true;
      
    } catch (e, stackTrace) {
      debugPrint('💥 Erreur déconnexion: $e');
      debugPrint('Stack trace: $stackTrace');
      return false;
    }
  }
  
  /// Vérifie si l'utilisateur est authentifié
  static Future<bool> isAuthenticated() async {
    try {
      final String? accessToken = await _secureStorage.read(
        key: _accessTokenKey,
      );
      
      if (accessToken == null) {
        return false;
      }
      
      // Vérifier si le token est expiré
      if (JwtDecoder.isExpired(accessToken)) {
        // Tenter de rafraîchir
        final TokenResponse? refreshResult = await refreshToken();
        return refreshResult != null;
      }
      
      return true;
      
    } catch (e) {
      debugPrint('💥 Erreur vérification authentification: $e');
      return false;
    }
  }
  
  /// Stocke les tokens de manière sécurisée
  static Future<void> _storeTokens(TokenResponse tokenResponse) async {
    if (tokenResponse.accessToken != null) {
      await _secureStorage.write(
        key: _accessTokenKey,
        value: tokenResponse.accessToken!,
      );
    }
    
    if (tokenResponse.refreshToken != null) {
      await _secureStorage.write(
        key: _refreshTokenKey,
        value: tokenResponse.refreshToken!,
      );
    }
    
    if (tokenResponse.idToken != null) {
      await _secureStorage.write(
        key: _idTokenKey,
        value: tokenResponse.idToken!,
      );
    }
    
    debugPrint('🔒 Tokens stockés de manière sécurisée');
  }
  
  /// Nettoie tous les tokens stockés
  static Future<void> _clearTokens() async {
    await _secureStorage.delete(key: _accessTokenKey);
    await _secureStorage.delete(key: _refreshTokenKey);
    await _secureStorage.delete(key: _idTokenKey);
    await _secureStorage.delete(key: _userInfoKey);
    
    debugPrint('🧹 Tokens nettoyés');
  }
  
  /// Extrait les rôles depuis le payload JWT Keycloak
  static List<String> _extractKeycloakRoles(Map<String, dynamic> payload) {
    final List<String> roles = [];
    
    try {
      // Rôles du realm
      final Map<String, dynamic>? realmAccess = payload['realm_access'];
      if (realmAccess != null && realmAccess['roles'] is List) {
        final List<dynamic> realmRoles = realmAccess['roles'];
        roles.addAll(realmRoles.cast<String>());
      }
      
      // Rôles des clients
      final Map<String, dynamic>? resourceAccess = payload['resource_access'];
      if (resourceAccess != null) {
        resourceAccess.forEach((clientId, clientData) {
          if (clientData is Map<String, dynamic> && clientData['roles'] is List) {
            final List<dynamic> clientRoles = clientData['roles'];
            roles.addAll(clientRoles.cast<String>());
          }
        });
      }
      
      // Filtrer les rôles système Keycloak
      return roles.where((role) => 
        !role.startsWith('default-roles-') &&
        role != 'offline_access' &&
        role != 'uma_authorization'
      ).toList();
      
    } catch (e) {
      debugPrint('💥 Erreur extraction rôles: $e');
      return [];
    }
  }
  
  /// Récupère le token d'accès actuel
  static Future<String?> getAccessToken() async {
    try {
      final String? accessToken = await _secureStorage.read(
        key: _accessTokenKey,
      );
      
      if (accessToken != null && !JwtDecoder.isExpired(accessToken)) {
        return accessToken;
      }
      
      // Token expiré, tenter de rafraîchir
      final TokenResponse? refreshResult = await refreshToken();
      return refreshResult?.accessToken;

    } catch (e) {
      debugPrint('💥 Erreur récupération access token: $e');
      return null;
    }
  }

  // ═══════════════════════════════════════════════════════════════════════════
  // MÉTHODES WEBVIEW - Délégation vers KeycloakWebViewAuthService
  // ═══════════════════════════════════════════════════════════════════════════

  /// Prépare l'authentification WebView
  ///
  /// Retourne les paramètres nécessaires pour lancer la WebView d'authentification
  static Future<Map<String, String>> prepareWebViewAuthentication() async {
    return KeycloakWebViewAuthService.prepareAuthentication();
  }

  /// Traite le callback WebView et finalise l'authentification
  ///
  /// Cette méthode doit être appelée quand l'URL de callback est interceptée
  static Future<User> handleWebViewCallback(String callbackUrl) async {
    return KeycloakWebViewAuthService.handleAuthCallback(callbackUrl);
  }

  /// Vérifie si l'utilisateur est authentifié (compatible WebView)
  static Future<bool> isWebViewAuthenticated() async {
    return KeycloakWebViewAuthService.isAuthenticated();
  }

  /// Récupère l'utilisateur authentifié (compatible WebView)
  static Future<User?> getCurrentWebViewUser() async {
    return KeycloakWebViewAuthService.getCurrentUser();
  }

  /// Déconnecte l'utilisateur (compatible WebView)
  static Future<bool> logoutWebView() async {
    return KeycloakWebViewAuthService.logout();
  }

  /// Nettoie les données d'authentification WebView
  static Future<void> clearWebViewAuthData() async {
    return KeycloakWebViewAuthService.clearAuthData();
  }
}
