import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, of, forkJoin } from 'rxjs';
import { map, catchError, switchMap } from 'rxjs/operators';
import { Course, CourseType, CourseStatut } from '../interfaces/course';
import { normalizePage } from '@shared/paging/normalize-page';
import { PaginatedHttpService } from '@shared/paging/paginated-http.service';
import { ListParams, PagedResult } from '@shared/paging/paging';
import { environment } from 'src/environments/environment.development';
import { Reunion } from '../interfaces/reunion';
import { ReunionService } from './reunion';
import { NonPartantService } from './non-partant';
import { ServicesUtils } from './services-utils';
import { HippodromeService } from './hippodrome';

const USE_SERVER = true;
const API_BASE = '/api/courses';
// Interface to match the API response structure for Course
export interface CourseApiResponse {
  id: string;
  hippodromeId: number;
  reunionNumero: number;
  reunionDate: string;
  nom: string;
  numero: number;
  heureDepartPrevue: string;
  discipline: string;
  distanceMetres: number;
  categorie: string;
  nombrePartants: number;
  statut: string;
  annulee: boolean;
  reporteeMemeJour: boolean;
  reporteeAutreJour: boolean;
  incidentTechnique: boolean;
  nonPartants: Array<unknown>;
  typesParisOuverts: Array<string>;
}

export interface NonApiRequest {
  nonPartants: String[]
}

@Injectable({ providedIn: 'root' })
export class CourseService {
  private apiUrl = environment.apiBaseUrl + API_BASE;

  constructor(
    private http: HttpClient,
    private paginatedHttp: PaginatedHttpService,
    private servivesUtil: ServicesUtils,
    private hippodromeService: HippodromeService,
    private reunionService: ReunionService, // Inject ReunionService
    private nonPartantService: NonPartantService // Inject NonPartantService
  ) {}

  // Helper method to get ngrok bypass headers
  private getNgrokHeaders(): Record<string, string> {
    const isNgrok =
      environment.apiBaseUrl.includes('ngrok-free.app') ||
      environment.apiBaseUrl.includes('ngrok.io') ||
      environment.apiBaseUrl.includes('ngrok');
    return isNgrok ? { 'ngrok-skip-browser-warning': 'true' } : {};
  }

  list(params: ListParams): Observable<PagedResult<Course>> {
    const coursesList = this.http.get<PagedResult<CourseApiResponse>>(this.apiUrl, {
      headers: this.getNgrokHeaders(),
      params: this.servivesUtil.getParamsFromModel(params),
    });
    return coursesList.pipe(
      switchMap((res) => {
        const items = res.content ?? [];

        // Transforme chaque course en Observable<Course>
        const courseObservables = items.map((apiCourse) =>
          this.hippodromeService.getById(String(apiCourse.hippodromeId)).pipe(
            // Map le hippodrome récupéré dans l'objet Course final
            map((hippodrome) => ({
              id: apiCourse.id,
              hippodrome: hippodrome ?? undefined,
              reunionNumero: apiCourse.reunionNumero,
              reunionDate: apiCourse.reunionDate,
              nom: apiCourse.nom,
              numero: apiCourse.numero,
              heureDepartPrevue: apiCourse.heureDepartPrevue,
              discipline: apiCourse.discipline,
              distanceMetres: apiCourse.distanceMetres,
              categorie: apiCourse.categorie,
              nombrePartants: apiCourse.nombrePartants,
              statut: apiCourse.statut,
              annulee: apiCourse.annulee,
              reporteeMemeJour: apiCourse.reporteeMemeJour,
              reporteeAutreJour: apiCourse.reporteeAutreJour,
              incidentTechnique: apiCourse.incidentTechnique,
              nonPartants: apiCourse.nonPartants || [],
              typesParisOuverts: apiCourse.typesParisOuverts || [],
            }))
          )
        );

        // ForkJoin pour attendre que tous les hippodromes soient résolus
        return forkJoin(courseObservables).pipe(
          map((courses) => {
            const mapped: PagedResult<Course> = {
              pageable: res.pageable,
              totalPages: res.totalPages,
              totalElements: res.totalElements,
              content: courses,
            };
            return mapped;
          })
        );
      }),
      catchError((err) => {
        console.error('Error fetching courses list:', err);
        return of({ content: [], pageable: {pageNumber: 1, pageSize: 0, total: 0}, totalPages:1, totalElements:0 } as PagedResult<Course>);
      })
    );
  }

