/**
 * 🌐 HttpClientService - Service HTTP centralisé avec configuration et intercepteurs
 * Remplace les appels HTTP éparpillés du GlobalService
 */

import { Injectable, Optional, Inject } from '@angular/core';
import { HttpClient, HttpHeaders, HttpErrorResponse, HttpParams } from '@angular/common/http';
import { Router } from '@angular/router';
import { Observable, throwError, BehaviorSubject } from 'rxjs';
import { catchError, retry, timeout, tap } from 'rxjs/operators';
import { environment } from '../../../environments/environment';
import { CacheService } from './cache.service';
import { AuthenticationService } from './authentication.service';
import { ApiConfig, ApiResponse, ApiError } from '../interfaces/api.interface';

export const HTTP_CONFIG = 'HTTP_CONFIG';

@Injectable({
  providedIn: 'root'
})
export class HttpClientService {
  private readonly config: ApiConfig;
  private requestsInProgress = new BehaviorSubject<number>(0);

  constructor(
    private http: HttpClient,
    private cacheService: CacheService,
    private authService: AuthenticationService,
    private router: Router,
    @Optional() @Inject(HTTP_CONFIG) customConfig?: Partial<ApiConfig>
  ) {
    this.config = {
      baseUrl: environment.API_URL,
      dataUrl: environment.DATA_URL,
      apiKey: environment.X_APY_KEY,
      dataApiKey: environment.DATA_APY_KEY,
      timeout: 30000,
      retryAttempts: 2,
      domain: 'default',
      ...customConfig
    };
  }

  /**
   * Nombre de requêtes en cours
   */
  get requestsInProgress$(): Observable<number> {
    return this.requestsInProgress.asObservable();
  }

  /**
   * GET avec cache optionnel
   */
  get<T>(
    endpoint: string, 
    options: {
      domain?: 'api' | 'data';
      useCache?: boolean;
      cacheTtl?: number;
      params?: HttpParams | { [key: string]: any };
      headers?: HttpHeaders | { [key: string]: string };
    } = {}
  ): Observable<T> {
    const url = this.buildUrl(endpoint, options.domain);
    const cacheKey = this.cacheService.generateKey('http_get', url, options.params || {});
    
    // Vérifier le cache si activé
    if (options.useCache) {
      const cached = this.cacheService.get<T>(cacheKey);
      if (cached) {
        return new Observable(observer => {
          observer.next(cached);
          observer.complete();
        });
      }
    }

    const headers = this.buildHeaders(options.domain, options.headers);
    let httpParams = this.buildParams(options.params);

    console.log(`GET ${url}`, { headers: headers.keys(), params: httpParams?.keys() });

    return this.executeRequest<T>(
      this.http.get<T>(url, { headers, params: httpParams }),
      cacheKey,
      options.useCache,
      options.cacheTtl
    );
  }

  /**
   * POST
   */
  post<T>(
    endpoint: string,
    body: any,
    options: {
      domain?: 'api' | 'data';
      headers?: HttpHeaders | { [key: string]: string };
    } = {}
  ): Observable<T> {
    const url = this.buildUrl(endpoint, options.domain);
    const headers = this.buildHeaders(options.domain, options.headers);
    
    console.log(`POST ${url}`, { body, headers: headers.keys() });

    return this.executeRequest<T>(
      this.http.post<T>(url, body, { headers })
    );
  }

  /**
   * PUT
   */
  put<T>(
    endpoint: string,
    body: any,
    options: {
      domain?: 'api' | 'data';
      headers?: HttpHeaders | { [key: string]: string };
    } = {}
  ): Observable<T> {
    const url = this.buildUrl(endpoint, options.domain);
    const headers = this.buildHeaders(options.domain, options.headers);
    
    console.log(`PUT ${url}`, { body, headers: headers.keys() });

    return this.executeRequest<T>(
      this.http.put<T>(url, body, { headers })
    );
  }

  /**
   * DELETE
   */
  delete<T>(
    endpoint: string,
    options: {
      domain?: 'api' | 'data';
      headers?: HttpHeaders | { [key: string]: string };
    } = {}
  ): Observable<T> {
    const url = this.buildUrl(endpoint, options.domain);
    const headers = this.buildHeaders(options.domain, options.headers);
    
    console.log(`DELETE ${url}`, { headers: headers.keys() });

    return this.executeRequest<T>(
      this.http.delete<T>(url, { headers })
    );
  }

