import { CommonModule } from '@angular/common';
import {
  Component,
  Input,
  computed,
  effect,
  inject,
  signal,
} from '@angular/core';
import {
  FormControl,
  FormGroup,
  FormsModule,
  ReactiveFormsModule,
  Validators,
  type AbstractControl,
} from '@angular/forms';
// import {
//   type CreateNomenclatore,
//   type CreateNomenclatoreLivello1,
//   type CreateNomenclatoreLivello2,
//   type CreateNomenclatoreLivello3,
//   type CreateNomenclatoreLivello4,
//   type UpdateNomenclatore,
//   type UpdateNomenclatoreLivello1,
//   type UpdateNomenclatoreLivello2,
//   type UpdateNomenclatoreLivello3,
//   type UpdateNomenclatoreLivello4,
// } from '@api/models.js';
// import { DateMaskDirective } from '@core/directives';
import { ReactiveFormHasRequiredValidatorPipe } from '@core/pipes';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { RxLet } from '@rx-angular/template/let';
import { get, isEmpty } from 'lodash';
import { ButtonModule } from 'primeng/button';
import { CalendarModule } from 'primeng/calendar';
import { CheckboxModule } from 'primeng/checkbox';
import { ChipModule } from 'primeng/chip';
import { DatePickerModule } from 'primeng/datepicker';
import { DialogModule } from 'primeng/dialog';
import { DropdownModule } from 'primeng/dropdown';
import { FieldsetModule } from 'primeng/fieldset';
import { InputGroupModule } from 'primeng/inputgroup';
import { InputGroupAddonModule } from 'primeng/inputgroupaddon';
import { InputTextModule } from 'primeng/inputtext';
import { ProgressBarModule } from 'primeng/progressbar';
import { SkeletonModule } from 'primeng/skeleton';
import { SplitButtonModule } from 'primeng/splitbutton';
import { TextareaModule } from 'primeng/textarea';
import { TreeModule } from 'primeng/tree';
import { TreeTableModule } from 'primeng/treetable';
import {
  BehaviorSubject,
  EMPTY,
  catchError,
  distinctUntilChanged,
  filter,
  map,
  of,
  raceWith,
  shareReplay,
  switchMap,
  tap,
  withLatestFrom,
} from 'rxjs';
// import type { NomenclatoreNodeType } from './nomenclatore.service.js';
import {
  CreateNomenclatore,
  CreateNomenclatoreLivello1,
  CreateNomenclatoreLivello2,
  CreateNomenclatoreLivello3,
  CreateNomenclatoreLivello4,
  UpdateNomenclatore,
  UpdateNomenclatoreLivello1,
  UpdateNomenclatoreLivello2,
  UpdateNomenclatoreLivello3,
  UpdateNomenclatoreLivello4,
} from '@api/models';
import { isDefined } from '@core/common/operators/is-defined.js';
import dayjs from 'dayjs';
import {
  AutoCompleteCompleteEvent,
  AutoCompleteModule,
} from 'primeng/autocomplete';
import { SelectModule } from 'primeng/select';
import {
  Nomenclatore,
  NomenclatoreLiv1,
  NomenclatoreLiv2,
  NomenclatoreLiv3,
  NomenclatoreLiv4,
  NomenclatoreNodeType,
  NomenclatoreService,
} from '../nomenclatore.service';

const nomenclatoreFormRequirements = {
  descrizione: {
    maxlength: 512,
  },
  descrizioneRimborso: {
    maxlength: 255,
  },
  note: {
    maxlength: 255,
  },
  rawChiaviRicerca: {
    separator: '; ',
    maxlength: 255,
  },
} as const;