  private applyClientFilters(data: Course[], params: ListParams): Course[] {
    let filtered = [...data];

    // Search filter
    // const q = (params.search ?? '').toLowerCase();
    // if (q) {
    //   filtered = filtered.filter((c) => {
    //     const reunionName = c.reunion?.nom?.toLowerCase?.() ?? '';
    //     const hippodromeName = c.reunion?.hippodrome?.nom?.toLowerCase?.() ?? '';
    //     return (
    //       c.nom.toLowerCase().includes(q) ||
    //       c.type.toLowerCase().includes(q) ||
    //       reunionName.includes(q) ||
    //       hippodromeName.includes(q)
    //     );
    //   });
    // }

    // Sort
    if (params.sortKey && params.sortDir) {
      const { sortKey, sortDir } = params;
      filtered.sort((a: any, b: any) => {
        const getValue = (obj: any, path: string): any =>
          path.split('.').reduce((o, key) => o?.[key], obj);

        const va = getValue(a, sortKey);
        const vb = getValue(b, sortKey);
        const sa = va == null ? '' : String(va);
        const sb = vb == null ? '' : String(vb);
        const cmp = sa.localeCompare(sb, 'fr', { numeric: true });
        return sortDir === 'asc' ? cmp : -cmp;
      });
    }

    return filtered;
  }

