import {
  AutocompleteRes,
  GooglePlaceAutocompleteComponent,
} from '../../../shared/components/google-place-autocomplete/google-place-autocomplete.component';

import { CommonModule } from '@angular/common';
import { Component, Optional } from '@angular/core';
import {
  FormControl,
  FormGroup,
  FormsModule,
  ReactiveFormsModule,
  Validators,
} from '@angular/forms';
import {
  LuogoRes,
  Stipulatore,
  Struttura,
  StrutturaExcerptRes,
  StrutturaMadre,
  TipoGestione,
  TipoStruttura,
} from '@api/models';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { faHospital } from '@fortawesome/free-regular-svg-icons';
import { faCity, faGlobeEurope } from '@fortawesome/free-solid-svg-icons';
import { RxState } from '@rx-angular/state';
import { ToastMessageOptions } from 'primeng/api';
import { AutoCompleteModule } from 'primeng/autocomplete';
import { ButtonModule } from 'primeng/button';
import { CalendarModule } from 'primeng/calendar';
import { DatePickerModule } from 'primeng/datepicker';
import { DividerModule } from 'primeng/divider';
import { DropdownModule } from 'primeng/dropdown';
import {
  DialogService,
  DynamicDialogConfig,
  DynamicDialogRef,
} from 'primeng/dynamicdialog';
import { InputSwitchModule } from 'primeng/inputswitch';
import { InputTextModule } from 'primeng/inputtext';
import { MessagesModule } from 'primeng/messages';
import { MultiSelectModule } from 'primeng/multiselect';
import { RatingModule } from 'primeng/rating';
import { SelectModule } from 'primeng/select';
import { SkeletonModule } from 'primeng/skeleton';
import { TextareaModule } from 'primeng/textarea';
import { combineLatest, EMPTY, Observable, of, Subject } from 'rxjs';
import {
  catchError,
  endWith,
  map,
  startWith,
  switchMap,
  takeUntil,
  tap,
} from 'rxjs/operators';
import { handleDynamicDialog } from '../../../shared/utils/dynamic-dialog-utils';
import { StruttureService } from '../strutture.service';

const struttureFormGroupFunc = (strutture?: Struttura) => ({
  nome: new FormControl<string | null>(strutture?.nome ?? null, {
    validators: [Validators.required],
  }),

  valutazione: new FormControl<number | null>(strutture?.stelline ?? null),
  partitaIva: new FormControl<string | null>(strutture?.partitaIva ?? null),

  latitudine: new FormControl<string | null>(strutture?.latitudine ?? null),
  longitudine: new FormControl<string | null>(strutture?.longitudine ?? null),

  citta: new FormControl<LuogoRes | null>(strutture?.codiceLuogo ?? null, {
    validators: [Validators.required],
  }),
  indirizzo: new FormControl<string | null>(strutture?.indirizzo ?? null),

  civico: new FormControl<string | null>({
    value: strutture?.numeroCivico ?? null,
    disabled: false,
  }),

  cap: new FormControl<string | null>(
    { value: strutture?.cap ?? null, disabled: false },
    {
      validators: [Validators.pattern('^(|[0-9]{5})$')],
    },
  ),

  strutturaMadre: new FormControl<StrutturaMadre | null>(
    strutture?.strutturaMadre && strutture?.strutturaMadre.id > 0
      ? strutture.strutturaMadre
      : null,
  ),
  tipoGestione: new FormControl<number | null>(
    strutture?.tipoGestione.id ?? null,
    {
      validators: [Validators.required],
    },
  ),
  tipoStruttura: new FormControl<number[]>(
    strutture?.struttureTipiStrutture?.map((s) => s.tipo.id) ?? [],
    { validators: [Validators.required] },
  ),
  stipulatori: new FormControl<number[]>(
    strutture?.visibilitaStrutture?.map((s) => s.idStipulatore) ?? [],
    { validators: [Validators.required] },
  ),
});
export type StruttureForm = FormGroup<
  ReturnType<typeof struttureFormGroupFunc>
>;
export type AccountFormValue = StruttureForm['value'];

