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 { Resultat, ResultatApiResponse, CreateResultatPayload } from '../interfaces/resultat';
import { Course } from '../interfaces/course';
import { CourseService } from './course';
import { environment } from 'src/environments/environment.development';

const USE_SERVER = true;
const API_BASE = '/api/v1/resultat';

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

  constructor(private http: HttpClient, private courseService: CourseService) {}

  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' } : {};
  }

  // GET /api/v1/resultat/{id}
  getById(id: string): Observable<Resultat | undefined> {
    if (USE_SERVER) {
      return this.http
        .get<ResultatApiResponse>(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() })
        .pipe(
          switchMap((apiResultat) => {
            // Fetch the full course object if course is just an ID
            const courseId =
              typeof apiResultat.course === 'object' && 'id' in apiResultat.course
                ? String(apiResultat.course.id)
                : String(apiResultat.course);

            return this.courseService.getById(courseId).pipe(
              map((course) => {
                if (!course) {
                  return undefined;
                }
                return this.transformApiResponse(apiResultat, course);
              })
            );
          }),
          catchError((err) => {
            console.error(`Error fetching resultat ${id}:`, err);
            return of(undefined);
          })
        );
    }
    return of(undefined);
  }

  // GET /api/v1/resultat
  list(): Observable<Resultat[]> {
    if (USE_SERVER) {
      return this.http
        .get<ResultatApiResponse[]>(this.apiUrl, { headers: this.getNgrokHeaders() })
        .pipe(
          switchMap((apiResultats) => {
            // Fetch all unique course IDs
            const courseIds = [
              ...new Set(
                apiResultats.map((r) =>
                  typeof r.course === 'object' && 'id' in r.course
                    ? String(r.course.id)
                    : String(r.course)
                )
              ),
            ];
            // Fetch all courses in parallel
            const courseRequests = courseIds.map((id) =>
              this.courseService
                .getById(id)
                .pipe(catchError(() => of<Course | undefined>(undefined)))
            );

            return forkJoin(courseRequests).pipe(
              map((courses) => {
                const courseMap = new Map<string, Course>();
                courseIds.forEach((id, index) => {
                  const course = courses[index];
                  if (course) {
                    courseMap.set(id, course);
                  }
                });

                return apiResultats
                  .map((apiResultat) => {
                    const courseId =
                      typeof apiResultat.course === 'object' && 'id' in apiResultat.course
                        ? String(apiResultat.course.id)
                        : String(apiResultat.course);
                    const course = courseMap.get(courseId);
                    if (!course) {
                      return null;
                    }
                    return this.transformApiResponse(apiResultat, course);
                  })
                  .filter((r): r is Resultat => r !== null);
              }),
              catchError((err) => {
                console.error('Error fetching resultats:', err);
                return of([]);
              })
            );
          }),
          catchError((err) => {
            console.error('Error fetching resultats:', err);
            return of([]);
          })
        );
    }
    return of([]);
  }

  // GET /api/v1/resultat/course/{courseId}
  getByCourseId(courseId: string): Observable<Resultat | undefined> {
    if (!USE_SERVER) {
      return of(undefined);
    }

    return this.http
      .get<any>(`${this.apiUrl}/course/${courseId}`, {
        headers: this.getNgrokHeaders(),
      })
      .pipe(
        switchMap((raw) => {
          // Some courses don't have a resultat yet.
          // In that case the API returns 200 with a body like:
          // { "message": "Aucun résultat disponible pour cette course" }
          // We interpret this as "no resultat" and return undefined.
          if (
            raw &&
            typeof raw === 'object' &&
            'message' in raw &&
            !('id' in raw) &&
            !('ordreArrivee' in raw)
          ) {
            return of<Resultat | undefined>(undefined);
          }

          const apiResultat = raw as ResultatApiResponse;

          return this.courseService.getById(courseId).pipe(
            map((course) => {
              if (!course) {
                return undefined;
              }
              return this.transformApiResponse(apiResultat, course);
            })
          );
        }),
        catchError((err) => {
          // If the backend ever responds with 404 here, also treat as "no resultat".
          if (err?.status === 404) {
            return of(undefined);
          }

          console.error(`Error fetching resultat for course ${courseId}:`, err);
          return of(undefined);
        })
      );
  }

  // POST /api/v1/resultat
  create(payload: CreateResultatPayload): Observable<Resultat> {
    if (USE_SERVER) {
      return this.http
        .post<ResultatApiResponse>(this.apiUrl, payload, { headers: this.getNgrokHeaders() })
        .pipe(
          switchMap((apiResultat) => {
            const courseId = String(payload.course.id);
            return this.courseService.getById(courseId).pipe(
              map((course) => {
                if (!course) {
                  throw new Error('Course not found');
                }
                return this.transformApiResponse(apiResultat, course);
              })
            );
          }),
          catchError((err) => {
            console.error('Error creating resultat:', err);
            throw err;
          })
        );
    }
    throw new Error('Server mode is required');
  }

  // PUT /api/v1/resultat/{id}
  update(id: string, payload: Partial<CreateResultatPayload>): Observable<Resultat | undefined> {
    if (USE_SERVER) {
      return this.http
        .put<ResultatApiResponse>(`${this.apiUrl}/${id}`, payload, {
          headers: this.getNgrokHeaders(),
        })
        .pipe(
          switchMap((apiResultat) => {
            const courseId =
              typeof apiResultat.course === 'object' && 'id' in apiResultat.course
                ? String(apiResultat.course.id)
                : String(apiResultat.course);

            return this.courseService.getById(courseId).pipe(
              map((course) => {
                if (!course) {
                  return undefined;
                }
                return this.transformApiResponse(apiResultat, course);
              })
            );
          }),
          catchError((err) => {
            console.error(`Error updating resultat ${id}:`, err);
            return of(undefined);
          })
        );
    }
    throw new Error('Server mode is required');
  }

  // DELETE /api/v1/resultat/{id}
  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 resultat ${id}:`, err);
            return of(false);
          })
        );
    }
    throw new Error('Server mode is required');
  }

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

  private transformApiResponse(apiResultat: ResultatApiResponse, course: Course): Resultat {
    return {
      id: String(apiResultat.id),
      course,
      // Normalize ordreArrivee to an array of cheval numbers
      ordreArrivee: (apiResultat.ordreArrivee || [])
        .map((v) => (typeof v === 'string' ? Number(v) : v))
        .filter((v): v is number => typeof v === 'number' && !Number.isNaN(v)),
      // Normalize dead-heat horses to numbers as well
      chevauxDeadHeat: (apiResultat.chevauxDeadHeat || [])
        .map((v) => (typeof v === 'string' ? Number(v) : v))
        .filter((v): v is number => typeof v === 'number' && !Number.isNaN(v)),
      totalMises: apiResultat.totalMises,
      masseAPartager: apiResultat.masseAPartager,
      prelevementsLegaux: apiResultat.prelevementsLegaux,
      montantRembourse: apiResultat.montantRembourse,
      montantCagnotte: apiResultat.montantCagnotte,
      adeadHeat: apiResultat.adeadHeat,
      createdAt: apiResultat.createdAt,
      updatedAt: apiResultat.updatedAt,
    };
  }
}