  getById(id: string): Observable<Course | undefined> {
      return this.http
        .get<CourseApiResponse>(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() })
        .pipe(
          switchMap((apiCourse) => {
            // Fetch the reunion (non-partants are already included in the API response)
            return this.hippodromeService.getById(String(apiCourse.hippodromeId)).pipe(
              map((hippodrome) => {
                return {
                  id: String(apiCourse.id),
                  hippodrome: hippodrome ?? undefined,
                  reunionNumero: apiCourse.reunionNumero,
                  reunionDate: apiCourse.reunionDate,
                  nom: apiCourse.nom,
                  numero: apiCourse.numero,
                  heureDepartPrevue: apiCourse.heureDepartPrevue,
                  discipline: apiCourse.discipline,
                  distanceMetres: apiCourse.distanceMetres,
                  categorie: apiCourse.categorie,
                  nombrePartants: apiCourse.nombrePartants,
                  statut: apiCourse.statut,
                  annulee: apiCourse.annulee,
                  reporteeMemeJour: apiCourse.reporteeMemeJour,
                  reporteeAutreJour: apiCourse.reporteeAutreJour,
                  incidentTechnique: apiCourse.incidentTechnique,
                  nonPartants: apiCourse.nonPartants || [],
                  typesParisOuverts: apiCourse.typesParisOuverts || []
                };
              })
            );
          }),
          catchError((err) => {
            console.error(`Error fetching course ${id}:`, err);
            return of(undefined);
          })
        );
  }

  // getByReunionId(reunionId: string): Observable<Course[]> {
  //   if (USE_SERVER) {
  //     return this.http
  //       .get<CourseApiResponse[]>(`${this.apiUrl}/reunion/${reunionId}`, {
  //         headers: this.getNgrokHeaders(),
  //       })
  //       .pipe(
  //         switchMap((apiData) => {
  //           // Fetch the reunion once
  //           return this.reunionService.getById(reunionId).pipe(
  //             map((reunion) => {
  //               if (!reunion) {
  //                 return [];
  //               }
  //               // Transform all courses with the same reunion
  //               return apiData.map((apiCourse) => ({
  //                 id: String(apiCourse.id),
  //                 type: apiCourse.type,
  //                 numero: apiCourse.numero,
  //                 nom: apiCourse.nom,
  //                 dateDepartCourse: apiCourse.dateDepartCourse,
  //                 dateDebutParis: apiCourse.dateDebutParis,
  //                 dateFinParis: apiCourse.dateFinParis,
  //                 reunion,
  //                 reunionCourse: apiCourse.reunionCourse,
  //                 particularite: apiCourse.particularite,
  //                 partants: apiCourse.partants,
  //                 distance: apiCourse.distance,
  //                 condition: apiCourse.condition,
  //                 statut: apiCourse.statut as CourseStatut,
  //                 nonPartants: apiCourse.nonPartants || [],
  //                 estTerminee: apiCourse.estTerminee,
  //                 estAnnulee: apiCourse.estAnnulee,
  //                 nombreChevauxInscrits: apiCourse.nombreChevauxInscrits,
  //                 adeadHeat: apiCourse.adeadHeat,
  //                 createdBy: apiCourse.createdBy,
  //                 validatedBy: apiCourse.validatedBy,
  //                 createdAt: apiCourse.createdAt || new Date().toISOString(),
  //                 updatedAt: apiCourse.updatedAt || new Date().toISOString(),
  //               }));
  //             })
  //           );
  //         }),
  //         catchError((err) => {
  //           console.error(`Error fetching courses for reunion ${reunionId}:`, err);
  //           return of([]);
  //         })
  //       );
  //   }
  //   return of([]);
  // }

  // search(query: string): Observable<Course[]> {
  //   if (USE_SERVER) {
  //     return this.http
  //       .get<CourseApiResponse[]>(`${this.apiUrl}/search`, {
  //         params: { q: query.trim() },
  //         headers: this.getNgrokHeaders(),
  //       })
  //       .pipe(
  //         switchMap((apiData) => {
  //           // Extract unique reunionIds
  //           const uniqueReunionIds = [...new Set(apiData.map((c) => String(c.reunionId)))];

  //           // Fetch all reunions in parallel
  //           const reunionRequests = uniqueReunionIds.map((id) =>
  //             this.reunionService
  //               .getById(id)
  //               .pipe(catchError(() => of<Reunion | undefined>(undefined)))
  //           );

  //           return forkJoin(reunionRequests).pipe(
  //             map((reunions) => {
  //               // Create a map of reunionId -> Reunion
  //               const reunionMap = new Map<string, Reunion>();
  //               uniqueReunionIds.forEach((id, index) => {
  //                 const reunion = reunions[index];
  //                 if (reunion) {
  //                   reunionMap.set(id, reunion);
  //                 }
  //               });

  //               // Transform API data to Course objects
  //               return apiData
  //                 .map((apiCourse) => {
  //                   const reunion = reunionMap.get(String(apiCourse.reunionId));
  //                   if (!reunion) {
  //                     return null;
  //                   }
  //                   return {
  //                     id: String(apiCourse.id),
  //                     type: apiCourse.type,
  //                     numero: apiCourse.numero,
  //                     nom: apiCourse.nom,
  //                     dateDepartCourse: apiCourse.dateDepartCourse,
  //                     dateDebutParis: apiCourse.dateDebutParis,
  //                     dateFinParis: apiCourse.dateFinParis,
  //                     reunion,
  //                     reunionCourse: apiCourse.reunionCourse,
  //                     particularite: apiCourse.particularite,
  //                     partants: apiCourse.partants,
  //                     distance: apiCourse.distance,
  //                     condition: apiCourse.condition,
  //                     statut: apiCourse.statut as CourseStatut,
  //                     nonPartants: apiCourse.nonPartants || [],
  //                     estTerminee: apiCourse.estTerminee,
  //                     estAnnulee: apiCourse.estAnnulee,
  //                     nombreChevauxInscrits: apiCourse.nombreChevauxInscrits,
  //                     adeadHeat: apiCourse.adeadHeat,
  //                     createdBy: apiCourse.createdBy,
  //                     validatedBy: apiCourse.validatedBy,
  //                     createdAt: apiCourse.createdAt || new Date().toISOString(),
  //                     updatedAt: apiCourse.updatedAt || new Date().toISOString(),
  //                   } as Course;
  //                 })
  //                 .filter((c): c is Course => c !== null);
  //             })
  //           );
  //         }),
  //         catchError((err) => {
  //           console.error(`Error searching courses with query ${query}:`, err);
  //           return of([]);
  //         })
  //       );
  //   }
  //   return of([]);
  // }

  create(payload: Partial<CourseApiResponse>): Observable<Course> {
    if (USE_SERVER) {
      // Transform payload to API format (send reunionId instead of reunion object)
      const apiPayload: Partial<CourseApiResponse> = payload;

      return this.http
        .post<CourseApiResponse>(this.apiUrl, apiPayload, { headers: this.getNgrokHeaders() })
        .pipe(
          switchMap((apiCourse) => {
            // Fetch the reunion to build the full Course object
            return this.hippodromeService.getById(String(apiCourse.hippodromeId)).pipe(
              map((hippodrome) => {
                if (!hippodrome) {
                  throw new Error('Hippodrome not found');
                }
                const item: Course = {
                  id: String(apiCourse.id),
                  hippodrome: hippodrome ?? undefined,
                  reunionNumero: apiCourse.reunionNumero,
                  reunionDate: apiCourse.reunionDate,
                  nom: apiCourse.nom,
                  numero: apiCourse.numero,
                  heureDepartPrevue: apiCourse.heureDepartPrevue,
                  discipline: apiCourse.discipline,
                  distanceMetres: apiCourse.distanceMetres,
                  categorie: apiCourse.categorie,
                  nombrePartants: apiCourse.nombrePartants,
                  statut: apiCourse.statut,
                  annulee: apiCourse.annulee,
                  reporteeMemeJour: apiCourse.reporteeMemeJour,
                  reporteeAutreJour: apiCourse.reporteeAutreJour,
                  incidentTechnique: apiCourse.incidentTechnique,
                  nonPartants: apiCourse.nonPartants || [],
                  typesParisOuverts: apiCourse.typesParisOuverts || []
                };
                return item;
              })
            );
          }),
          catchError((err) => {
            console.error('Error creating course:', err);
            throw err;
          })
        );
    }
    throw new Error('Server mode is required');
  }

  update(id: string, payload: Partial<CourseApiResponse>): Observable<Course | undefined> {
    if (USE_SERVER) {
      // Transform payload to API format (send reunionId instead of reunion object

      return this.http
        .put<CourseApiResponse>(`${this.apiUrl}/${id}`, payload, {
          headers: this.getNgrokHeaders(),
        })
        .pipe(
          switchMap((apiCourse) => {
            // Fetch the reunion to build the full Course object
            return this.hippodromeService.getById(String(apiCourse.hippodromeId)).pipe(
              map((hippodrome) => {
                if (!hippodrome) {
                  throw new Error('Reunion not found');
                }
                return {
                  id: String(apiCourse.id),
                  hippodrome: hippodrome ?? undefined,
                  reunionNumero: apiCourse.reunionNumero,
                  reunionDate: apiCourse.reunionDate,
                  nom: apiCourse.nom,
                  numero: apiCourse.numero,
                  heureDepartPrevue: apiCourse.heureDepartPrevue,
                  discipline: apiCourse.discipline,
                  distanceMetres: apiCourse.distanceMetres,
                  categorie: apiCourse.categorie,
                  nombrePartants: apiCourse.nombrePartants,
                  statut: apiCourse.statut,
                  annulee: apiCourse.annulee,
                  reporteeMemeJour: apiCourse.reporteeMemeJour,
                  reporteeAutreJour: apiCourse.reporteeAutreJour,
                  incidentTechnique: apiCourse.incidentTechnique,
                  nonPartants: apiCourse.nonPartants || [],
                  typesParisOuverts: apiCourse.typesParisOuverts || []
                };
              })
            );
          }),
          catchError((err) => {
            console.error(`Error updating course ${id}:`, err);
            return of(undefined);
          })
        );
    }
    throw new Error('Server mode is required');
  }

  updateStatut(id: string, statut: CourseStatut): Observable<Course | undefined> {
    if (USE_SERVER) {
      return this.http
        .patch<Course>(
          `${this.apiUrl}/${id}/statut`,
          { statut },
          { headers: this.getNgrokHeaders() }
        )
        .pipe(
          catchError((err) => {
            console.error(`Error updating course statut ${id}:`, err);
            return this.update(id, { statut });
          })
        );
    }
    return this.update(id, { statut });
  }

  delete(id: string): Observable<boolean> {
    if (USE_SERVER) {
      return this.http
        .delete<void>(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() })
        .pipe(
          map(() => true),
          catchError((err) => {
            console.error(`Error deleting course ${id}:`, err);
            return of(false);
          })
        );
    }
    throw new Error('Server mode is required');
  }

  addNonPartant(courseId: string, npList: string[]) {
    const payload = {
      nonPartants: [...npList]
    }
    console.warn('addNonPartant is deprecated. Use setNonPartants instead.');
    return this.setNonPartants(courseId, payload);
  }

  setNonPartants(courseId: string, npList: NonApiRequest): Observable<CourseApiResponse | undefined> {
    return this.nonPartantService.replaceNonPartants(courseId, npList).pipe(
      map((updatedNonPartants) => updatedNonPartants),
      catchError((err) => {
        console.error(`Error setting nonPartants for course ${courseId}:`, err);
        return of(undefined);
      })
    );
  }
}
