/**
 * 📊 DataService - Service générique pour les opérations CRUD
 * Remplace les opérations getAll, create, update, delete du GlobalService
 */

import { Injectable } from '@angular/core';
import { Observable, BehaviorSubject } from 'rxjs';
import { map, tap, catchError } from 'rxjs/operators';
import { HttpClientService } from './http-client.service';
import { CacheService } from './cache.service';
import { 
  PagedResult, 
  PagedQueryOptions, 
  QueryOptions, 
  Entity, 
  DataServiceConfig,
  ImportOptions,
  ImportResult
} from '../interfaces/data.interface';

@Injectable({
  providedIn: 'root'
})
export class DataService {
  private loadingStates = new Map<string, BehaviorSubject<boolean>>();

  constructor(
    private httpClient: HttpClientService,
    private cacheService: CacheService
  ) {}

  /**
   * Récupérer tous les éléments d'un module
   */
  getAll<T extends Entity>(
    domaine: string,
    moduleName: string,
    options?: QueryOptions & { useCache?: boolean; cacheTtl?: number }
  ): Observable<T[]> {
    const domain = domaine === 'parametre' ? 'api' : 'data';
    const endpoint = `admin/${moduleName}`;
    
    console.log('DataService.getAll:', { domaine, moduleName, domain, endpoint });

    return this.httpClient.get<T[]>(endpoint, {
      domain,
      params: this.buildQueryParams(options),
      useCache: options?.useCache ?? true,
      cacheTtl: options?.cacheTtl
    }).pipe(
      tap(response => {
        console.log(`getAll ${moduleName} success:`, response);
      }),
      catchError(error => {
        console.error(`getAll ${moduleName} error:`, error);
        throw error;
      })
    );
  }

  /**
   * Récupérer une page d'éléments avec pagination
   */
  getPagedData<T extends Entity>(
    domaine: string,
    moduleName: string,
    options: PagedQueryOptions & { useCache?: boolean; cacheTtl?: number }
  ): Observable<PagedResult<T>> {
    const domain = domaine === 'parametre' ? 'api' : 'data';
    const endpoint = `admin/${moduleName}`;
    
    console.log('DataService.getPagedData:', { endpoint, domaine, domain, moduleName, options });

    return this.httpClient.get<PagedResult<T>>(endpoint, {
      domain,
      params: this.buildPagedQueryParams(options),
      useCache: options?.useCache ?? true,
      cacheTtl: options?.cacheTtl
    }).pipe(
      tap(response => {
        console.log(`getPagedData ${moduleName} success:`, response);
      })
    );
  }

  /**
   * Récupérer un élément par ID
   */
  getById<T extends Entity>(
    domaine: string,
    moduleName: string,
    id: string | number,
    options?: { useCache?: boolean; cacheTtl?: number }
  ): Observable<T> {
    const domain = domaine === 'parametre' ? 'api' : 'data';
    const endpoint = `admin/${moduleName}/${id}`;
    
    console.log('DataService.getById:', { domaine, moduleName, id });

    return this.httpClient.get<T>(endpoint, {
      domain,
      useCache: options?.useCache ?? true,
      cacheTtl: options?.cacheTtl
    }).pipe(
      tap(response => {
        console.log(`getById ${moduleName}/${id} success:`, response);
      })
    );
  }

  /**
   * Créer un nouvel élément
   */
  create<T extends Entity>(
    domaine: string,
    moduleName: string,
    data: Omit<T, 'id' | 'createdAt' | 'updatedAt'>
  ): Observable<T> {
    const domain = domaine === 'parametre' ? 'api' : 'data';
    const endpoint = `admin/${moduleName}`;
    
    console.log('DataService.create:', { domaine, moduleName, data });

    return this.httpClient.post<T>(endpoint, { element: data }, { domain }).pipe(
      tap(response => {
        // Invalider le cache pour ce module
        this.invalidateModuleCache(domaine, moduleName);
        console.log(`create ${moduleName} success:`, response);
      })
    );
  }