const nomenclatoreFormGroupFunc = (
  mode: NomenclatoreDialogType | undefined,
  entry?: NomenclatoreDialogEntry,
) => {
  const rawDataInizioValidita = get(entry, 'dataInizioValidita');
  const rawDataFineValidita = get(entry, 'dataFineValidita');
  const rawChiaviRicerca = get(entry, 'chiaviRicerca') as string | undefined;

  return {
    id: new FormControl<number | null>(entry?.id ?? null),
    codice: new FormControl<string | null>(entry?.codice ?? null, {
      validators: [Validators.required],
    }),
    descrizione: new FormControl<string | null>(entry?.descrizione ?? null, {
      validators: [
        Validators.required,
        Validators.maxLength(
          nomenclatoreFormRequirements.descrizione.maxlength,
        ),
      ],
    }),
    nomenclatoreLiv1: new FormControl<number>(get(entry, 'idLivello1') ?? 0, {
      validators: [Validators.required],
    }),
    nomenclatoreLiv2: new FormControl<number>(get(entry, 'idLivello2') ?? 0, {
      validators: [Validators.required],
    }),
    nomenclatoreLiv3: new FormControl<number>(get(entry, 'idLivello3') ?? 0, {
      validators: [Validators.required],
    }),
    nomenclatoreLiv4: new FormControl<number>(get(entry, 'idLivello4') ?? 0, {
      validators: [Validators.required],
    }),
    chiaviRicerca: new FormControl<string[]>(
      rawChiaviRicerca && !isEmpty(rawChiaviRicerca)
        ? rawChiaviRicerca.split(
            nomenclatoreFormRequirements.rawChiaviRicerca.separator,
          )
        : [],
    ),
    rawChiaviRicerca: new FormControl<string | null>(rawChiaviRicerca ?? null, {
      validators: [
        Validators.maxLength(
          nomenclatoreFormRequirements.rawChiaviRicerca.maxlength,
        ),
      ],
    }),
    codiceFasi: new FormControl<string | null>(
      get(entry, 'codiceFasi') ?? null,
    ),
    dataInizioValidita: new FormControl<Date>(
      rawDataInizioValidita ? new Date(rawDataInizioValidita) : new Date(),
      {
        validators: [Validators.required],
      },
    ),
    dataFineValidita: new FormControl<Date>(
      rawDataFineValidita ? new Date(rawDataFineValidita) : new Date(),
      {
        validators: [Validators.required],
      },
    ),
    descrizioneRimborso: new FormControl<string | null>(
      get(entry, 'descrizioneRimborso') ?? null,
      {
        validators: [
          Validators.maxLength(
            nomenclatoreFormRequirements.descrizioneRimborso.maxlength,
          ),
        ],
      },
    ),
    flagAccorpaLivello: new FormControl<boolean | null>(
      get(entry, 'flagAccorpaLivello') ?? null,
    ),
    flagNecessitaDocumentazione: new FormControl<boolean | null>(
      get(entry, 'flagNecessitaDocumentazione') ?? null,
    ),
    flagSelezionabileDaUtenti: new FormControl<boolean | null>(
      get(entry, 'flagSelezionabileDaUtenti') ?? null,
    ),
    idElemento: new FormControl<number>(get(entry, 'idElemento') ?? 0, {
      validators: [Validators.required],
    }),
    idTipoPrestazione: new FormControl<number>(
      get(entry, 'idTipoPrestazione') ?? 0,
      {
        validators: [Validators.required],
      },
    ),
    idUnitaDiMisura: new FormControl<number>(
      get(entry, 'idUnitaDiMisura') ?? 0,
      {
        validators: [Validators.required],
      },
    ),
    note: new FormControl<string | null>(get(entry, 'note') ?? null, {
      validators: [
        Validators.maxLength(nomenclatoreFormRequirements.note.maxlength),
      ],
    }),
  };
};
export type NomenclatoreEntryForm = FormGroup<
  ReturnType<typeof nomenclatoreFormGroupFunc>
>;
export type NomenclatoreEntryFormValue = NomenclatoreEntryForm['value'];

export type NomenclatoreDialogEntry = Nomenclatore &
  NomenclatoreLiv1 &
  NomenclatoreLiv2 &
  NomenclatoreLiv3 &
  NomenclatoreLiv4;
export type NomenclatoreDialogType = NomenclatoreNodeType;

@Component({
  selector: 'app-codice-descrizione-item',
  template: ` @if (item) {
      <span
        ><span class="font-semibold">{{ item.codice }}</span> -
        {{ item.descrizione }}</span
      >
    } @else {
      {{ emptyMessage }}
    }`,
  standalone: true,
})
export class CodiceDescrizioneItemComponent {
  @Input({ required: true }) item?: {
    codice: number | string;
    descrizione: string;
  };
  @Input() emptyMessage = 'Non impostato';
}

