/**
 * 🔐 AuthenticationService - Gestion centralisée de l'authentification
 * Remplace les fonctionnalités d'authentification du GlobalService
 */

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable, throwError, timer } from 'rxjs';
import { tap, catchError, switchMap, map } from 'rxjs/operators';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Router } from '@angular/router';
import { environment } from '../../../environments/environment';
import {
  User,
  AuthToken,
  LoginCredentials,
  AuthState,
  TokenStorage
} from '../interfaces/auth.interface';
import { LoginOptimizationService } from '../../services/login-optimization.service';

@Injectable({
  providedIn: 'root'
})
export class AuthenticationService implements TokenStorage {
  private readonly TOKEN_KEY = '__token';
  private readonly USER_KEY = '__user';
  private readonly REFRESH_THRESHOLD = 15 * 60 * 1000; // 15 minutes avant expiration

  private authState = new BehaviorSubject<AuthState>({
    isAuthenticated: false,
    user: null,
    token: null,
    loading: false,
    error: null
  });

  private refreshTimer?: any;

  constructor(
    private http: HttpClient,
    private router: Router,
    private loginOptimizationService: LoginOptimizationService
  ) {
    this.initializeFromStorage();
  }

  /**
   * État observable de l'authentification
   */
  get authState$(): Observable<AuthState> {
    return this.authState.asObservable();
  }

  /**
   * Utilisateur actuel
   */
  get currentUser(): User | null {
    return this.authState.value.user;
  }

  /**
   * Token actuel
   */
  get currentToken(): AuthToken | null {
    return this.authState.value.token;
  }

  /**
   * Vérifier si l'utilisateur est authentifié
   */
  get isAuthenticated(): boolean {
    return this.authState.value.isAuthenticated && this.isTokenValid();
  }

  /**
   * Connexion avec identifiants
   */
  /** Roles qui necessitent obligatoirement un account_id */
  private readonly ROLES_REQUIRING_ACCOUNT = ['admin_agence', 'agent'];

