import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
import { environment } from '../../../environments/environment';
import { ApiResponse } from '../models';

/**
 * Service de communication avec l'API ESTAIR
 * Gère les données métier (CRUD)
 */
@Injectable({
  providedIn: 'root'
})
export class EstairService {
  private readonly baseUrl = environment.DATA_URL;
  private readonly apiKey = environment.DATA_APY_KEY;

  constructor(private http: HttpClient) {}

  /**
   * Récupère tous les éléments d'un module
   */
  getAll<T>(moduleName: string, params?: any): Observable<T[]> {
    const url = `${this.baseUrl}admin/${moduleName}`;

    return this.http.get<ApiResponse<T[]>>(url, {
      params
    }).pipe(
      map(response => this.extractData(response)),
      catchError(this.handleError('getAll', []))
    );
  }

  /**
   * Récupère un élément spécifique par UUID
   */
  getOne<T>(moduleName: string, uuid: string): Observable<T> {
    const url = `${this.baseUrl}admin/${moduleName}/${uuid}`;

    return this.http.get<ApiResponse<T>>(url).pipe(
      map(response => this.extractData(response)),
      catchError(this.handleError<T>('getOne'))
    );
  }

  /**
   * Crée un nouvel élément
   */
  create<T>(moduleName: string, data: Partial<T>): Observable<T> {
    const url = `${this.baseUrl}admin/${moduleName}`;

    return this.http.post<ApiResponse<T>>(url, data).pipe(
      map(response => this.extractData(response)),
      catchError(this.handleError<T>('create'))
    );
  }

  /**
   * Met à jour un élément existant
   */
  update<T>(moduleName: string, uuid: string, data: Partial<T>): Observable<T> {
    const url = `${this.baseUrl}admin/${moduleName}/${uuid}`;

    return this.http.put<ApiResponse<T>>(url, {element : data}).pipe(
      map(response => this.extractData(response)),
      catchError(this.handleError<T>('update'))
    );
  }

  /**
   * Supprime un élément
   */
  delete(moduleName: string, uuid: string): Observable<boolean> {
    const url = `${this.baseUrl}admin/${moduleName}/${uuid}`;

    return this.http.delete<ApiResponse<any>>(url).pipe(
      map(response => response.success || false),
      catchError(this.handleError('delete', false))
    );
  }

  /**
   * Suppression en masse
   */
  deleteMultiple(moduleName: string, uuids: string[]): Observable<boolean> {
    const url = `${this.baseUrl}admin/${moduleName}/delete`;

    return this.http.post<ApiResponse<any>>(url, { uuids }).pipe(
      map(response => response.success || false),
      catchError(this.handleError('deleteMultiple', false))
    );
  }

  /**
   * Mise à jour en masse
   */
  updateMultiple<T>(moduleName: string, updates: Array<{uuid: string, data: Partial<T>}>): Observable<boolean> {
    const url = `${this.baseUrl}admin/${moduleName}/edit`;

    return this.http.post<ApiResponse<any>>(url, { updates }).pipe(
      map(response => response.success || false),
      catchError(this.handleError('updateMultiple', false))
    );
  }

  /**
   * Exécution d'actions en masse
   */
  executeAction(moduleName: string, uuids: string[], action: string): Observable<boolean> {
    const url = `${this.baseUrl}admin/${moduleName}/execute`;

    return this.http.post<ApiResponse<any>>(url, { uuids, action }).pipe(
      map(response => response.success || false),
      catchError(this.handleError('executeAction', false))
    );
  }

  /**
   * Recherche avec autocomplétion
   */
  search<T>(moduleName: string, query: string, field?: string): Observable<T[]> {
    const searchField = field || 'name';
    const url = `${this.baseUrl}admin/${moduleName}/search/${searchField}`;

    return this.http.get<ApiResponse<T[]>>(url, {
      params: { q: query }
    }).pipe(
      map(response => this.extractData(response)),
      catchError(this.handleError('search', []))
    );
  }

  /**
   * Récupère les statistiques d'un module
   */
  getStatistics(moduleName: string): Observable<any> {
    const url = `${this.baseUrl}admin/${moduleName}/statistics`;

    return this.http.get<ApiResponse<any>>(url).pipe(
      map(response => this.extractData(response)),
      catchError(this.handleError('getStatistics', {}))
    );
  }

  /**
   * Export des données
   */
  exportData(moduleName: string, format: 'csv' | 'excel' = 'csv', filters?: any): Observable<Blob> {
    const url = `${this.baseUrl}admin/${moduleName}/export`;

    return this.http.post(url, { format, filters }, {
      responseType: 'blob'
    }).pipe(
      catchError(this.handleError('exportData', new Blob()))
    );
  }

  /**
   * Import des données
   */
  importData(moduleName: string, file: File, mappings?: any): Observable<any> {
    const url = `${this.baseUrl}admin/${moduleName}/import`;
    const formData = new FormData();
    formData.append('file', file);

    if (mappings) {
      formData.append('mappings', JSON.stringify(mappings));
    }

    // L'intercepteur ajoutera automatiquement Authorization et X-API-KEY
    // Pas de Content-Type pour FormData (géré automatiquement par le navigateur)
    return this.http.post<ApiResponse<any>>(url, formData).pipe(
      map(response => this.extractData(response)),
      catchError(this.handleError<any>('importData'))
    );
  }

  /**
   * Extrait les données de la réponse API
   */
  private extractData<T>(response: ApiResponse<T>): T {
    if (response.success && response.data) {
      return response.data;
    }
    
    // Si pas de structure success/data, retourner la réponse directement
    return response as unknown as T;
  }

  /**
   * Gestionnaire d'erreur générique
   */
  private handleError<T>(operation = 'operation', result?: T) {
    return (error: any): Observable<T> => {
      console.error(`${operation} failed:`, error);
      
      // Log l'erreur pour le debugging
      if (error.status) {
        console.error(`Status: ${error.status}, Message: ${error.message}`);
      }
      
      // Retourner le résultat par défaut ou lancer l'erreur si pas de résultat par défaut
      if (result !== undefined) {
        return new Observable(observer => {
          observer.next(result as T);
          observer.complete();
        });
      } else {
        throw error;
      }
    };
  }
}