export interface ManageStruttureDialogComponentState {
  isSaving: boolean;
  isLoading: boolean;
  struttureFormSubmitted: boolean;
  selectedStruttura: Struttura;
  tipoStrutture: TipoStruttura[];
  tipoStruttureIsLoading: boolean;
  tipoGestione: TipoGestione[];
  tipoGestioneIsLoading: boolean;
  filteredCities: LuogoRes[];
  filteredStrutturaMadre: StrutturaExcerptRes[];
  stipulatori: Stipulatore[];
  stipulatoriIsLoading: boolean;
}

type State = ManageStruttureDialogComponentState;

@Component({
  imports: [
    CommonModule,
    SkeletonModule,
    InputSwitchModule,
    FormsModule,
    ButtonModule,
    DividerModule,
    DropdownModule,
    ReactiveFormsModule,
    InputTextModule,
    CalendarModule,
    MultiSelectModule,
    SelectModule,
    DatePickerModule,
    MessagesModule,
    AutoCompleteModule,
    FontAwesomeModule,
    RatingModule,
    GooglePlaceAutocompleteComponent,
    TextareaModule,
  ],
  providers: [RxState, DialogService],
  selector: 'manage-strutture-dialog',
  templateUrl: 'manage-strutture-dialog.component.html',
  styleUrl: './manage-strutture-dialog.component.scss',
})
export class ManageStruttureDialogComponent {
  model$: Observable<ManageStruttureDialogComponentState>;

  struttureForm = new FormGroup(struttureFormGroupFunc(undefined));

  showTransitionOptions = '500ms';

  messages: ToastMessageOptions[] = [];

  setValueEvent: Subject<string | null> = new Subject<string | null>();
  private destroy$ = new Subject<void>();
  autocompleteDisabled = true;

  faCity = faCity;
  faGlobeEurope = faGlobeEurope;
  faHospital = faHospital;

  constructor(
    @Optional() readonly dialogRef: DynamicDialogRef,
    @Optional() readonly dialogConfig: DynamicDialogConfig,
    private struttureService: StruttureService,
    public state: RxState<State>,
  ) {
    this.model$ = this.state.select();
    this.state.set({
      isSaving: false,
      isLoading: false,
      struttureFormSubmitted: false,
      tipoStruttureIsLoading: false,
      tipoGestioneIsLoading: false,
      stipulatori: [],
      stipulatoriIsLoading: false,
    });

    const fetchStipulatori$ = of(EMPTY).pipe(
      switchMap(() =>
        this.struttureService.getStipulatori().pipe(
          map((res) => ({
            stipulatori: res,
          })),
          startWith({ stipulatoriIsLoading: true }),
          endWith({ stipulatoriIsLoading: false }),
        ),
      ),
    );
    this.state.connect(fetchStipulatori$);

    handleDynamicDialog.bind(this)();

    const patchForm$ = combineLatest([
      this.state.select('selectedStruttura'),
    ]).pipe(
      switchMap(([struttura]) => {
        return of(struttura).pipe(
          tap(() => {
            this.struttureForm.patchValue(
              new FormGroup(struttureFormGroupFunc(struttura)).value,
            );
            if (struttura.codiceLuogo) {
              this.autocompleteDisabled = false;
              setTimeout(() => {
                this.setValueEvent.next(struttura.indirizzo);
              }, 10);
            }
          }),
          map((struttura) => ({ selectedStruttura: struttura })),
          startWith({ isLoading: true }),
          endWith({ isLoading: false }),
        );
      }),
    );

    this.state.connect(patchForm$);
    this.getTipiGestione();
    this.getTipiStrutture();

    this.struttureForm
      .get('citta')
      ?.valueChanges.pipe(
        tap((value) => this.handleCittaChange(value)),
        takeUntil(this.destroy$),
      )
      .subscribe();
  }

  private handleCittaChange(value: any) {
    const isEmpty = !value;
    this.autocompleteDisabled = isEmpty;
    this.setValueEvent.next(null);

    [
      this.struttureForm.get('indirizzo'),
      this.struttureForm.get('cap'),
      this.struttureForm.get('civico'),
    ].forEach((field) => {
      field?.reset();
      isEmpty ? field?.disable() : field?.enable();
    });
  }