  login(credentials: LoginCredentials): Observable<AuthState> {
    this.updateState({ loading: true, error: null });

    const authHeaders = new HttpHeaders({
      'Content-Type': 'application/json',
      'X-API-KEY': environment.AUTH_APY_KEY
    });

    return this.http.post<any>(`${environment.AUTH_URL}auth/login`, credentials, { headers: authHeaders })
      .pipe(
        switchMap(authResponse => {
          console.log('Login AUTH response:', authResponse);

          if (!authResponse || !authResponse.token) {
            throw new Error('Réponse d\'authentification invalide');
          }

          const token: AuthToken = {
            accessToken: authResponse.token.accessToken,
            refreshToken: authResponse.token.refreshToken,
            expiresAt: Date.now() + (authResponse.token.expiresIn * 1000),
            tokenType: authResponse.token.tokenType || 'Bearer',
            scope: authResponse.token.scope
          };

          // Appeler ESTAIR /me pour enrichir le profil user
          const meHeaders = new HttpHeaders({
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${token.accessToken}`,
            'X-API-KEY': environment.DATA_APY_KEY
          });

          return this.http.get<any>(`${environment.DATA_URL}admin/me`, { headers: meHeaders })
            .pipe(
              map(meResponse => {
                console.log('Profile ESTAIR response:', meResponse);

                const profile = meResponse?.data;
                if (!profile) {
                  throw new Error('Impossible de récupérer le profil utilisateur');
                }

                // Validation : role obligatoire
                if (!profile.role) {
                  throw new Error('Aucun rôle attribué à ce compte. Contactez votre administrateur.');
                }

                // Validation : account_id obligatoire pour admin_agence/agent
                if (this.ROLES_REQUIRING_ACCOUNT.includes(profile.role) && !profile.account_id) {
                  throw new Error('Compte non associé à une agence. Contactez votre administrateur.');
                }

                // Construire User enrichi (données AUTH + ESTAIR)
                const authUser = authResponse.user || {};
                const user: User = {
                  id: profile.uuid || authUser.id,
                  username: authUser.username || profile.email,
                  email: profile.email || authUser.email,
                  fullname: profile.name || authUser.fullname,
                  firstname: authUser.firstname,
                  lastname: authUser.lastname,
                  role: profile.role,
                  permissions: authUser.permissions || [],
                  avatar: authUser.avatar,
                  lastLogin: new Date(),
                  isActive: profile.status === 'active',
                  account_id: profile.account_id || null,
                  account_uuid: profile.account_uuid || null,
                  account_name: profile.account_name || null,
                };

                this.setAuthData(user, token);
                return this.authState.value;
              })
            );
        }),
        catchError(error => {
          console.error('Login error:', error);
          const errorMessage = error?.message || error?.error?.message || 'Erreur de connexion';
          this.updateState({
            loading: false,
            error: errorMessage,
            isAuthenticated: false,
            user: null,
            token: null
          });
          return throwError(() => error);
        })
      );
  }

  /**
   * Déconnexion
   */
  logout(): Observable<any> {
    console.log('🔒 Déconnexion en cours...');

    // Nettoyer immédiatement l'état local SANS appeler le backend
    // Cela évite le blocage si le backend ne répond pas
    this.clearAuthData();

    // Vider le cache des menus et autres données
    this.loginOptimizationService.clearAllData();
    console.log('🧹 Cache des menus vidé');

    console.log('✅ Déconnexion locale réussie');

    // Retourner immédiatement sans appeler le backend
    // Le token sera invalidé automatiquement côté serveur à son expiration
    return new Observable(observer => {
      observer.next(true);
      observer.complete();
    });
  }

  /**
   * Rafraîchir le token
   */
  refreshToken(refresh_token?: string): Observable<AuthToken> {
    const currentToken = this.currentToken;
    const tokenToRefresh = refresh_token || currentToken?.refreshToken;

    if (!tokenToRefresh) {
      return throwError(() => new Error('Aucun refresh token disponible'));
    }

    const headers = new HttpHeaders({
      'Content-Type': 'application/json',
      'X-API-KEY': environment.AUTH_APY_KEY
    });

    const body = {
      refresh_token: tokenToRefresh
    };

    return this.http.post<any>(`${environment.AUTH_URL}auth/refresh`, body, { headers })
      .pipe(
        switchMap(response => {
          if (response && response.success && response.data) {
            const newToken: AuthToken = {
              accessToken: response.data.token,
              refreshToken: response.data.refresh_token,
              expiresAt: this.extractExpirationFromToken(response.data.token),
              tokenType: 'Bearer',
              scope: ['openid', 'profile', 'email']
            };

            this.setToken(newToken);
            console.log('✅ Token rafraîchi avec succès');

            // Retourner le nouveau token pour l'interceptor
            return new Observable<AuthToken>(observer => {
              observer.next(newToken);
              observer.complete();
            });
          } else {
            return throwError(() => new Error('Réponse de refresh invalide'));
          }
        }),
        catchError(error => {
          console.error('❌ Erreur lors du rafraîchissement du token:', error);
          this.clearAuthData();
          return throwError(() => error);
        })
      );
  }

  /**
   * Extraire la date d'expiration d'un JWT
   */
  private extractExpirationFromToken(token: string): number {
    try {
      const payload = JSON.parse(atob(token.split('.')[1]));
      return payload.exp * 1000; // Convertir en millisecondes
    } catch (error) {
      console.warn('⚠️ Impossible d\'extraire l\'expiration du token, utilisation d\'une valeur par défaut');
      return Date.now() + (2 * 60 * 60 * 1000); // 2 heures par défaut
    }
  }

  /**
   * Vérifier les permissions de l'utilisateur
   */
  hasPermission(permission: string): boolean {
    const user = this.currentUser;
    return user?.permissions.includes(permission) || false;
  }

  /**
   * Vérifier le rôle de l'utilisateur
   */
  hasRole(role: string): boolean {
    const user = this.currentUser;
    return user?.role === role;
  }

  /**
   * Créer les headers d'authentification
   */
  createAuthHeaders(token?: AuthToken): HttpHeaders {
    const currentToken = token || this.currentToken;
    
    let headers = new HttpHeaders({
      'Content-Type': 'application/json',
      'X-API-KEY': environment.X_APY_KEY
    });

    if (currentToken && this.isTokenValid(currentToken)) {
      headers = headers.set('Authorization', `${currentToken.tokenType} ${currentToken.accessToken}`);
    }

    return headers;
  }

  // Implémentation de TokenStorage
  getToken(): AuthToken | null {
    return this.currentToken;
  }

  setToken(token: AuthToken): void {
    const currentState = this.authState.value;
    this.updateState({
      token,
      isAuthenticated: this.isTokenValid(token)
    });
    
    // Sauvegarder en localStorage
    localStorage.setItem(this.TOKEN_KEY, JSON.stringify(token));
    
    // Redémarrer le timer de rafraîchissement
    this.startRefreshTimer();
  }

  removeToken(): void {
    localStorage.removeItem(this.TOKEN_KEY);
    this.updateState({
      token: null,
      isAuthenticated: false
    });
    
    if (this.refreshTimer) {
      clearTimeout(this.refreshTimer);
      this.refreshTimer = null;
    }
  }

  /**
   * Vérifier si l'utilisateur est connecté (pour les guards)
   */
  isLoggedIn(): boolean {
    return this.authState.value.isAuthenticated;
  }

  /**
   * Obtenir l'état d'authentification actuel (pour les interceptors)
   */
  getCurrentAuthState(): AuthState {
    return this.authState.value;
  }

  isTokenValid(token?: AuthToken | any): boolean {
    const tokenToCheck = token || this.currentToken;
    
    if (!tokenToCheck) {
      return false;
    }

    // Vérifier avec expiresAt si disponible
    if (tokenToCheck.expiresAt) {
      return Date.now() < tokenToCheck.expiresAt;
    }

    // Fallback: essayer de décoder le JWT accessToken
    if (tokenToCheck.accessToken) {
      try {
        const payload = JSON.parse(atob(tokenToCheck.accessToken.split('.')[1]));
        const expiration = payload.exp * 1000; // JWT exp est en secondes
        return Date.now() < expiration;
      } catch (error) {
        console.error('❌ Erreur lors du décodage du JWT:', error);
        return false;
      }
    }

    // Support pour l'ancien format de token (legacy)
    if (tokenToCheck.token) {
      try {
        const payload = JSON.parse(atob(tokenToCheck.token.split('.')[1]));
        const expiration = payload.exp * 1000;
        return Date.now() < expiration;
      } catch (error) {
        console.error('❌ Erreur lors du décodage du JWT legacy:', error);
        return false;
      }
    }

    return false;
  }

  /**
   * Initialiser depuis le localStorage
   */
  private initializeFromStorage(): void {
    try {
      console.log('🔄 AuthService: Initialisation depuis localStorage...');
      const tokenData = localStorage.getItem(this.TOKEN_KEY);
      const userData = localStorage.getItem(this.USER_KEY);
      
      console.log('🔄 Token trouvé:', !!tokenData);
      console.log('🔄 User trouvé:', !!userData);

      if (tokenData && userData) {
        const token: AuthToken = JSON.parse(tokenData);
        const user: User = JSON.parse(userData);
        
        console.log('🔄 Token parsé:', token);
        console.log('🔄 User parsé:', user);
        console.log('🔄 Token valide?', this.isTokenValid(token));

        if (this.isTokenValid(token)) {
          console.log('✅ AuthService: État restauré avec succès');
          this.updateState({
            isAuthenticated: true,
            user,
            token,
            loading: false,
            error: null
          });

          // Démarrer le timer de rafraîchissement seulement avec un token valide
          this.startRefreshTimer();
        } else {
          console.log('❌ AuthService: Token invalide');

          // Token expiré, nettoyer
          this.clearAuthData();
        }
      }
    } catch (error) {
      console.error('Erreur lors de l\'initialisation depuis le localStorage:', error);
      this.clearAuthData();
    }
  }

  /**
   * Définir les données d'authentification
   */
  private setAuthData(user: User, token: AuthToken): void {
    this.updateState({
      isAuthenticated: true,
      user,
      token,
      loading: false,
      error: null
    });

    // Sauvegarder en localStorage
    localStorage.setItem(this.USER_KEY, JSON.stringify(user));
    localStorage.setItem(this.TOKEN_KEY, JSON.stringify(token));
    
    // Démarrer le timer de rafraîchissement
    this.startRefreshTimer();

    console.log('Utilisateur authentifié:', user.username);
  }

  /**
   * Nettoyer les données d'authentification
   */
  private clearAuthData(): void {
    console.log('🧹 Nettoyage des données d\'authentification...');

    // Supprimer uniquement les clés d'authentification (pas tout le localStorage)
    // localStorage.removeItem(this.TOKEN_KEY);
    // localStorage.removeItem(this.USER_KEY);
    // localStorage.removeItem('__menus');
    // localStorage.removeItem('__domaineDD');
    // localStorage.removeItem('isFirst');
    
    localStorage.clear();

    this.updateState({
      isAuthenticated: false,
      user: null,
      token: null,
      loading: false,
      error: null
    });

    if (this.refreshTimer) {
      clearTimeout(this.refreshTimer);
      this.refreshTimer = null;
    }

    console.log('✅ Données d\'authentification nettoyées');

    // NE PAS naviguer ici - laisser le composant appelant gérer la navigation
    // pour éviter les doubles navigations qui causent le blocage
  }

  /**
   * Mettre à jour l'état
   */
  private updateState(newState: Partial<AuthState>): void {
    this.authState.next({
      ...this.authState.value,
      ...newState
    });
  }

  /**
   * Démarrer le timer de rafraîchissement automatique
   */
  private startRefreshTimer(): void {
    if (this.refreshTimer) {
      clearTimeout(this.refreshTimer);
    }

    const token: any = this.currentToken;
    if (!token || !this.isTokenValid(token)) {
      return;
    }

    let tokenExpiration: number;

    // Gérer les différents formats de token pour l'expiration
    if (token.expiresAt) {
      // Format AuthToken avec expiresAt
      tokenExpiration = token.expiresAt;
    } else if (token.accessToken) {
      // Format moderne avec accessToken
      try {
        const payload = JSON.parse(atob(token.accessToken.split('.')[1]));
        tokenExpiration = payload.exp * 1000; // JWT exp est en secondes
      } catch (error) {
        console.error('❌ Erreur lors du décodage du JWT pour le timer:', error);
        return;
      }
    } else if (token.token) {
      // Format legacy
      try {
        const payload = JSON.parse(atob(token.token.split('.')[1]));
        tokenExpiration = payload.exp * 1000;
      } catch (error) {
        console.error('❌ Erreur lors du décodage du JWT legacy pour le timer:', error);
        return;
      }
    } else {
      console.warn('⚠️ Format de token non reconnu pour le timer');
      return;
    }

    const timeUntilRefresh = Math.max(
      tokenExpiration - Date.now() - this.REFRESH_THRESHOLD,
      300000 // Minimum 5 minutes
    );

    // Vérifier que le temps calculé est valide
    if (!isFinite(timeUntilRefresh) || timeUntilRefresh <= 0) {
      console.warn('⚠️ Temps de rafraîchissement invalide, token probablement expiré');
      return;
    }

    this.refreshTimer = setTimeout(() => {
      if (this.isAuthenticated && token.refreshToken) {
        this.refreshToken().subscribe({
          error: (error) => {
            console.error('Échec du rafraîchissement automatique du token:', error);
            this.clearAuthData();
          }
        });
      }
    }, timeUntilRefresh);

    console.log(`Timer de rafraîchissement programmé dans ${Math.round(timeUntilRefresh / 1000)} secondes`);
  }

  /**
   * Nettoyage lors de la destruction du service
   */
  ngOnDestroy(): void {
    if (this.refreshTimer) {
      clearTimeout(this.refreshTimer);
    }
    this.authState.complete();
  }
}