import { CommonModule } from '@angular/common';
import {
  ChangeDetectionStrategy,
  Component,
  computed,
  effect,
  signal,
  ViewChild,
  untracked,
} from '@angular/core';
import { DataTable, SortState, TableColumn } from '@shared/components/data-table/data-table';
import { Paginator } from '@shared/components/paginator/paginator';
import { SearchBar } from '@shared/components/search-bar/search-bar';
import { Modal } from '@shared/components/modal/modal';
import { ZardCardComponent } from '@shared/components/card/card.component';
import { ZardButtonComponent } from '@shared/components/button/button.component';
import { CourseStatut, Course as CourseType } from 'src/app/core/interfaces/course';
import { SortDir } from '@shared/paging/paging';
import { CourseApiResponse, CourseService, NonApiRequest } from 'src/app/core/services/course';
import { ResultatService } from 'src/app/core/services/resultat';
import { Resultat, ResultatStatut } from 'src/app/core/interfaces/resultat';
import { A11yModule } from '@angular/cdk/a11y';
import { CourseForm } from '@shared/forms/course-form/course-form';
import { NonPartantForm } from '@shared/forms/nonpartant-form/nonpartant-form';
import { LucideAngularModule } from 'lucide-angular';
import { ResultatForm } from '@shared/forms/resultat-form/resultat-form';
import { toast } from 'ngx-sonner';
import { forkJoin, of } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Component({
  standalone: true,
  selector: 'app-course-list',
  imports: [
    CommonModule,
    DataTable,
    Paginator,
    SearchBar,
    Modal,
    CourseForm,
    NonPartantForm,
    ResultatForm,
    ZardCardComponent,
    ZardButtonComponent,
    A11yModule,
    LucideAngularModule,
  ],
  templateUrl: './courses.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class Course {
  rows = signal<CourseType[]>([]);
  resultatsMap = signal<Map<string, Resultat>>(new Map());
  loading = signal(false);
  total = signal(0);
  totalRunning = signal(0);
  totalClosed = signal(0);
  totalByType = signal<Record<string, number>>({});

  page = signal(0);
  perPage = signal(10);
  search = signal('');
  sort = signal<SortState>({ key: 'id', dir: 'asc' });
  pageSize = [10, 20, 50];

  modalOpen = signal(false);
  modalTitle = signal('Nouvelle course');
  editingItem = signal<CourseType | null>(null);

  @ViewChild(CourseForm) formComp?: CourseForm;

  // 🟩 Corrected columns
  cols: TableColumn<CourseType>[] = [
    { key: 'numero', label: 'N°', sortable: true },
    { key: 'nom', label: 'Nom', sortable: true },
    {
      key: 'type',
      label: 'Type',
      sortable: true,
      cell: (c) => `<span class="font-medium">${c.discipline}</span>`,
    },
    {
      key: 'dateDepartCourse',
      label: 'Date et Heure Départ',
      sortable: true,
      cell: (c) => c.heureDepartPrevue
    },
    {
      key: 'partants',
      label: 'Partants',
      cell: (c) =>
        `<span>${c.nombrePartants}</span> <span class="text-xs text-red-500">(${
          c.nonPartants?.length ?? 0
        } NP)</span>`,
    },
    {
      key: 'resultat',
      label: 'Résultat',
      cell: (c) => {
        const resultat = this.resultatsMap().get(c.id);
        if (!resultat || !resultat.ordreArrivee || resultat.ordreArrivee.length === 0) {
          return '<span class="text-gray-500 dark:text-gray-400">—</span>';
        }

        return `<span class="text-gray-500 dark:text-gray-400">${resultat.ordreArrivee}</span>`

        // // Group horses that are at the same place (ex-aequo/dead heat).
        // // Backend/Resultat model store ordreArrivee as cheval numbers (1,2,3,...) and
        // // chevauxDeadHeat as the subset that are ex-aequo.
        // const deadHeatSet = new Set(resultat.chevauxDeadHeat || []);

        // const groups: number[][] = [];
        // let currentGroup: number[] = [];

        // resultat.ordreArrivee.forEach((num, index) => {
        //   const isInDeadHeat = deadHeatSet.has(num);
        //   const prevNum = index > 0 ? resultat.ordreArrivee[index - 1] : null;
        //   const prevIsInDeadHeat = prevNum !== null && deadHeatSet.has(prevNum);

        //   if (isInDeadHeat && prevIsInDeadHeat && currentGroup.length > 0) {
        //     // Continue the current dead heat group
        //     currentGroup.push(num);
        //   } else {
        //     // Start a new group
        //     if (currentGroup.length > 0) {
        //       groups.push(currentGroup);
        //     }
        //     currentGroup = [num];
        //   }
        // });

        // // Don't forget the last group
        // if (currentGroup.length > 0) {
        //   groups.push(currentGroup);
        // }

        // const s = groups.map((nums) => nums.join('=')).join(' - ');

        // // For now, we'll show the resultat. In the future, we might add a statut field to Resultat
        // return ;
      },
    },
    {
      key: 'statut',
      label: 'Statut',
      sortable: true,
      cell: (c) => {
        const colorMap: Record<string, string> = {
          OUVERT: 'bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-300',
          RESULTAT_PROVISOIRE: 'bg-cyan-100 text-purple-700 dark:bg-cyan-900/30 dark:text-cyan-300',
          RESULTAT_OFFICIEL: 'bg-amber-100 text-purple-700 dark:bg-amber-900/30 dark:text-amber-300',
          BROUILLON: 'bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-300',
          VALIDE: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300',
          REGLEE: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300',
          FERME: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300',
          ANNULEE: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300',
        };
        const labelMap: Record<string, string> = {
          OUVERT: 'Ouvert',
          RESULTAT_PROVISOIRE: 'Résultat provisoire',
          RESULTAT_OFFICIEL: 'Résultat officiel',
          BROUILLON: 'Brouillon',
          VALIDE: 'Validé',
          REGLEE: 'Réglée',
          FERME: 'Fermé',
          ANNULEE: 'Annulée'
        };
        return `<span class="px-2 py-1 rounded-full text-xs font-semibold ${colorMap[c.statut]}">${
          labelMap[c.statut]
        }</span>`;
      },
    },
    {
      key: 'reunion.hippodrome.nom',
      label: 'Hippodrome',
      cell: (c) => (c?.hippodrome ? `${c.hippodrome.nom}` : '—'),
    },
    {
      key: 'distance',
      label: 'Distance (m)',
      sortable: true,
      cell: (c) => c.distanceMetres.toLocaleString('fr-FR'),
    },
  ];

  visibleKeys = signal<string[]>([]);

  constructor(private api: CourseService, private resultatService: ResultatService) {
    effect(() => {
      const params = {
        page: this.page(),
        size: this.perPage(),
        search: this.search(),
        sortKey: this.sort().key,
        sortDir: this.sort().dir as SortDir,
      };
      untracked(() => this.fetch(params));
    });
  }

  private fetch(params: {
    page: number;
    size: number;
    search: string;
    sortKey: string;
    sortDir: SortDir;
  }) {
    this.loading.set(true);
    this.api.list(params).subscribe({
      next: (res) => {
        this.rows.set(res.content);
        this.total.set(res.totalElements);
        this.totalRunning.set(res.content.filter(c=> c.statut === String(CourseStatut.OUVERT)).length);
        this.totalClosed.set(res.content.filter(c=>c.statut === String(CourseStatut.FERME)).length);
        this.totalByType.set({
          
        });

        // Fetch resultats for all courses in parallel
        const courseIds = res.content.map((c) => c.id);
        if (courseIds.length > 0) {
          const resultatRequests = courseIds.map((id) =>
            this.resultatService.getByCourseId(id).pipe(catchError(() => of(undefined)))
          );

          forkJoin(resultatRequests).subscribe({
            next: (resultats) => {
              const resultatsMap = new Map<string, Resultat>();
              courseIds.forEach((id, index) => {
                const resultat = resultats[index];
                if (resultat) {
                  resultatsMap.set(id, resultat);
                }
              });
              this.resultatsMap.set(resultatsMap);
              this.loading.set(false);
            },
            error: () => {
              this.resultatsMap.set(new Map());
              this.loading.set(false);
            },
          });
        } else {
          this.resultatsMap.set(new Map());
          this.loading.set(false);
        }
      },
      error: () => {
        this.rows.set([]);
        this.total.set(0);
        this.totalRunning.set(0);
        this.totalClosed.set(0);
        this.totalByType.set({});
        this.resultatsMap.set(new Map());
        this.loading.set(false);
      },
    });
  }

  // === UI Actions ===
  onSearch(q: string) {
    this.search.set(q);
    this.page.set(0);
  }

  openCreate() {
    this.modalTitle.set('Nouvelle course');
    this.editingItem.set(null);
    queueMicrotask(() => this.modalOpen.set(true));
  }

  isClosed = (c: CourseType | null | undefined) =>
    c?.statut === 'CLOSED' || c?.statut === 'CANCELED';

  openEdit(row: CourseType) {
    if (this.isClosed(row)) return;
    this.modalTitle.set('Modifier la course');
    this.editingItem.set(row);
    queueMicrotask(() => this.modalOpen.set(true));
  }

  closeModal() {
    this.modalOpen.set(false);
  }

  submitChildForm() {
    this.formComp?.onSubmit();
  }

  onFormSave(_: CourseType) {
    // The form now persists create/update itself. Just close and refresh.
    this.closeModal();
    this.fetch({
      page: this.page(),
      size: this.perPage(),
      search: this.search(),
      sortKey: this.sort().key,
      sortDir: this.sort().dir,
    });
  }

  remove(row: CourseType) {
    if (this.isClosed(row)) return;
    if (!confirm(`Supprimer la course « ${row.nom} » ?`)) return;
    this.api.delete(row.id).subscribe(() =>
      this.fetch({
        page: this.page(),
        size: this.perPage(),
        search: this.search(),
        sortKey: this.sort().key,
        sortDir: this.sort().dir,
      })
    );
  }

  // === Stats Computed ===
  totalCourses = computed(() => this.total());
  runningCourses = computed(() => this.totalRunning());
  closedCourses = computed(() => this.totalClosed());
  byType = computed(() => this.totalByType());

  nonPartantModalOpen = signal(false);
  selectedCourse = signal<CourseType | null>(null);
  @ViewChild(NonPartantForm) npForm?: NonPartantForm;

  openResultat(row: CourseType) {
    if (this.isClosed(row)) return;
    this.selectedCourseForResultat.set(row);
    this.resultatModalOpen.set(true);
  }

  openNonPartant(row: CourseType) {
    if (this.isClosed(row)) return;
    this.selectedCourse.set(row);
    this.nonPartantModalOpen.set(true);
  }

  closeNonPartantModal() {
    this.nonPartantModalOpen.set(false);
    this.selectedCourse.set(null);
  }

  submitNonPartant() {
    this.npForm?.onSubmit();
  }

  onNonPartantSave(payload: string[]) {
    const course = this.selectedCourse();
    if (!course) return;
    const reqPayload = {
      nonPartants: [
        ...payload
      ]
    }

    this.api.setNonPartants(course.id, reqPayload).subscribe({
      next: (updatedCourse) => {
        if (updatedCourse) {
          toast.success('Non-partants mis à jour avec succès');
        } else {
          toast.error('Erreur lors de la mise à jour des non-partants');
        }
        this.closeNonPartantModal();
        this.fetch({
          page: this.page(),
          size: this.perPage(),
          search: this.search(),
          sortKey: this.sort().key,
          sortDir: this.sort().dir,
        });
      },
      error: (err) => {
        console.error('Error saving non-partants:', err);
        toast.error('Erreur lors de la mise à jour des non-partants');
      },
    });
  }

  resultatModalOpen = signal(false);
  selectedCourseForResultat = signal<CourseType | null>(null);

  closeResultatModal() {
    this.resultatModalOpen.set(false);
    this.selectedCourseForResultat.set(null);
  }

  onResultatSave(places: number[][], typesParisOuverts: string[]) {
    const c = this.selectedCourseForResultat();
    if (!c) return;

    // Determine required number of horses based on course type
    const getRequiredHorses = (types: string[]): number => {
      const typeStr = types;
      if (typeStr.includes('PLACE')  || typeStr.includes('GAGNANT')) return 3;
      if(typeStr.includes('JUMELE_GAGNANT') || typeStr.includes('JUMELE_PLACE') || typeStr.includes('JUMELE_ORDRE')) return 2;
      if(typeStr.includes('TRIO') || typeStr.includes('TRIO_ORDRE') || typeStr.includes('TRIPLET')) return 3
      if (typeStr.includes('QUATRO')) return 4;
      if (typeStr.includes('QUINTE')) return 5;
      return 3; // Default
    };

    const requiredHorses = getRequiredHorses(typesParisOuverts);

    // Collect all selected horses (flatten the places array)
    const allHorses: number[] = places
      .flatMap((placeGroup) => placeGroup.filter((n) => typeof n === 'number' && n > 0))
      .slice(0, requiredHorses); // Only take the first N horses

    // Check if all horses are in first place (ex-aequo)
    const firstPlaceHorses = places[0]?.filter((n) => typeof n === 'number' && n > 0) || [];
    const isAllExAequo =
      firstPlaceHorses.length === requiredHorses && allHorses.length === requiredHorses;

    // Convert to ordreArrivee format
    // If all are ex-aequo, they all go in ordreArrivee as they are (first place)
    // Otherwise, distribute them across places
    let ordreArrivee: string = '';

    places.forEach((place)=>{
      if(Array.isArray(place) && place.length>1){
       place.forEach((p, index)=>{
        if(index == 0){
          ordreArrivee = ordreArrivee ==''? String(p) : ordreArrivee+","+String(p)  
        }else{
          ordreArrivee = ordreArrivee ==''? String(p) : ordreArrivee+"="+String(p)
        }
       })
      }else{
        ordreArrivee = ordreArrivee ==''? String(place[0]): ordreArrivee+","+String(place[0])
      }
    })
    // Check if resultat already exists
    const existingResultat = this.resultatsMap().get(c.id);

    const payload = {
      courseId: Number(c.id) ,
      ordreArrivee,
      statut: ResultatStatut.EN_ATTENTE,
    };

    const request$ = existingResultat
      ? this.resultatService.update(existingResultat.id, payload)
      : this.resultatService.create(payload);

    request$.subscribe({
      next: () => {
        this.closeResultatModal();
        this.fetch({
          page: this.page(),
          size: this.perPage(),
          search: this.search(),
          sortKey: this.sort().key,
          sortDir: this.sort().dir,
        });
        toast.success('Résultat enregistré avec succès');
      },
      error: (err) => {
        console.error('Error saving resultat:', err);
        toast.error("Erreur lors de l'enregistrement du résultat");
      },
    });
  }

  onResultatValidate() {
    const c = this.selectedCourseForResultat();
    if (!c) return;

    const resultat = this.resultatsMap().get(c.id);
    if (!resultat) {
      toast.error('Aucun résultat à valider');
      return;
    }

    // For now, validation is just an update. In the future, you might add a statut field
    this.resultatService.update(resultat.id, {
      ...resultat,
      statut: ResultatStatut.PROVISOIRE
    }).subscribe({
      next: () => {
        this.closeResultatModal();
        this.fetch({
          page: this.page(),
          size: this.perPage(),
          search: this.search(),
          sortKey: this.sort().key,
          sortDir: this.sort().dir,
        });
        toast.success('Résultat validé avec succès');
      },
      error: (err) => {
        console.error('Error validating resultat:', err);
        toast.error('Erreur lors de la validation du résultat');
      },
    });
  }

  onResultatConfirm() {
    const c = this.selectedCourseForResultat();
    if (!c) return;

    const resultat = this.resultatsMap().get(c.id);
    if (!resultat) {
      toast.error('Aucun résultat à confirmer');
      return;
    }

    // For now, confirmation is just an update. In the future, you might add a statut field
    this.resultatService.update(resultat.id, {
      ...resultat,
      statut: ResultatStatut.OFFICIEL
    }).subscribe({
      next: () => {
        this.closeResultatModal();
        this.fetch({
          page: this.page(),
          size: this.perPage(),
          search: this.search(),
          sortKey: this.sort().key,
          sortDir: this.sort().dir,
        });
        toast.success('Résultat confirmé avec succès');
      },
      error: (err) => {
        console.error('Error confirming resultat:', err);
        toast.error('Erreur lors de la confirmation du résultat');
      },
    });
  }
}
