// src/app/core/services/auth.service.ts
import { Injectable, inject } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Router } from '@angular/router';
import { environment } from '@environments/environment';
import { BehaviorSubject, Observable, throwError, tap, catchError, map, of } from 'rxjs';

// Interfaces pour les DTOs de l'API
export interface LoginDto {
  username: string;
  password: string;
}

export interface RefreshTokenDto {
  refresh_token: string;
}

export interface LoginResponseDto {
  access_token: string;
  refresh_token: string;
  expires_in: number;
  token_type: string;
}

export interface LogoutResponseDto {
  message: string;
}

export interface AuthStatusResponseDto {
  authenticated: boolean;
  status: string;
}

export interface UserProfileDto {
  id: string;
  username: string;
  email: string;
  firstName: string;
  lastName: string;
  roles: string[];
  emailVerified: boolean;
}

export interface TokenValidationResponseDto {
  valid: boolean;
  user: {
    id: string;
    username: string;
    email: string;
    firstName: string;
    lastName: string;
    roles: string[];
  };
  expires_in: number;
}

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private readonly http = inject(HttpClient);
  private readonly router = inject(Router);
  
  private readonly tokenKey = 'access_token';
  private readonly refreshTokenKey = 'refresh_token';
  
  private authState$ = new BehaviorSubject<boolean>(this.isAuthenticated());
  private userProfile$ = new BehaviorSubject<UserProfileDto | null>(null);
  private initialized$ = new BehaviorSubject<boolean>(false);

  // === INITIALISATION DE L'APPLICATION ===

  /**
   * Initialise l'authentification au démarrage de l'application
   */
  async initialize(): Promise<boolean> {
    console.log('🔄 Initialisation du service d\'authentification...');

    try {
      const token = this.getAccessToken();
      
      if (!token) {
        console.log('🔍 Aucun token trouvé, utilisateur non authentifié');
        this.initialized$.next(true);
        return false;
      }

      if (this.isTokenExpired(token)) {
        console.log('⚠️ Token expiré, tentative de rafraîchissement...');
        const refreshSuccess = await this.tryRefreshToken();
        this.initialized$.next(true);
        return refreshSuccess;
      }

      // Token valide, vérifier le profil utilisateur
      console.log('✅ Token valide détecté, vérification du profil...');
      await this.loadUserProfile().toPromise();
      
      this.authState$.next(true);
      this.initialized$.next(true);
      
      console.log('🎯 Authentification initialisée avec succès');
      return true;

    } catch (error) {
      console.error('❌ Erreur lors de l\'initialisation de l\'auth:', error);
      this.clearAuthData();
      this.initialized$.next(true);
      return false;
    }
  }

  /**
   * Tente de rafraîchir le token de manière synchrone
   */
  private async tryRefreshToken(): Promise<boolean> {
    const refreshToken = this.getRefreshToken();
    
    if (!refreshToken) {
      console.log('🔍 Aucun refresh token disponible');
      return false;
    }

    try {
      // Convertir l'Observable en Promise pour l'initialisation
      const response = await this.refreshToken().toPromise();
      console.log('✅ Token rafraîchi avec succès lors de l\'initialisation');
      return true;
    } catch (error) {
      console.error('❌ Échec du rafraîchissement du token:', error);
      this.clearAuthData();
      return false;
    }
  }

  /**
   * Observable pour suivre l'état d'initialisation
   */
  getInitializedState(): Observable<boolean> {
    return this.initialized$.asObservable();
  }

  // === MÉTHODES EXISTANTES AVEC AMÉLIORATIONS ===

  /**
   * Connexion utilisateur
   */
  login(credentials: LoginDto): Observable<LoginResponseDto> {
    return this.http.post<LoginResponseDto>(
      `${environment.iamApiUrl}/auth/login`, 
      credentials
    ).pipe(
      tap(response => {
        this.handleLoginSuccess(response);
        this.loadUserProfile().subscribe(); // Charger le profil après connexion
      }),
      catchError(error => this.handleLoginError(error))
    );
  }

  /**
   * Rafraîchissement du token
   */
  refreshToken(): Observable<LoginResponseDto> {
    const refreshToken = this.getRefreshToken();
    
    if (!refreshToken) {
      return throwError(() => new Error('No refresh token available'));
    }

    return this.http.post<LoginResponseDto>(
      `${environment.iamApiUrl}/auth/refresh`,
      { refresh_token: refreshToken }
    ).pipe(
      tap(response => {
        this.handleLoginSuccess(response);
        console.log('🔄 Token rafraîchi avec succès');
      }),
      catchError(error => {
        console.error('❌ Échec du rafraîchissement du token:', error);
        this.clearAuthData();
        return throwError(() => error);
      })
    );
  }

  /**
   * Déconnexion utilisateur
   */
  logout(): Observable<LogoutResponseDto> {
    return this.http.post<LogoutResponseDto>(
      `${environment.iamApiUrl}/auth/logout`, 
      {}
    ).pipe(
      tap(() => {
        this.clearAuthData();
        console.log('👋 Déconnexion réussie');
      }),
      catchError(error => {
        this.clearAuthData(); // Nettoyer même en cas d'erreur
        return throwError(() => error);
      })
    );
  }

  /**
   * Chargement du profil utilisateur
   */
  loadUserProfile(): Observable<UserProfileDto> {
    return this.http.get<UserProfileDto>(
      `${environment.iamApiUrl}/auth/profile`
    ).pipe(
      tap(profile => {
        this.userProfile$.next(profile);
        console.log('👤 Profil utilisateur chargé:', profile.username);
      }),
      catchError(error => {
        console.error('❌ Erreur lors du chargement du profil:', error);
        return throwError(() => error);
      })
    );
  }

  /**
   * Gestion de la connexion réussie
   */
  private handleLoginSuccess(response: LoginResponseDto): void {
    if (response.access_token) {
      localStorage.setItem(this.tokenKey, response.access_token);
      
      if (response.refresh_token) {
        localStorage.setItem(this.refreshTokenKey, response.refresh_token);
      }
      
      this.authState$.next(true);
      console.log('✅ Connexion réussie');
    }
  }

  /**
   * Nettoyage des données d'authentification
   */
  private clearAuthData(): void {
    localStorage.removeItem(this.tokenKey);
    localStorage.removeItem(this.refreshTokenKey);
    this.authState$.next(false);
    this.userProfile$.next(null);
    console.log('🧹 Données d\'authentification nettoyées');
  }

  /**
   * Validation du token
   */
  validateToken(): Observable<TokenValidationResponseDto> {
    return this.http.get<TokenValidationResponseDto>(
      `${environment.iamApiUrl}/auth/validate`
    );
  }

  // === OBSERVABLES POUR COMPOSANTS ===

  getAuthState(): Observable<boolean> {
    return this.authState$.asObservable();
  }

  getUserProfile(): Observable<UserProfileDto | null> {
    return this.userProfile$.asObservable();
  }

  /**
 * Récupère les rôles de l'utilisateur courant
 */
