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, tap, catchError, Observable, throwError, map } from 'rxjs';
import { jwtDecode } from "jwt-decode";

interface DecodedToken {
  exp: number;
  iat?: number;
  sub?: string;
  preferred_username?: string;
  email?: string;
  given_name?: string;
  family_name?: string;
  resource_access?: {
    [key: string]: {
      roles: string[];
    };
  };
}

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

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

  private userRoles$ = new BehaviorSubject<string[]>(this.getRolesFromToken());

  /**
   * Récupère les rôles depuis le token JWT
   */
  private getRolesFromToken(): string[] {
    const token = this.getToken();
    if (!token) return [];

    try {
      const decoded: DecodedToken = jwtDecode(token);
      
      // Priorité 2: Rôles du client (resource_access)
      // Prendre tous les rôles de tous les clients
      if (decoded.resource_access) {
        const allClientRoles: string[] = [];
        
        Object.values(decoded.resource_access).forEach(client => {
          if (client?.roles) {
            allClientRoles.push(...client.roles);
          }
        });
        
        // Retourner les rôles uniques
        return [...new Set(allClientRoles)];
      }
      
      return [];
    } catch {
      return [];
    }
  }

  /**
   * Récupère les rôles de l'utilisateur courant
   */
  getCurrentUserRoles(): string[] {
    return this.userRoles$.value;
  }

  /**
   * Observable des rôles
   */
  onRolesChange(): Observable<string[]> {
    return this.userRoles$.asObservable();
  }

  /**
   * Vérifications rapides par rôle
   */
  isAdmin(): boolean {
    return this.getCurrentUserRoles().includes('admin');
  }

  isMerchant(): boolean {
    return this.getCurrentUserRoles().includes('merchant');
  }

  isSupport(): boolean {
    return this.getCurrentUserRoles().includes('support');
  }

  hasAnyRole(roles: string[]): boolean {
    const userRoles = this.getCurrentUserRoles();
    return roles.some(role => userRoles.includes(role));
  }

  hasAllRoles(roles: string[]): boolean {
    const userRoles = this.getCurrentUserRoles();
    return roles.every(role => userRoles.includes(role));
  }

  /**
   * Rafraîchir les rôles (utile après modification des rôles)
   */
  refreshRoles(): void {
    const roles = this.getRolesFromToken();
    this.userRoles$.next(roles);
  }

  /**
   * Initialisation simple - à appeler dans app.ts
   */
  initialize(): Promise<boolean> {
    return new Promise((resolve) => {
      const token = this.getToken();
      
      if (!token) {
        resolve(false);
        return;
      }

      if (this.isTokenExpired(token)) {
        this.refreshToken().subscribe({
          next: () => resolve(true),
          error: () => {
            this.clearSession(false);
            resolve(false);
          }
        });
      } else {
        resolve(true);
      }
    });
  }
  
  /**
   * Authentifie l'utilisateur via le backend NestJS
   */
  login(username: string, password: string): Observable<AuthResponse> {
    return this.http.post<AuthResponse>(
      `${environment.apiUrl}/auth/login`,
      { username, password }
    ).pipe(
      tap(response => {
        this.handleLoginResponse(response);
      }),
      catchError((error: HttpErrorResponse) => {
        console.error('Login failed:', error);
        return throwError(() => this.getErrorMessage(error));
      })
    );
  }

  /**
   * Rafraîchit le token d'accès
   */
  refreshToken(): Observable<AuthResponse> {
    const refreshToken = localStorage.getItem(this.refreshTokenKey);

    if (!refreshToken) {
      return throwError(() => 'No refresh token available');
    }

    return this.http.post<AuthResponse>(
      `${environment.apiUrl}/auth/refresh`,
      { refresh_token: refreshToken }
    ).pipe(
      tap(response => {
        this.handleLoginResponse(response);
      }),
      catchError((error: HttpErrorResponse) => {
        console.error('Token refresh failed:', error);
        this.clearSession();
        return throwError(() => error);
      })
    );
  }

  /**
   * Déconnecte l'utilisateur
   */
  logout(redirect = true): void {
    this.clearSession(redirect);
    
    // Appel API optionnel (ne pas bloquer dessus)
    this.http.post(`${environment.apiUrl}/auth/logout`, {}).subscribe({
      error: () => {} // Ignorer silencieusement les erreurs de logout
    });
  }

  private clearSession(redirect: boolean = true): void {
    localStorage.removeItem(this.tokenKey);
    localStorage.removeItem(this.refreshTokenKey);
    this.authState$.next(false);
    this.userRoles$.next([]); 
    
    if (redirect) {
      this.router.navigate(['/auth/sign-in']);
    }
  }


  private handleLoginResponse(response: AuthResponse): void {
    if (response?.access_token) {
      localStorage.setItem(this.tokenKey, response.access_token);
      
      if (response.refresh_token) {
        localStorage.setItem(this.refreshTokenKey, response.refresh_token);
      }
      
      // Mettre à jour les rôles après login
      const roles = this.getRolesFromToken();
      this.userRoles$.next(roles);
      
      this.authState$.next(true);
    }
  }

  private getErrorMessage(error: HttpErrorResponse): string {
    if (error?.error?.message) {
      return error.error.message;
    }
    
    if (error.status === 401) {
      return 'Invalid username or password';
    }
    
    return 'Login failed. Please try again.';
  }

  /**
   * Retourne le token JWT stocké
   */
  getToken(): string | null {
    return localStorage.getItem(this.tokenKey);
  }

  /**
   * Vérifie si le token est expiré
   */
  isTokenExpired(token: string): boolean {
    try {
      const decoded: DecodedToken = jwtDecode(token);
      const now = Math.floor(Date.now() / 1000);
      return decoded.exp < (now + 60); // Marge de sécurité de 60 secondes
    } catch {
      return true;
    }
  }

  /**
   * Vérifie si l'utilisateur est authentifié
   */
  isAuthenticated(): boolean {
    const token = this.getToken();
    if (!token) return false;
    return !this.isTokenExpired(token);
  }

  onAuthState() { 
    return this.authState$.asObservable(); 
  }

  /**
   * Récupère les infos utilisateur depuis le backend
   */
  getProfile(): Observable<any> {
    return this.http.get(`${environment.apiUrl}/users/profile/me`).pipe(
      catchError(error => {
        return throwError(() => error);
      })
    );
  }
}