import { CommonModule } from '@angular/common';
import {
  AfterViewInit,
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  effect,
  input,
  OnDestroy,
  output,
  signal,
  OnInit,
} from '@angular/core';
import {
  FormBuilder,
  FormControl,
  FormGroup,
  ReactiveFormsModule,
  Validators,
} from '@angular/forms';
import { Subscription } from 'rxjs';

import { Reunion, ReunionStatut } from 'src/app/core/interfaces/reunion';
import { Hippodrome } from 'src/app/core/interfaces/hippodrome';
import { HippodromeService } from 'src/app/core/services/hippodrome';

import { ZardButtonComponent } from '@shared/components/button/button.component';
import { ZardFormModule } from '@shared/components/form/form.module';
import { ZardInputDirective } from '@shared/components/input/input.directive';
import { ZardSelectComponent } from '@shared/components/select/select.component';
import { ZardSelectItemComponent } from '@shared/components/select/select-item.component';

@Component({
  selector: 'app-reunion-form',
  standalone: true,
  imports: [
    CommonModule,
    ReactiveFormsModule,
    ZardFormModule,
    ZardInputDirective,
    ZardSelectComponent,
    ZardSelectItemComponent,
    ZardButtonComponent,
  ],
  templateUrl: './reunion-form.html',
  styleUrls: ['./reunion-form.css'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ReunionForm implements OnInit, AfterViewInit, OnDestroy {
  value = input<Partial<Reunion> | null>(null);
  save = output<Partial<Reunion>>();
  cancel = output<void>();
  showInternalActions = input<boolean>(false);

  submitting = signal(false);
  submitted = signal(false);
  initializing = signal(false);
  loadingHippodromes = signal(false);

  private subs = new Subscription();
  form!: FormGroup;

  hippodromes = signal<Hippodrome[]>([]);
  selectedHippodromeLabel = signal<string>('');
  statuts = Object.values(ReunionStatut).map((s) => ({
    value: s,
    label:
      s === ReunionStatut.PLANIFIEE
        ? 'Planifiée'
        : s === ReunionStatut.EN_COURS
        ? 'En cours'
        : s === ReunionStatut.TERMINEE
        ? 'Terminée'
        : 'Annulée',
  }));

  constructor(
    private fb: FormBuilder,
    private hippodromeService: HippodromeService,
    private cdr: ChangeDetectorRef
  ) {
    this.form = this.fb.group({
      code: ['', Validators.required],
      nom: ['', Validators.required],
      date: ['', Validators.required],
      numero: [1, [Validators.required, Validators.min(1)]],
      statut: [ReunionStatut.PLANIFIEE, Validators.required],
      hippodromeId: new FormControl<string | null>(null, Validators.required),
      totalCourses: new FormControl<number | null>(null, [Validators.min(1)]),
    });

    // Update label when user selects a different hippodrome (both create and edit modes)
    // We always set the label manually to ensure it displays correctly
    this.subs.add(
      this.form.get('hippodromeId')?.valueChanges.subscribe((hippodromeId) => {
        // Skip if we're initializing (to avoid interfering with form initialization)
        if (this.initializing()) {
          return;
        }

        // Always update the label when user selects a hippodrome (both create and edit)
        // Update immediately without setTimeout for better responsiveness
        if (hippodromeId && this.hippodromes().length > 0) {
          // Try both string and direct comparison since form control might convert types
          const matchingHippodrome = this.hippodromes().find(
            (h) => String(h.id) === String(hippodromeId) || h.id === hippodromeId
          );
          if (matchingHippodrome) {
            const hippodromeLabel = `${matchingHippodrome.nom} (${matchingHippodrome.ville}, ${matchingHippodrome.pays})`;
            this.selectedHippodromeLabel.set(hippodromeLabel);
            // Force change detection to ensure the label is displayed immediately
            this.cdr.markForCheck();
          } else {
            this.selectedHippodromeLabel.set('');
          }
        } else {
          this.selectedHippodromeLabel.set('');
        }
      }) || new Subscription()
    );

    // When editing - watch both value and hippodromes to handle timing
    effect(() => {
        const v = this.value();
      const hippodromesList = this.hippodromes();
      const isLoading = this.loadingHippodromes();

      // Enable/disable hippodrome control based on loading state
      const hippodromeControl = this.form.get('hippodromeId');
      if (isLoading) {
        hippodromeControl?.disable({ emitEvent: false });
      } else {
        hippodromeControl?.enable({ emitEvent: false });
      }

      // Only reset if we have a new value (not just hippodromes loading)
      if (v !== null && v !== undefined) {
        this.initializing.set(true);

        // Get hippodromeId - convert to string if needed
        let hippodromeId: string | null = null;
        let hippodromeLabel: string = '';
        if (v.hippodrome?.id) {
          hippodromeId = String(v.hippodrome.id);
          // Only set hippodromeId if hippodromes are loaded and the ID exists
          // This ensures the select component can find the matching label
          if (hippodromesList.length > 0) {
            const matchingHippodrome = hippodromesList.find((h) => String(h.id) === hippodromeId);
            if (matchingHippodrome) {
              // Set the label manually so the select component displays it
              hippodromeLabel = `${matchingHippodrome.nom} (${matchingHippodrome.ville}, ${matchingHippodrome.pays})`;
              this.selectedHippodromeLabel.set(hippodromeLabel);
            } else {
              hippodromeId = null; // Don't set if not found
              this.selectedHippodromeLabel.set('');
            }
          } else {
            // If hippodromes aren't loaded yet, use the hippodrome from the value if available
            if (v.hippodrome.nom) {
              hippodromeLabel = `${v.hippodrome.nom} (${v.hippodrome.ville}, ${v.hippodrome.pays})`;
              this.selectedHippodromeLabel.set(hippodromeLabel);
            }
            // Don't set the value yet - it will be set once hippodromes finish loading
            hippodromeId = null;
          }
        } else {
          this.selectedHippodromeLabel.set('');
        }

        this.form.patchValue(
          {
            code: v.code ?? '',
            nom: v.nom ?? '',
            date: v.date ?? '',
            numero: v.numero ?? 1,
            statut: v.statut ?? ReunionStatut.PLANIFIEE,
            hippodromeId,
            totalCourses: v.totalCourses ?? null,
          },
          { emitEvent: false }
        );

        this.form.markAsPristine();
        this.form.markAsUntouched();
        queueMicrotask(() => this.initializing.set(false));
      } else if (v === null) {
        // Reset form when value is cleared
        this.initializing.set(true);
        this.selectedHippodromeLabel.set('');
        this.form.reset(
          {
            code: '',
            nom: '',
            date: '',
            numero: 1,
            statut: ReunionStatut.PLANIFIEE,
            hippodromeId: null,
            totalCourses: null,
          },
          { emitEvent: false }
        );
        this.form.markAsPristine();
        this.form.markAsUntouched();
        queueMicrotask(() => this.initializing.set(false));
      } else if (hippodromesList.length > 0 && !isLoading) {
        // Hippodromes just finished loading - re-apply value if we have one
        const currentValue = this.value();
        if (currentValue?.hippodrome?.id) {
          const hippodromeId = String(currentValue.hippodrome.id);
          const matchingHippodrome = hippodromesList.find((h) => String(h.id) === hippodromeId);
          if (matchingHippodrome) {
            // Set the label manually
            const hippodromeLabel = `${matchingHippodrome.nom} (${matchingHippodrome.ville}, ${matchingHippodrome.pays})`;
            this.selectedHippodromeLabel.set(hippodromeLabel);
            // Wait for Angular to render the select items
            setTimeout(() => {
              this.form.patchValue({ hippodromeId }, { emitEvent: false });
              this.cdr.detectChanges();
            }, 50);
          }
        }
      }
    });
  }

  ngOnInit() {
    // Fetch hippodromes from API
    this.loadingHippodromes.set(true);
    this.subs.add(
      this.hippodromeService.list({ page: 1, size: 1000 }, false).subscribe({
        next: (result) => {
          this.hippodromes.set(result.content);
          this.loadingHippodromes.set(false);
          // The effect will automatically re-run when hippodromes signal updates
        },
        error: (err) => {
          console.error('Error loading hippodromes:', err);
          this.loadingHippodromes.set(false);
        },
      })
    );
  }

  ngAfterViewInit() {
    // After view is initialized, ensure the hippodrome value and label are set correctly
    const currentValue = this.value();
    if (currentValue?.hippodrome?.id && this.hippodromes().length > 0) {
      const hippodromeId = String(currentValue.hippodrome.id);
      const matchingHippodrome = this.hippodromes().find((h) => String(h.id) === hippodromeId);
      if (matchingHippodrome) {
        // Set the label manually
        const hippodromeLabel = `${matchingHippodrome.nom} (${matchingHippodrome.ville}, ${matchingHippodrome.pays})`;
        this.selectedHippodromeLabel.set(hippodromeLabel);
        // Wait a bit for the select items to be fully rendered
        setTimeout(() => {
          const currentFormValue = this.form.get('hippodromeId')?.value;
          if (String(currentFormValue) !== hippodromeId) {
            this.form.patchValue({ hippodromeId }, { emitEvent: false });
            this.cdr.detectChanges();
          }
        }, 100);
      }
    }
  }

  onHippodromeSelectionChange(value: string) {
    // Immediately update the label when user selects a hippodrome
    if (value && this.hippodromes().length > 0) {
      const matchingHippodrome = this.hippodromes().find(
        (h) => String(h.id) === String(value) || h.id === value
      );
      if (matchingHippodrome) {
        const hippodromeLabel = `${matchingHippodrome.nom} (${matchingHippodrome.ville}, ${matchingHippodrome.pays})`;
        this.selectedHippodromeLabel.set(hippodromeLabel);
        this.cdr.markForCheck();
      }
    }
  }

  invalid(name: string): boolean {
    const c = this.form.get(name);
    return !!((this.submitted() || c?.touched || c?.dirty) && c?.invalid);
  }

  onSubmit() {
    this.submitted.set(true);
    if (this.form.invalid) {
      this.form.markAllAsTouched();
      return;
    }
    this.submitting.set(true);

    const values = this.form.value;
    const hippodrome = this.hippodromes().find((h) => h.id === values.hippodromeId) ?? null;

    this.save.emit({
      ...values,
      hippodrome,
    } as Partial<Reunion>);

    // Reset form state after emitting (parent will handle actual form reset via value input)
    // But reset submitting state so button is enabled again
    this.submitting.set(false);
    this.submitted.set(false);
  }

  ngOnDestroy() {
    this.subs.unsubscribe();
  }
}
