import { CommonModule } from '@angular/common';
import {
  Component,
  EventEmitter,
  Input,
  OnChanges,
  OnDestroy,
  OnInit,
  Output,
  SimpleChanges,
  computed,
  signal,
} from '@angular/core';
import { FormArray, FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { ZardFormModule } from '@shared/components/form/form.module';
import { ZardInputDirective } from '@shared/components/input/input.directive';
import { ZardButtonComponent } from '@shared/components/button/button.component';
import { ZardSelectComponent } from '@shared/components/select/select.component';
import { ZardSelectItemComponent } from '@shared/components/select/select-item.component';
import { Subscription } from 'rxjs';
import { Course } from 'src/app/core/interfaces/course';

/** One non-partant row shape (controls) */
type NonPartantRow = {
  numero: FormControl<number | null>;
  motif: FormControl<string>;
  declarePar: FormControl<string>;
};

/** Whole form shape */
type NonPartantFormShape = {
  nonPartants: FormArray<FormGroup<NonPartantRow>>;
};

@Component({
  selector: 'app-nonpartant-form',
  standalone: true,
  imports: [
    CommonModule,
    ReactiveFormsModule,
    ZardFormModule,
    ZardInputDirective,
    ZardButtonComponent,
    ZardSelectComponent,
    ZardSelectItemComponent,
  ],
  templateUrl: './nonpartant-form.html',
})
export class NonPartantForm implements OnInit, OnChanges, OnDestroy {
  @Input() course!: Course | null;

  /** We emit the UPDATED course with merged nonPartants */
  @Output() save = new EventEmitter<string[]>();
  @Output() cancel = new EventEmitter<void>();

  // 🔒 Strongly typed form
  form = new FormGroup<NonPartantFormShape>({
    nonPartants: new FormArray<FormGroup<NonPartantRow>>([]),
  });

  private sub = new Subscription();

  // reactive signals
  partantsMax = signal<number>(0);
  chosenNumbers = signal<number[]>([]); // current selected numbers across rows

  // --------- lifecycle ----------
  ngOnInit(): void {
    this.seedFromCourse();

    // Keep chosenNumbers in sync
    this.sub.add(
      this.rows.valueChanges.subscribe(() => {
        const nums = this.rows.controls
          .map((g) => g.controls.numero.value)
          .filter((n): n is number => Number.isFinite(n as number));
        this.chosenNumbers.set(nums);
      })
    );

    // initial seed chosenNumbers
    const nums = this.rows.controls
      .map((g) => g.controls.numero.value)
      .filter((n): n is number => Number.isFinite(n as number));
    this.chosenNumbers.set(nums);
  }

  ngOnChanges(changes: SimpleChanges): void {
    if ('course' in changes && this.course) {
      this.seedFromCourse();
    }
  }

  ngOnDestroy(): void {
    this.sub.unsubscribe();
  }

  // --------- typed helpers ----------
  get rows(): FormArray<FormGroup<NonPartantRow>> {
    return this.form.controls.nonPartants;
  }

  private createRow(initial?: {
    numero?: number | null;
    motif?: string;
    declarePar?: string;
  }): FormGroup<NonPartantRow> {
    return new FormGroup<NonPartantRow>({
      numero: new FormControl<number | null>(initial?.numero ?? null, {
        validators: [Validators.required, Validators.min(1)],
        nonNullable: false,
      }),
      motif: new FormControl<string>(initial?.motif ?? '', {
        nonNullable: true,
      }),
      declarePar: new FormControl<string>(initial?.declarePar ?? 'commissaire-1', {
        validators: [Validators.required],
        nonNullable: true,
      }),
    });
  }

  private seedFromCourse() {
    const max = this.course?.nombrePartants ?? 0;
    this.partantsMax.set(max);

    const arr = new FormArray<FormGroup<NonPartantRow>>([]);

    const existing = this.course?.nonPartants ?? [];
    if (existing.length) {
      for (const np of existing) {
        arr.push(
          this.createRow({
            numero: Number(np),
            motif: '',
            declarePar: 'commissaire-1',
          })
        );
      }
    } else {
      arr.push(this.createRow());
    }

    // Replace control with properly typed FormArray<FormGroup<...>>
    this.form.setControl('nonPartants', arr);

    // Refresh chosen numbers
    const nums = arr.controls
      .map((g) => g.controls.numero.value)
      .filter((n): n is number => Number.isFinite(n as number));
    this.chosenNumbers.set(nums);

    // Optional: coerce numero values to numbers if your z-select emits strings
    // (keep this if you notice strings arriving)
    this.sub.add(
      arr.valueChanges.subscribe(() => {
        arr.controls.forEach((g) => {
          const v = g.controls.numero.value as unknown as string | number | null;
          if (typeof v === 'string') {
            const n = Number(v);
            if (!Number.isNaN(n)) g.controls.numero.setValue(n, { emitEvent: false });
          }
        });
      })
    );
  }

  addRow() {
    if (!this.hasFreeSlots()) return;
    this.rows.push(this.createRow());
  }

  removeRow(i: number) {
    this.rows.removeAt(i);
  }

  // --------- options & validation ----------
  /** All numbers 1..partants */
  allNumbers = computed(() => Array.from({ length: this.partantsMax() }, (_, i) => i + 1));

  /** Remaining (not chosen elsewhere) */
  remainingNumbers = computed(() => {
    const chosen = new Set(this.chosenNumbers());
    return this.allNumbers().filter((n) => !chosen.has(n));
  });

  /** Options for a given row = remaining + its current value (so it stays visible) */
  optionsForRow(i: number) {
    const current = this.rows.at(i).controls.numero.value ?? undefined;
    const base = this.remainingNumbers();
    return typeof current === 'number' ? [...base, current].sort((a, b) => a - b) : base;
  }

  hasFreeSlots(): boolean {
    return this.remainingNumbers().length > 0;
  }

  onSubmit() {
    const max = this.partantsMax();
    let ok = true;

    this.rows.controls.forEach((g) => {
      const num = g.controls.numero.value;
      if (typeof num !== 'number' || num < 1 || num > max) {
        g.controls.numero.setErrors({ range: true });
        ok = false;
      }
    });

    if (ok && this.form.valid && this.course) {
      const nonPartants = this.rows.controls.map((g) => {
        const numero = g.controls.numero.value as number;
        return String(numero);
      });

      this.save.emit(nonPartants);
    } else {
      this.form.markAllAsTouched();
    }
  }
}