getCurrentUserRoles(): string[] {
  const token = this.getAccessToken();
  if (!token) return [];

  try {
    const payload = JSON.parse(atob(token.split('.')[1]));
    const decoded: any = payload;
    
    // Récupérer tous les rôles de tous les clients
    if (decoded.resource_access) {
      const allRoles: string[] = [];
      
      Object.values(decoded.resource_access).forEach((client: any) => {
        if (client?.roles) {
          allRoles.push(...client.roles);
        }
      });
      
      return [...new Set(allRoles)];
    }
    
    return [];
  } catch {
    return [];
  }
}

/**
 * Observable de l'état d'authentification
 */
onAuthState(): Observable<boolean> {
  return this.authState$.asObservable();
}

/**
 * Récupère le profil utilisateur
 */
getProfile(): Observable<any> {
  return this.getUserProfile();
}

/**
 * Vérifie si l'utilisateur a un rôle spécifique
 */
hasRole(role: string): boolean {
  return this.getCurrentUserRoles().includes(role);
}

/**
 * Vérifie si l'utilisateur a un des rôles spécifiés
 */
hasAnyRole(roles: string[]): boolean {
  const userRoles = this.getCurrentUserRoles();
  return roles.some(role => userRoles.includes(role));
}

  // === GETTERS POUR LES TOKENS ===

  getAccessToken(): string | null {
    return localStorage.getItem(this.tokenKey);
  }

  getRefreshToken(): string | null {
    return localStorage.getItem(this.refreshTokenKey);
  }

  // === METHODES PRIVEES ===

  private handleLoginError(error: HttpErrorResponse): Observable<never> {
    let errorMessage = 'Login failed';
    
    if (error.status === 401) {
      errorMessage = 'Invalid username or password';
    } else if (error.status === 403) {
      errorMessage = 'Account is disabled or not fully set up';
    } else if (error.error?.message) {
      errorMessage = error.error.message;
    }
    
    return throwError(() => new Error(errorMessage));
  }

  // === VERIFICATIONS D'ETAT ===

  isAuthenticated(): boolean {
    const token = this.getAccessToken();
    return !!token && !this.isTokenExpired(token);
  }

  private isTokenExpired(token: string): boolean {
    try {
      const payload = JSON.parse(atob(token.split('.')[1]));
      const expiry = payload.exp;
      return (Math.floor((new Date).getTime() / 1000)) >= expiry;
    } catch {
      return true;
    }
  }
}