  /**
   * Mettre à jour un élément existant
   */
  update<T extends Entity>(
    domaine: string,
    moduleName: string,
    id: string | number,
    data: Partial<T>
  ): Observable<T> {
    const domain = domaine === 'parametre' ? 'api' : 'data';
    const endpoint = `admin/${moduleName}/${id}`;
    
    console.log('DataService.update:', { domaine, moduleName, id, data });

    return this.httpClient.put<T>(endpoint, {element : data}, { domain }).pipe(
      tap(response => {
        // Invalider le cache pour ce module et cet élément
        this.invalidateModuleCache(domaine, moduleName);
        console.log(`update ${moduleName}/${id} success:`, response);
      })
    );
  }

  /**
   * Supprimer un élément
   */
  delete<T = any>(
    domaine: string,
    moduleName: string,
    id: string | number
  ): Observable<T> {
    const domain = domaine === 'parametre' ? 'api' : 'data';
    const endpoint = `admin/${moduleName}/${id}`;
    
    console.log('DataService.delete:', { domaine, moduleName, id });

    return this.httpClient.delete<T>(endpoint, { domain }).pipe(
      tap(response => {
        // Invalider le cache pour ce module
        this.invalidateModuleCache(domaine, moduleName);
        console.log(`delete ${moduleName}/${id} success:`, response);
      })
    );
  }

  /**
   * Supprimer plusieurs éléments
   */
  deleteMultiple<T = any>(
    domaine: string,
    moduleName: string,
    ids: (string | number)[]
  ): Observable<T> {
    const domain = domaine === 'parametre' ? 'api' : 'data';
    const endpoint = `admin/${moduleName}/delete`;
    
    console.log('DataService.deleteMultiple:', { domaine, moduleName, ids });

    return this.httpClient.post<T>(endpoint, { ids }, { 
      domain,
      headers: { 'Content-Type': 'application/json' }
    }).pipe(
      tap(response => {
        this.invalidateModuleCache(domaine, moduleName);
        console.log(`deleteMultiple ${moduleName} success:`, response);
      })
    );
  }

  /**
   * Rechercher des éléments
   */
  search<T extends Entity>(
    domaine: string,
    moduleName: string,
    searchTerm: string,
    options?: QueryOptions & { useCache?: boolean }
  ): Observable<T[]> {
    const domain = domaine === 'parametre' ? 'api' : 'data';
    const endpoint = `admin/${moduleName}/search`;
    
    const searchOptions = {
      ...options,
      search: searchTerm
    };
    
    console.log('DataService.search:', { domaine, moduleName, searchTerm, options });

    return this.httpClient.get<any>(endpoint, {
      domain,
      params: this.buildQueryParams(searchOptions),
      useCache: options?.useCache ?? false // Search non mis en cache par défaut
    }).pipe(
      map((response: any) => {
        // Le serveur retourne {success: true, result: [...]}
        // Extraire le tableau result
        if (response && response.success && Array.isArray(response.result)) {
          return response.result as T[];
        }
        // Fallback: si la réponse est déjà un tableau
        if (Array.isArray(response)) {
          return response as T[];
        }
        return [] as T[];
      }),
      tap(response => {
        console.log(`search ${moduleName} success:`, response);
      })
    );
  }

  /**
   * Importer des données depuis un fichier
   */
  importData<T = any>(
    domaine: string,
    moduleName: string,
    file: File,
    options: ImportOptions
  ): Observable<ImportResult> {
    const domain = domaine === 'parametre' ? 'api' : 'data';
    const endpoint = `admin/${moduleName}/import`;
    
    console.log('DataService.importData:', { domaine, moduleName, file: file.name, options });

    return this.httpClient.uploadFile<ImportResult>(endpoint, file, {
      domain,
      additionalData: options
    }).pipe(
      tap(result => {
        if (result.success) {
          this.invalidateModuleCache(domaine, moduleName);
        }
        console.log(`import ${moduleName} result:`, result);
      })
    );
  }