@Component({
  selector: 'app-maxlength-counter',
  template: `
    @if (control && control.enabled && (control.value?.length ?? 0) >= 0) {
      <small
        class="block text-right"
        [ngClass]="{
          'text-error': control.errors?.['maxlength'],
        }"
        ><span
          [ngClass]="{
            'font-bold': control.errors?.['maxlength'],
          }"
          >{{ control.value?.length ?? 0 }}</span
        ><span>{{ '/' + maxLength + ' caratteri' }}</span></small
      >
    }
  `,
  imports: [CommonModule],
  standalone: true,
})
export class MaxLengthCounterComponent {
  @Input({ required: true }) control?: FormControl;
  @Input({ required: true }) maxLength = 0;
}
@UntilDestroy()
@Component({
  selector: 'app-nomenclatore-dialog',
  standalone: true,
  imports: [
    RxLet,
    CommonModule,
    FormsModule,
    ButtonModule,
    SplitButtonModule,
    InputGroupModule,
    InputGroupAddonModule,
    InputTextModule,
    CheckboxModule,
    TextareaModule,
    ChipModule,
    CalendarModule,
    TreeModule,
    TreeTableModule,
    ProgressBarModule,
    FieldsetModule,
    ReactiveFormHasRequiredValidatorPipe,
    CodiceDescrizioneItemComponent,
    MaxLengthCounterComponent,
    DialogModule,
    DropdownModule,
    ReactiveFormsModule,
    SkeletonModule,
    DatePickerModule,
    SelectModule,
    AutoCompleteModule,
  ],
  templateUrl: `./nomenclatore-dialog.component.html`,
  styleUrl: `./nomenclatore-dialog.component.scss`,
})
export class NomenclatoreDialogComponent {
  nomenclatoreService = inject(NomenclatoreService);

  items: any[] = [];

  @Input({ required: true }) isOpen = signal(false);
  @Input({ required: true }) data = signal<
    | {
        mode: NomenclatoreDialogType;
        nomenclatore: NomenclatoreDialogEntry | null;
      }
    | undefined
  >(undefined);

  isLoading = signal(false);
  isSaving = signal(false);
  isBusy = computed(() => this.isLoading() || this.isSaving());

  submitted = signal(false);
  nomenclatoreForm = new FormGroup(
    nomenclatoreFormGroupFunc(this.data()?.mode),
  );
  nomenclatoreFormRequirements = nomenclatoreFormRequirements;

  selectableDateRange = {
    min: new Date('1900-01-01'),
    max: new Date('9998-12-31'),
  };

  _data$ = new BehaviorSubject<
    | {
        mode: NomenclatoreDialogType;
        nomenclatore: NomenclatoreDialogEntry | null;
      }
    | undefined
  >(undefined);

  data$ = this._data$.asObservable().pipe(
    untilDestroyed(this),
    distinctUntilChanged(),
    tap((data) => {
      this.isLoading.set(true);
      const mode = data?.mode;
      const nomenclatore = data?.nomenclatore;
      this.nomenclatoreForm.reset(
        new FormGroup(
          nomenclatoreFormGroupFunc(mode, nomenclatore ?? undefined),
        ).value,
      );
      this.isLoading.set(false);
    }),
    shareReplay(1),
  );

  nomenclatore$ = this.data$.pipe(
    filter(isDefined),
    switchMap((data) => of(data.nomenclatore)),
    shareReplay(1),
  );

  nomenclatoreLiv1$ = this.nomenclatore$.pipe(
    untilDestroyed(this),
    withLatestFrom(this.nomenclatoreService.nomenclatoreLivello1$),
    map(([nomenclatore, nomenclatoreLiv1]) => {
      return nomenclatoreLiv1.filter((item) => item.id !== nomenclatore?.id);
    }),
    shareReplay(1),
  );