  setDetailFromGoogle(event: AutocompleteRes) {
    [
      this.struttureForm.get('indirizzo'),
      this.struttureForm.get('cap'),
      this.struttureForm.get('civico'),
    ].forEach((field) => {
      field?.reset();
      field?.enable();
    });

    const { indirizzo, cap, civico, valutazione, latitudine, longitudine } =
      event;
    this.struttureForm.patchValue({
      indirizzo,
      cap,
      civico,
      valutazione,
      latitudine,
      longitudine,
    });
  }

  getTipiStrutture() {
    const tipiStrutture$ = this.struttureService.getTipoStrutture().pipe(
      map((res) => ({ tipoStrutture: res.rows })),
      catchError((err) => {
        this.messages = [
          {
            severity: 'error',
            life: 5000,
            summary: 'Attenzione!',
            detail:
              'Impossibile recuperare i tipi struttura al momento, riprova più tardi',
          },
        ];
        return of({});
      }),
      startWith({ tipoStruttureIsLoading: true }),
      endWith({ tipoStruttureIsLoading: false }),
    );
    this.state.connect(tipiStrutture$);
  }

  fetchCity(
    event: { originalEvent: Event; query: string },
    flagAttivo?: number,
  ) {
    const fetchCities$ = this.struttureService
      .getLuoghi(event.query, flagAttivo)
      .pipe(
        catchError((err) => {
          return of(null);
        }),
        map((res) => ({ filteredCities: res?.rows })),
      );
    this.state.connect(fetchCities$);
  }

  fetchStrutturaMadre(
    event: { originalEvent: Event; query: string },
    flagOnlyStrutturaMadre?: number,
  ) {
    const fetchStrutturaMadre$ = this.struttureService
      .getStrutturaMadre(event.query, flagOnlyStrutturaMadre)
      .pipe(
        catchError((err) => {
          return of(null);
        }),
        map((res) => ({ filteredStrutturaMadre: res?.rows })),
      );
    this.state.connect(fetchStrutturaMadre$);
  }

  getTipiGestione() {
    const tipiGestione$ = this.struttureService.getTipoGestione().pipe(
      map((res) => ({ tipoGestione: res.rows })),
      catchError((err) => {
        this.messages = [
          {
            severity: 'error',
            life: 5000,
            summary: 'Attenzione!',
            detail:
              'Impossibile recuperare i tipi gestione al momento, riprova più tardi',
          },
        ];
        return of({});
      }),
      startWith({ tipoGestioneIsLoading: true }),
      endWith({ tipoGestioneIsLoading: false }),
    );
    this.state.connect(tipiGestione$);
  }

  submitStruttureForm() {
    this.state.set({
      struttureFormSubmitted: true,
    });
    if (this.struttureForm.invalid) return;
    const _struttureForm = this.struttureForm.getRawValue();
    console.log(_struttureForm.stipulatori);

    const baseParams = {
      nome: _struttureForm.nome!,
      idTipoGestione: _struttureForm.tipoGestione ?? 0,
      stelline: _struttureForm.valutazione ?? 0,
      codiceLuogo: _struttureForm.citta!.codice ?? '',
      idStrutturaMadre: _struttureForm.strutturaMadre?.id,
      partitaIva: _struttureForm.partitaIva,
      indirizzo: _struttureForm.indirizzo,
      numeroCivico: _struttureForm.civico,
      cap: _struttureForm.cap,
      idTipiStruttura: Array.isArray(_struttureForm.tipoStruttura)
        ? _struttureForm.tipoStruttura
        : [],
    };

    const createParams = {
      ...baseParams,
      idStipulatori: _struttureForm.stipulatori ?? [],
    };
    const saveAddStrutture$ = (
      !this.state.get('selectedStruttura')
        ? this.struttureService.createStruttura(createParams)
        : this.struttureService.editStruttura(
            this.state.get('selectedStruttura').id,
            baseParams,
          )
    ).pipe(
      catchError((err) => {
        return of(null);
      }),
      tap((res) => {
        if (!res) return;
        this.state.set(() => ({
          struttureFormSubmitted: false,
        }));
        this.dialogRef.close(res);
      }),
      map(() => ({})),
      startWith({ isSaving: true }),
      endWith({ isSaving: false }),
    );
    this.state.connect(saveAddStrutture$);
  }

  cancelForm() {
    this.dialogRef?.close();
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
}