  /**
   * PATCH
   */
  patch<T>(
    endpoint: string,
    body: any,
    options: {
      domain?: 'api' | 'data';
      headers?: HttpHeaders | { [key: string]: string };
    } = {}
  ): Observable<T> {
    const url = this.buildUrl(endpoint, options.domain);
    const headers = this.buildHeaders(options.domain, options.headers);
    
    console.log(`PATCH ${url}`, { body, headers: headers.keys() });

    return this.executeRequest<T>(
      this.http.patch<T>(url, body, { headers })
    );
  }

  /**
   * Upload de fichier avec progression
   */
  uploadFile<T>(
    endpoint: string,
    file: File,
    options: {
      domain?: 'api' | 'data';
      additionalData?: { [key: string]: any };
      progressCallback?: (progress: number) => void;
    } = {}
  ): Observable<T> {
    const url = this.buildUrl(endpoint, options.domain);
    const formData = new FormData();
    
    formData.append('file', file);
    
    if (options.additionalData) {
      Object.keys(options.additionalData).forEach(key => {
        formData.append(key, options.additionalData![key]);
      });
    }

    // Headers sans Content-Type pour les uploads
    const headers = this.buildHeaders(options.domain, undefined, true);
    
    console.log(`UPLOAD ${url}`, { fileName: file.name, size: file.size });

    return this.executeRequest<T>(
      this.http.post<T>(url, formData, { headers }) as Observable<T>
    );
  }

  /**
   * Requête personnalisée avec tous les paramètres
   */
  request<T>(
    method: string,
    endpoint: string,
    options: {
      body?: any;
      domain?: 'api' | 'data';
      headers?: HttpHeaders | { [key: string]: string };
      params?: HttpParams | { [key: string]: any };
      useCache?: boolean;
      cacheTtl?: number;
      responseType?: 'json' | 'text' | 'blob' | 'arraybuffer';
    } = {}
  ): Observable<T> {
    const url = this.buildUrl(endpoint, options.domain);
    const headers = this.buildHeaders(options.domain, options.headers);
    const params = this.buildParams(options.params);
    const cacheKey = options.useCache ? 
      this.cacheService.generateKey('http_request', method, url, options.body || {}, options.params || {}) : '';

    // Vérifier le cache pour GET uniquement
    if (options.useCache && method.toUpperCase() === 'GET') {
      const cached = this.cacheService.get<T>(cacheKey);
      if (cached) {
        return new Observable(observer => {
          observer.next(cached);
          observer.complete();
        });
      }
    }

    const requestOptions = {
      body: options.body,
      headers,
      params,
      responseType: (options.responseType || 'json') as 'json'
    };

    console.log(`${method.toUpperCase()} ${url}`, requestOptions);

    return this.executeRequest<T>(
      this.http.request<T>(method, url, requestOptions),
      cacheKey,
      options.useCache && method.toUpperCase() === 'GET',
      options.cacheTtl
    );
  }

  /**
   * Construire l'URL complète
   */
  private buildUrl(endpoint: string, domain: 'api' | 'data' = 'api'): string {
    const baseUrl = domain === 'data' ? this.config.dataUrl : this.config.baseUrl;
    let cleanEndpoint = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint;
    // Pour le domaine CHAPS (api), ajouter le préfixe v1/ si absent
    // Les routes v1/ utilisent l'authentification Bearer Token (JWT)
    if (domain === 'api' && !cleanEndpoint.startsWith('v1/')) {
      cleanEndpoint = `v1/${cleanEndpoint}`;
    }
    return `${baseUrl}${cleanEndpoint}`;
  }

  /**
   * Construire les headers
   */
  private buildHeaders(
    domain: 'api' | 'data' = 'api',
    additionalHeaders?: HttpHeaders | { [key: string]: string },
    isFileUpload: boolean = false
  ): HttpHeaders {
    let headers = new HttpHeaders();

    // API Key selon le domaine
    const apiKey = domain === 'data' ? this.config.dataApiKey : this.config.apiKey;
    if (apiKey) {
      headers = headers.set('X-API-KEY', apiKey);
    }

    // Content-Type sauf pour les uploads
    if (!isFileUpload) {
      headers = headers.set('Content-Type', 'application/json');
    }

    // Authorization si authentifié
    const authToken = this.authService.currentToken;
    console.log('🔑 Token récupéré:', authToken);
    console.log('✅ Token valide?', authToken ? this.authService.isTokenValid(authToken) : false);
    if (authToken && this.authService.isTokenValid(authToken)) {
      const authHeader = `${authToken.tokenType} ${authToken.accessToken}`;
      console.log('📤 Authorization header:', authHeader);
      headers = headers.set('Authorization', authHeader);
    } else {
      console.warn('⚠️ Aucun token valide trouvé - requête sans authentification');
    }

    // Headers additionnels
    if (additionalHeaders) {
      if (additionalHeaders instanceof HttpHeaders) {
        additionalHeaders.keys().forEach(key => {
          const value = additionalHeaders.get(key);
          if (value) {
            headers = headers.set(key, value);
          }
        });
      } else {
        Object.keys(additionalHeaders).forEach(key => {
          headers = headers.set(key, additionalHeaders[key]);
        });
      }
    }

    return headers;
  }