  nomenclatoreLiv2$ = this.nomenclatore$.pipe(
    raceWith(
      this.nomenclatoreForm.controls.nomenclatoreLiv1.valueChanges.pipe(
        filter(isDefined),
        distinctUntilChanged(),
      ),
    ),
    untilDestroyed(this),
    withLatestFrom(this.nomenclatoreService.nomenclatoreLivello2$),
    switchMap(([liv1IdOrNomenclatore, nomenclatoreLiv2]) => {
      const isValueChange = typeof liv1IdOrNomenclatore === 'number';
      const liv1Id = isValueChange
        ? liv1IdOrNomenclatore
        : liv1IdOrNomenclatore?.idLivello1;
      return of(
        nomenclatoreLiv2.filter(
          (item) =>
            (item.idLivello1 === liv1Id || item.id === 0) &&
            item.id !== this.data()?.nomenclatore?.id,
        ),
      ).pipe(
        tap(() => {
          if (isValueChange)
            this.clearField(this.nomenclatoreForm.controls.nomenclatoreLiv2);
        }),
      );
    }),
    shareReplay(1),
  );

  search(event: AutoCompleteCompleteEvent) {
    this.items = [...Array(1).keys()].map((item) => event.query);
  }

  nomenclatoreLiv3$ = this.nomenclatore$.pipe(
    raceWith(
      this.nomenclatoreForm.controls.nomenclatoreLiv2.valueChanges.pipe(
        filter(isDefined),
        distinctUntilChanged(),
      ),
    ),
    untilDestroyed(this),
    withLatestFrom(this.nomenclatoreService.nomenclatoreLivello3$),
    switchMap(([liv2IdOrNomenclatore, nomenclatoreLiv3]) => {
      const isValueChange = typeof liv2IdOrNomenclatore === 'number';
      const liv2Id = isValueChange
        ? liv2IdOrNomenclatore
        : liv2IdOrNomenclatore?.idLivello2;
      return of(
        nomenclatoreLiv3.filter(
          (item) =>
            (item.idLivello2 === liv2Id || item.id === 0) &&
            item.id !== this.data()?.nomenclatore?.id,
        ),
      ).pipe(
        tap(() => {
          if (isValueChange)
            this.clearField(this.nomenclatoreForm.controls.nomenclatoreLiv3);
        }),
      );
    }),
    shareReplay(1),
  );

  nomenclatoreLiv4$ = this.nomenclatore$.pipe(
    raceWith(
      this.nomenclatoreForm.controls.nomenclatoreLiv3.valueChanges.pipe(
        filter(isDefined),
        distinctUntilChanged(),
      ),
    ),
    untilDestroyed(this),
    withLatestFrom(this.nomenclatoreService.nomenclatoreLivello4$),
    switchMap(([liv3IdOrNomenclatore, nomenclatoreLiv4]) => {
      const isValueChange = typeof liv3IdOrNomenclatore === 'number';
      const liv3Id = isValueChange
        ? liv3IdOrNomenclatore
        : liv3IdOrNomenclatore?.idLivello2;
      return of(
        nomenclatoreLiv4.filter(
          (item) =>
            (item.idLivello3 === liv3Id || item.id === 0) &&
            item.id !== this.data()?.nomenclatore?.id,
        ),
      ).pipe(
        tap(() => {
          if (isValueChange)
            this.clearField(this.nomenclatoreForm.controls.nomenclatoreLiv4);
        }),
      );
    }),
    shareReplay(1),
  );

  categoriePrestazione$ = this.nomenclatoreService.categoriePrestazione$;
  elementi$ = this.nomenclatoreService.elementi$;
  tipiPrestazione$ = this.nomenclatoreService.tipiPrestazione$;
  unitaDiMisura$ = this.nomenclatoreService.unitaDiMisura$;

