import { Injectable, inject } from '@angular/core';
import { HttpClient, HttpParams, HttpHeaders, HttpEvent, HttpEventType, HttpRequest } from '@angular/common/http';
import { Observable, map, filter } from 'rxjs';
import { environment } from '../../../environments/environment';

export interface UploadProgress<T> {
  progress: number;
  loaded: number;
  total: number;
  complete: boolean;
  response?: T;
}

export interface ApiResponse<T> {
  success: boolean;
  data: T;
  timestamp: string;
  message?: string;
}

export interface PaginatedResponse<T> {
  data: T[];
  total: number;
  page: number;
  limit: number;
  totalPages: number;
  hasNextPage: boolean;
}

@Injectable({
  providedIn: 'root',
})
export class ApiService {
  private readonly http = inject(HttpClient);
  private readonly baseUrl = environment.apiBaseUrl;

  get<T>(path: string, params?: Record<string, string | number | boolean>): Observable<T> {
    let httpParams = new HttpParams();
    if (params) {
      Object.entries(params).forEach(([key, value]) => {
        if (value !== undefined && value !== null) {
          httpParams = httpParams.set(key, String(value));
        }
      });
    }
    return this.http
      .get<ApiResponse<T>>(`${this.baseUrl}${path}`, { params: httpParams })
      .pipe(map((response) => response.data));
  }

  getRaw<T>(path: string, params?: Record<string, string | number | boolean>): Observable<T> {
    let httpParams = new HttpParams();
    if (params) {
      Object.entries(params).forEach(([key, value]) => {
        if (value !== undefined && value !== null) {
          httpParams = httpParams.set(key, String(value));
        }
      });
    }
    return this.http.get<T>(`${this.baseUrl}${path}`, { params: httpParams });
  }

  post<T>(path: string, body: unknown): Observable<T> {
    return this.http
      .post<ApiResponse<T>>(`${this.baseUrl}${path}`, body)
      .pipe(map((response) => response.data));
  }

  postRaw<T>(path: string, body: unknown): Observable<T> {
    return this.http.post<T>(`${this.baseUrl}${path}`, body);
  }

  put<T>(path: string, body: unknown): Observable<T> {
    return this.http
      .put<ApiResponse<T>>(`${this.baseUrl}${path}`, body)
      .pipe(map((response) => response.data));
  }

  patch<T>(path: string, body: unknown): Observable<T> {
    return this.http
      .patch<ApiResponse<T>>(`${this.baseUrl}${path}`, body)
      .pipe(map((response) => response.data));
  }

  delete<T>(path: string): Observable<T> {
    return this.http
      .delete<ApiResponse<T>>(`${this.baseUrl}${path}`)
      .pipe(map((response) => response.data));
  }

  upload<T>(path: string, formData: FormData): Observable<T> {
    return this.http
      .post<ApiResponse<T>>(`${this.baseUrl}${path}`, formData)
      .pipe(map((response) => response.data));
  }

  /**
   * Upload with progress tracking
   * Returns an observable that emits upload progress and final response
   */
  uploadWithProgress<T>(path: string, formData: FormData): Observable<UploadProgress<T>> {
    const req = new HttpRequest('POST', `${this.baseUrl}${path}`, formData, {
      reportProgress: true,
    });

    return this.http.request<ApiResponse<T>>(req).pipe(
      map((event: HttpEvent<ApiResponse<T>>) => {
        switch (event.type) {
          case HttpEventType.UploadProgress:
            const total = event.total || 0;
            const loaded = event.loaded;
            const progress = total > 0 ? Math.round((loaded / total) * 100) : 0;
            return {
              progress,
              loaded,
              total,
              complete: false,
            } as UploadProgress<T>;

          case HttpEventType.Response:
            return {
              progress: 100,
              loaded: event.body?.data ? 1 : 0,
              total: 1,
              complete: true,
              response: event.body?.data,
            } as UploadProgress<T>;

          default:
            return {
              progress: 0,
              loaded: 0,
              total: 0,
              complete: false,
            } as UploadProgress<T>;
        }
      })
    );
  }

  download(path: string): Observable<Blob> {
    return this.http.get(`${this.baseUrl}${path}`, {
      responseType: 'blob',
    });
  }

  getBlob(url: string): Observable<Blob> {
    return this.http.get(url, { responseType: 'blob' });
  }
}