  /**
   * Exporter des données
   */
  exportData(
    domaine: string,
    moduleName: string,
    format: 'csv' | 'xlsx' | 'json' = 'csv',
    options?: QueryOptions
  ): Observable<Blob> {
    const domain = domaine === 'parametre' ? 'api' : 'data';
    const endpoint = `admin/${moduleName}/export`;
    
    console.log('DataService.exportData:', { domaine, moduleName, format, options });

    return this.httpClient.request<Blob>('GET', endpoint, {
      domain,
      params: { 
        format,
        ...this.buildQueryParams(options)
      },
      responseType: 'blob'
    }).pipe(
      tap(() => {
        console.log(`export ${moduleName} success`);
      })
    );
  }

  /**
   * Récupérer les éléments de vue liste (pour remplacer getListViewlements)
   */
  getListViewElements<T = any>(
    moduleName: string,
    options?: QueryOptions & { useCache?: boolean; cacheTtl?: number }
  ): Observable<T[]> {
    const endpoint = `v1/metadata/${moduleName}`;
    
    console.log('DataService.getListViewElements:', { moduleName, options });

    return this.httpClient.get<T[]>(endpoint, {
      domain: 'api',
      params: this.buildQueryParams(options),
      useCache: options?.useCache ?? true,
      cacheTtl: options?.cacheTtl
    }).pipe(
      tap(response => {
        console.log(`getListViewElements ${moduleName} success:`, response);
      }),
      catchError(error => {
        console.error(`getListViewElements ${moduleName} error:`, error);
        throw error;
      })
    );
  }

  /**
   * Importer des données depuis FormData (pour remplacer importData global)
   */
  import<T = any>(data: FormData): Observable<T> {
    const endpoint = `admin/import`;
    
    console.log('DataService.import:', { data });

    return this.httpClient.post<T>(endpoint, data, {
      domain: 'data'
    }).pipe(
      tap(response => {
        console.log('import success:', response);
      }),
      catchError(error => {
        console.error('import error:', error);
        throw error;
      })
    );
  }

  /**
   * Obtenir l'état de chargement d'un module
   */
  getLoadingState(moduleName: string): Observable<boolean> {
    if (!this.loadingStates.has(moduleName)) {
      this.loadingStates.set(moduleName, new BehaviorSubject<boolean>(false));
    }
    return this.loadingStates.get(moduleName)!.asObservable();
  }

  /**
   * Définir l'état de chargement d'un module
   */
  setLoadingState(moduleName: string, loading: boolean): void {
    if (!this.loadingStates.has(moduleName)) {
      this.loadingStates.set(moduleName, new BehaviorSubject<boolean>(false));
    }
    this.loadingStates.get(moduleName)!.next(loading);
  }

  /**
   * Invalider le cache pour un module spécifique
   */
  private invalidateModuleCache(domaine: string, moduleName: string): void {
    const patterns = [
      `http_get_*admin/${moduleName}*`,
      `http_request_*admin/${moduleName}*`
    ];
    
    patterns.forEach(pattern => {
      const invalidated = this.cacheService.invalidatePattern(new RegExp(pattern));
      console.log(`Cache invalidé pour ${pattern}: ${invalidated} entrées`);
    });
  }

  /**
   * Construire les paramètres de requête
   */
  private buildQueryParams(options?: QueryOptions): { [key: string]: any } {
    if (!options) return {};
    
    const params: { [key: string]: any } = {};
    
    if (options.search) {
      params.search = options.search;
    }
    
    if (options.sort) {
      params.sort = options.sort;
    }
    
    if (options.filters) {
      Object.keys(options.filters).forEach(key => {
        params[`filter.${key}`] = options.filters![key];
      });
    }
    
    if (options.include && options.include.length > 0) {
      params.include = options.include.join(',');
    }
    
    if (options.exclude && options.exclude.length > 0) {
      params.exclude = options.exclude.join(',');
    }
    
    return params;
  }

  /**
   * Construire les paramètres de requête avec pagination
   */
  private buildPagedQueryParams(options: PagedQueryOptions): { [key: string]: any } {
    const params = this.buildQueryParams(options);
    
    params.page = options.page;
    params.size = options.size;
    
    return params;
  }

  /**
   * Nettoyer les observables lors de la destruction
   */
  ngOnDestroy(): void {
    this.loadingStates.forEach(subject => subject.complete());
    this.loadingStates.clear();
  }
}