  /**
   * Construire les paramètres
   */
  private buildParams(params?: HttpParams | { [key: string]: any }): HttpParams | undefined {
    if (!params) return undefined;
    
    if (params instanceof HttpParams) {
      return params;
    }

    let httpParams = new HttpParams();
    Object.keys(params).forEach(key => {
      const value = params[key];
      if (value !== null && value !== undefined) {
        if (Array.isArray(value)) {
          value.forEach(v => httpParams = httpParams.append(key, v.toString()));
        } else {
          httpParams = httpParams.set(key, value.toString());
        }
      }
    });

    return httpParams;
  }

  /**
   * Exécuter la requête avec gestion d'erreur et cache
   */
  private executeRequest<T>(
    request: Observable<T>,
    cacheKey?: string,
    useCache?: boolean,
    cacheTtl?: number
  ): Observable<T> {
    // Incrémenter le compteur de requêtes
    this.requestsInProgress.next(this.requestsInProgress.value + 1);

    return request.pipe(
      timeout(this.config.timeout || 30000),
      retry(this.config.retryAttempts),
      tap((response: T) => {
        // Mettre en cache si demandé
        if (useCache && cacheKey && response) {
          this.cacheService.set(cacheKey, response, { 
            ttl: cacheTtl || this.config.cacheTtl 
          });
        }

        console.log('Request success:', response);
        
        // Décrémenter le compteur
        this.requestsInProgress.next(Math.max(0, this.requestsInProgress.value - 1));
      }),
      catchError((error: HttpErrorResponse) => {
        // Décrémenter le compteur en cas d'erreur
        this.requestsInProgress.next(Math.max(0, this.requestsInProgress.value - 1));
        
        console.error('HTTP Error:', {
          status: error.status,
          statusText: error.statusText,
          message: error.message,
          url: error.url,
          error: error.error
        });

        // Gestion spécifique des erreurs d'authentification
        if (error.status === 401 || error.status === 403) {
          this.handleAuthError(error);
        }

        // Transformer l'erreur en format standard
        const apiError: ApiError = {
          status: error.status,
          statusText: error.statusText,
          message: error.error?.message || error.message || 'Erreur inconnue',
          timestamp: new Date().toISOString(),
          path: error.url || '',
          details: error.error
        };

        return throwError(apiError);
      })
    );
  }

  /**
   * Gérer les erreurs d'authentification
   */
  private handleAuthError(error: HttpErrorResponse): void {
    if (error.status === 401) {
      console.warn('❌ 401 Unauthorized - Token invalide ou expiré');
      console.log('🔒 Déconnexion forcée et redirection vers /login');

      // Nettoyer immédiatement le localStorage
      localStorage.removeItem('__token');
      localStorage.removeItem('__user');

      // Mettre à jour l'état d'authentification
      this.authService['updateState']({
        isAuthenticated: false,
        user: null,
        token: null,
        loading: false,
        error: null
      });

      // Utiliser setTimeout pour s'assurer que la navigation se fait en dehors du cycle de détection Angular
      setTimeout(() => {
        console.log('🔄 Navigation vers /login...');
        this.router.navigate(['/login'], {
          replaceUrl: true,
          queryParams: { sessionExpired: 'true' }
        }).then(success => {
          console.log('✅ Navigation réussie:', success);
          if (!success) {
            // Si la navigation échoue, forcer avec window.location
            console.warn('⚠️ Navigation Angular échouée, utilisation de window.location');
            window.location.href = '/login';
          }
        }).catch(err => {
          console.error('❌ Erreur de navigation:', err);
          window.location.href = '/login';
        });
      }, 0);
    } else if (error.status === 403) {
      // Permissions insuffisantes
      console.warn('⚠️ 403 Forbidden - Permissions insuffisantes pour cette action');
    }
  }

  /**
   * Invalider le cache pour un pattern donné
   */
  invalidateCache(pattern: string | RegExp): number {
    return this.cacheService.invalidatePattern(pattern);
  }

  /**
   * Statistiques du service
   */
  getStats() {
    return {
      requestsInProgress: this.requestsInProgress.value,
      cacheStats: this.cacheService.getCurrentStats(),
      config: {
        baseUrl: this.config.baseUrl,
        dataUrl: this.config.dataUrl,
        timeout: this.config.timeout,
        retryAttempts: this.config.retryAttempts
      }
    };
  }
}