  constructor() {
    effect(
      () => {
        const data = this.data();
        this._data$.next(data);

        const mode = data?.mode;
        const nomenclatore = data?.nomenclatore;

        this.nomenclatoreForm.controls.codice.disable();
        this.nomenclatoreForm.controls.descrizioneRimborso.disable();
        this.nomenclatoreForm.controls.idElemento.disable();
        this.nomenclatoreForm.controls.dataInizioValidita.disable();
        this.nomenclatoreForm.controls.dataFineValidita.disable();
        this.nomenclatoreForm.controls.note.disable();
        this.nomenclatoreForm.controls.chiaviRicerca.disable();
        this.nomenclatoreForm.controls.flagAccorpaLivello.disable();
        this.nomenclatoreForm.controls.flagNecessitaDocumentazione.disable();
        this.nomenclatoreForm.controls.flagSelezionabileDaUtenti.disable();
        this.nomenclatoreForm.controls.nomenclatoreLiv1.disable();
        this.nomenclatoreForm.controls.nomenclatoreLiv2.disable();
        this.nomenclatoreForm.controls.nomenclatoreLiv3.disable();
        this.nomenclatoreForm.controls.nomenclatoreLiv4.disable();

        switch (mode) {
          case 'livello':
            if (!nomenclatore) {
              this.nomenclatoreForm.controls.codice.enable();
              this.nomenclatoreForm.controls.nomenclatoreLiv1.enable();
              this.nomenclatoreForm.controls.nomenclatoreLiv2.enable();
              this.nomenclatoreForm.controls.nomenclatoreLiv3.enable();
              this.nomenclatoreForm.controls.nomenclatoreLiv4.enable();
            }
            break;
          case 'voce nomenclatore':
            this.nomenclatoreForm.controls.descrizioneRimborso.enable();
            this.nomenclatoreForm.controls.idElemento.enable();
            this.nomenclatoreForm.controls.dataInizioValidita.enable();
            this.nomenclatoreForm.controls.dataFineValidita.enable();
            this.nomenclatoreForm.controls.note.enable();
            this.nomenclatoreForm.controls.chiaviRicerca.enable();
            this.nomenclatoreForm.controls.flagAccorpaLivello.enable();
            this.nomenclatoreForm.controls.flagNecessitaDocumentazione.enable();
            this.nomenclatoreForm.controls.flagSelezionabileDaUtenti.enable();
            this.nomenclatoreForm.controls.nomenclatoreLiv1.enable();
            this.nomenclatoreForm.controls.nomenclatoreLiv2.enable();
            this.nomenclatoreForm.controls.nomenclatoreLiv3.enable();
            this.nomenclatoreForm.controls.nomenclatoreLiv4.enable();
            if (!nomenclatore) {
              this.nomenclatoreForm.controls.codice.enable();
            }
            break;
        }
      },
      { allowSignalWrites: true },
    );

    const updateRawChiaviRicercaOnChipsChange$ =
      this.nomenclatoreForm.controls.chiaviRicerca.valueChanges.pipe(
        untilDestroyed(this),
        map(
          (values) =>
            values?.join(
              nomenclatoreFormRequirements.rawChiaviRicerca.separator,
            ) ?? '',
        ),
        tap((rawValue) => {
          this.nomenclatoreForm.controls.rawChiaviRicerca.setValue(rawValue);
        }),
      );
    updateRawChiaviRicercaOnChipsChange$.subscribe();
  }

  saveChanges() {
    this.submitted.set(true);
    if (!this.nomenclatoreForm.valid) return;
    this.isSaving.set(true);

    const data = this.data();
    const mode = data?.mode;
    const nomenclatore = data?.nomenclatore;
    const form = this.nomenclatoreForm.getRawValue();

    const save$ = of(EMPTY).pipe(
      tap(() => {
        this.isSaving.set(true);
      }),
      untilDestroyed(this),
      switchMap(() => {
        switch (true) {
          case mode === 'voce nomenclatore': {
            const dto: CreateNomenclatore & UpdateNomenclatore = {
              chiaviRicerca: form.rawChiaviRicerca ?? null,
              codice: form.codice!,
              codiceFasi: form.codiceFasi,
              dataFineValidita:
                dayjs(form.dataFineValidita).toISOString() ??
                dayjs(new Date()).toISOString(),
              // ?.toTemporalInstant()
              // .toZonedDateTimeISO(Temporal.Now.timeZoneId())
              // .toPlainDate()
              // .toString() ?? null,
              dataInizioValidita:
                dayjs(form.dataInizioValidita).toISOString() ??
                dayjs(new Date()).toISOString(),
              // ?.toTemporalInstant()
              // .toZonedDateTimeISO(Temporal.Now.timeZoneId())
              // .toPlainDate()
              // .toString() ?? null,
              descrizione: form.descrizione!,
              descrizioneRimborso: form.descrizioneRimborso,
              flagAccorpaLivello: form.flagAccorpaLivello!,
              flagNecessitaDocumentazione: form.flagNecessitaDocumentazione!,
              flagSelezionabileDaUtenti: form.flagSelezionabileDaUtenti!,
              idElemento: form.idElemento!,
              idLivello1: form.nomenclatoreLiv1!,
              idLivello2: form.nomenclatoreLiv2!,
              idLivello3: form.nomenclatoreLiv3!,
              idLivello4: form.nomenclatoreLiv4!,
              idTipoPrestazione: form.idTipoPrestazione!,
              idUnitaDiMisura: form.idUnitaDiMisura!,
              note: form.note,
            };

            return !nomenclatore?.id
              ? this.nomenclatoreService.createNomenclatore(dto)
              : this.nomenclatoreService.updateNomenclatore(
                  nomenclatore.id,
                  dto,
                );
          }
          case mode === 'livello' && (form.nomenclatoreLiv3 ?? 0) > 0: {
            const dto: CreateNomenclatoreLivello4 & UpdateNomenclatoreLivello4 =
              {
                codice: form.codice!,
                descrizione: form.descrizione!,
                idLivello3: form.nomenclatoreLiv3!,
              };

            return !nomenclatore?.id
              ? this.nomenclatoreService.createNomenclatoreLivello({
                  livello: 4,
                  dto,
                })
              : this.nomenclatoreService.updateNomenclatoreLivello(
                  nomenclatore.id,
                  { livello: 4, dto },
                );
          }
          case mode === 'livello' && (form.nomenclatoreLiv2 ?? 0) > 0: {
            const dto: CreateNomenclatoreLivello3 & UpdateNomenclatoreLivello3 =
              {
                codice: form.codice!,
                descrizione: form.descrizione!,
                idLivello2: form.nomenclatoreLiv2!,
              };
            return !nomenclatore?.id
              ? this.nomenclatoreService.createNomenclatoreLivello({
                  livello: 3,
                  dto,
                })
              : this.nomenclatoreService.updateNomenclatoreLivello(
                  nomenclatore.id,
                  { livello: 3, dto },
                );
          }
          case mode === 'livello' && (form.nomenclatoreLiv1 ?? 0) > 0: {
            const dto: CreateNomenclatoreLivello2 & UpdateNomenclatoreLivello2 =
              {
                codice: form.codice!,
                descrizione: form.descrizione!,
                idLivello1: form.nomenclatoreLiv1!,
              };

            return !nomenclatore?.id
              ? this.nomenclatoreService.createNomenclatoreLivello({
                  livello: 2,
                  dto,
                })
              : this.nomenclatoreService.updateNomenclatoreLivello(
                  nomenclatore.id,
                  { livello: 2, dto },
                );
          }
          case mode === 'livello' && (form.nomenclatoreLiv1 ?? 0) === 0: {
            const dto: CreateNomenclatoreLivello1 & UpdateNomenclatoreLivello1 =
              {
                codice: form.codice!,
                descrizione: form.descrizione!,
              };

            return !nomenclatore?.id
              ? this.nomenclatoreService.createNomenclatoreLivello({
                  livello: 1,
                  dto,
                })
              : this.nomenclatoreService.updateNomenclatoreLivello(
                  nomenclatore.id,
                  { livello: 1, dto },
                );
          }
          default:
            return of(null);
        }
      }),
      catchError(() => of(null)),
      tap((res) => {
        this.isSaving.set(false);
        if (!res) return;

        this.close();
        this.nomenclatoreService.refresh({ treeOnly: true });
      }),
    );
    save$.subscribe();
  }

  revertChanges() {
    const data = this.data();
    const mode = data?.mode;
    const nomenclatore = data?.nomenclatore;
    this.nomenclatoreForm.reset(
      new FormGroup(nomenclatoreFormGroupFunc(mode, nomenclatore ?? undefined))
        .value,
    );
  }

  close() {
    this.isOpen.set(false);
  }

  reset() {
    this.data.set(undefined);
    this.submitted.set(false);
  }

  toggleFieldEnable(formControl: AbstractControl) {
    formControl.enabled ? formControl.disable() : formControl.enable();
  }

  clearField(formControl: AbstractControl) {
    formControl.patchValue(0);
  }
}
