/* eslint-disable @typescript-eslint/no-unsafe-return */
import { StrutturaExcerptRes } from './../../../../api/models/struttura-excerpt-res';

import { CommonModule } from '@angular/common';
import { Component, ViewChild } from '@angular/core';
import {
  AbstractControl,
  FormControl,
  FormGroup,
  FormsModule,
  ReactiveFormsModule,
  ValidationErrors,
  Validators,
} from '@angular/forms';
import { StatoConvenzioniEnum } from '@core/common/enum/convenzioni.enum';
import { FileSizePipe } from '@core/pipes';
import { UtilsService } from '@core/services/utils.service';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { faHospital } from '@fortawesome/free-regular-svg-icons';
import {
  faCity,
  faGlobeEurope,
  faMapLocationDot,
} from '@fortawesome/free-solid-svg-icons';
import { RxState } from '@rx-angular/state';
import dayjs from 'dayjs';
import saveAs from 'file-saver';
import { ConfirmationService, ToastMessageOptions } from 'primeng/api';
import { AutoCompleteModule } from 'primeng/autocomplete';
import { ButtonModule } from 'primeng/button';
import { CheckboxModule } from 'primeng/checkbox';
import { ConfirmDialogModule } from 'primeng/confirmdialog';
import { ConfirmPopupModule } from 'primeng/confirmpopup';
import { DatePickerModule } from 'primeng/datepicker';
import { DialogModule } from 'primeng/dialog';
import { DividerModule } from 'primeng/divider';
import { DropdownModule } from 'primeng/dropdown';
import { DialogService, DynamicDialogRef } from 'primeng/dynamicdialog';
import { IconFieldModule } from 'primeng/iconfield';
import { InputIconModule } from 'primeng/inputicon';
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 { Table, TableModule } from 'primeng/table';
import { TagModule } from 'primeng/tag';
import { TextareaModule } from 'primeng/textarea';
import { ToastModule } from 'primeng/toast';
import { TooltipModule } from 'primeng/tooltip';
import {
  BehaviorSubject,
  catchError,
  EMPTY,
  endWith,
  map,
  Observable,
  of,
  startWith,
  switchMap,
  tap,
} from 'rxjs';
import {
  AccountResDto,
  ConvenzioniStrutture,
  DownloadFile,
  FileDetails,
  FindStrutture,
  GenericMessageDto,
  LuogoRes,
  Stipulatore,
  Struttura,
  StrutturaMadre,
  TipoGestione,
  TipoStruttura,
} from '../../../../api/models';
import { AuthService, TableColumn } from '../../../core';
import { MyDirectiveModule } from '../../../core/directives/my-directive.module';
import { UploadDocumentazioneComponent } from '../../shared/components/upload-documentazione-cf/upload-documentazione.component';
import { DynamicDialogData } from '../../shared/utils/dynamic-dialog-utils';
import { FolderFile } from './dto/strutture.dto';
import {
  ManageStruttureDialogComponent,
  ManageStruttureDialogComponentState,
} from './manage-strutture-dialog/manage-strutture-dialog.component';
import {
  GestioneVisibilitaDialogComponent,
  GestioneVisibilitaDialogComponentState,
} from './manage-visibilita-dialog /manage-visibilita-dialog.component';
import { StruttureService } from './strutture.service';

const cercaStruttureFormGroupFunc = () => {
  return new FormGroup(
    {
      nome: new FormControl<string | null>(null),
      regione: new FormControl<string | null>(null),
      provincia: new FormControl<string | null>(null),
      localita: new FormControl<string | null>(null),
      tipologiaStruttura: new FormControl<number[] | null>(null),
      dataInizioConvenzione: new FormControl<Date[] | null>(null),
      dataFineConvenzione: new FormControl<Date[] | null>(null),
      statoConvenzione: new FormControl<StatoConvenzioniEnum>(
        StatoConvenzioniEnum.ATTIVO,
        {
          nonNullable: true,
        },
      ),
      flagOnlyStrutturaMadre: new FormControl<boolean>(false),
      luogo: new FormControl<LuogoRes | null>(null),
    },
    [atLeastOneFilterRequired],
  );
};

export type cercaStruttureForm = ReturnType<typeof cercaStruttureFormGroupFunc>;
export type cercaStruttureFormValue = cercaStruttureForm['value'];

const convenzioneFormGroupFunc = (convenzione?: ConvenzioniStrutture) => ({
  condizioniGeneraliConvenzione: new FormControl<string | null>(
    convenzione ? convenzione.condizioniGeneraliConvenzione : null,
  ),
  dataInizio: new FormControl<Date | null>(
    convenzione ? new Date(convenzione.dataInizio) : null,
    {
      validators: [Validators.required],
    },
  ),
  dataFine: new FormControl<Date | null>(
    convenzione ? new Date(convenzione.dataFine) : null,
  ),
  stipulata: new FormControl<number>(
    convenzione ? convenzione.stipulatore.id : 0,
    {
      validators: [Validators.required],
    },
  ),
  flagConvenzioneDiretta: new FormControl<boolean>(
    convenzione ? convenzione.flagConvenzioneDiretta : false,
  ),
  flagConvenzioneIndiretta: new FormControl<boolean>(
    convenzione ? convenzione.flagConvenzioneIndiretta : false,
  ),
  flagConvenzioneTicket: new FormControl<boolean>(
    convenzione ? convenzione.flagConvenzioneTicket : false,
  ),
  flagSospesa: new FormControl<boolean>(
    convenzione ? convenzione.flagSospesa : false,
  ),
  flagSceltaMedico: new FormControl<boolean>(
    convenzione ? convenzione.flagSceltaMedico : false,
  ),
  flagUsaTariffarioCasaMadre: new FormControl<boolean>(
    convenzione ? convenzione.flagUsaTariffarioCasaMadre : false,
  ),
});
export type ConvenzioneForm = FormGroup<
  ReturnType<typeof convenzioneFormGroupFunc>
>;
export type ConvenzioneFormValue = ConvenzioneForm['value'];

export interface ManageStruttureComponentState {
  strutture: StrutturaExcerptRes[];
  struttureAreLoading: boolean;
  struttureTotalCount: number;
  selectedStruttura: Struttura;
  cdIsLoading: boolean;
  convenzioniAreLoading: boolean;
  showConvenzioniDialog: boolean;
  convenzioni: ConvenzioniStrutture[];
  modalMode: string;
  modalName: string;
  selectedConvenzione: ConvenzioniStrutture;
  convenzioneFormSubmitted: boolean;
  cercaStruttureFormSubmitted: boolean;
  showStrutturaDialog: boolean;
  tipoGestione: TipoGestione[];
  tipoStruttura: TipoStruttura[];
  csvExportIsLoading: boolean;
  tipoGestioneIsLoading: boolean;
  tipoStruttureIsLoading: boolean;
  isSaving: boolean;
  stipulatori: Stipulatore[];
  stipulatoriIsLoading: boolean;

  statusConvenzioni: { label: string; value: string }[];
  statusConvenzioniAreLoading: boolean;
  tipologieStrutture: TipoStruttura[];
  tipologieStruttureAreLoading: boolean;
  isSearching: boolean;
  filteredLuoghiEsteso: LuogoRes[];

  allegati: FileDetails[];
  loadingAllegatiTable: boolean;
  showUploadDialog: boolean;
  descrizioniTipiStruttura: any[];
  account: AccountResDto;
  accountIsLoading: boolean;
}
type State = ManageStruttureComponentState;

@Component({
  selector: 'app-strutture',
  imports: [
    CommonModule,
    TableModule,
    InputTextModule,
    SkeletonModule,
    ButtonModule,
    TooltipModule,
    FormsModule,
    ReactiveFormsModule,
    MultiSelectModule,
    SelectModule,
    ConfirmDialogModule,
    IconFieldModule,
    InputIconModule,
    MessagesModule,
    TagModule,
    ConfirmPopupModule,
    ToastModule,
    DialogModule,
    DividerModule,
    TextareaModule,
    DatePickerModule,
    RatingModule,
    DropdownModule,
    CheckboxModule,
    AutoCompleteModule,
    FontAwesomeModule,
    FileSizePipe,
    UploadDocumentazioneComponent,
    MyDirectiveModule,
  ],
  providers: [RxState, DialogService, ConfirmationService],
  templateUrl: 'strutture.component.html',
  styleUrl: './strutture.component.scss',
})
export class StruttureComponent {
  model$: Observable<ManageStruttureComponentState>;

  showTransitionOptions = '500ms';

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

  minDate = new Date(1998, 0, 1);
  todayDate = new Date();

  luoghiFilter = new BehaviorSubject<string | null>(null);

  convenzioneForm = new FormGroup(convenzioneFormGroupFunc());
  cercaStruttureForm = cercaStruttureFormGroupFunc();
  private childDialogRef: DynamicDialogRef | undefined;

  struttureTable!: Table;

  @ViewChild('struttureTable') set table(table: Table) {
    if (!table || this.struttureTable === table) return;
    this.struttureTable = table;
  }

  emptyStruttura: StrutturaExcerptRes = {
    cap: null,
    codiceLuogo: null,
    id: 0,
    indirizzo: null,
    nome: '',
    numeroCivico: null,
    partitaIva: null,
    strutturaMadre: {} as StrutturaMadre,
    struttureTipiStrutture: [],
    tipoGestione: {} as TipoGestione,
    visibilitaStrutture: [],
  };

  emptyStrutturaArray = Array(10).fill(this.emptyStruttura);

  cols: TableColumn[] = [
    {
      header: 'Nome',
      field: 'nome',
      sortField: 'nome',
    },
    {
      header: 'Partita IVA',
      field: 'partitaIva',
      sortField: 'partitaIva',
    },
    {
      header: 'Indirizzo',
      field: 'indirizzo',
    },

    {
      header: 'Struttura madre',
      field: 'strutturaMadre.nome',
      sortField: 'strutturaMadre.nome',
    },

    {
      header: 'Tipo gestione',
      field: 'tipoGestione.descrizione',
      sortField: 'tipoGestione.descrizione',
    },
    {
      header: 'Tipo struttura',
      field: 'tipoStruttura.codice',
      sortField: 'tipoStruttura.codice',
    },
    {
      header: 'Azioni',
      field: 'azioni',
    },
  ];

  allegatiCols: TableColumn[] = [
    { field: 'preview', header: '' },
    { field: 'name', header: 'Nome' },
    { field: 'lastModified', header: 'Data ultima modifica' },
    { field: 'fileSizeInBytes', header: 'Dimensione' },
    { field: 'actions', header: 'Azioni' },
  ];

  defaultSort = [{ field: 'numero', order: 1 }];

  messages: ToastMessageOptions[] = [];
  filterMessage: ToastMessageOptions[] = [];

  editStrutturaIsLoading: boolean[] = [];
  dettaglioStrutturaIsLoading: boolean[] = [];
  editVisibilitaStrutturaIsLoading: boolean[] = [];
  openStrutturaIsLoading: boolean[] = [];
  convenzioniAreLoading: boolean[] = [];
  allegatiAreLoading: boolean[] = [];
  downloading: boolean[] = [];
  hasAdminRole: boolean = false;
  convenzioneStrutturaPrivilegio: boolean = false;

  constructor(
    private struttureService: StruttureService,
    private readonly dialogService: DialogService,
    private confirmationService: ConfirmationService,
    public state: RxState<State>,
    public utilsService: UtilsService,
    private authService: AuthService,
  ) {
    this.model$ = this.state.select();
    this.state.set({
      strutture: [],
      struttureAreLoading: false,
      struttureTotalCount: 0,
      selectedStruttura: undefined,
      cdIsLoading: false,
      convenzioniAreLoading: false,
      showConvenzioniDialog: false,
      convenzioni: [],
      modalMode: '',
      modalName: '',
      convenzioneFormSubmitted: false,
      cercaStruttureFormSubmitted: false,
      csvExportIsLoading: false,
      stipulatoriIsLoading: false,

      statusConvenzioniAreLoading: false,
      tipologieStruttureAreLoading: false,
      isSearching: false,

      loadingAllegatiTable: false,
      showUploadDialog: false,
      descrizioniTipiStruttura: [],
      account: undefined,
      accountIsLoading: false,
    });

    const fetchValoriStatus$ = of(EMPTY).pipe(
      switchMap(() =>
        this.struttureService.getValoriStatusConvenzioni().pipe(
          map((res) => ({
            statusConvenzioni: res,
          })),
          startWith({ statusConvenzioniAreLoading: true }),
          endWith({ statusConvenzioniAreLoading: false }),
        ),
      ),
    );
    this.state.connect(fetchValoriStatus$);

    const fetchTipologieStrutture$ = of(EMPTY).pipe(
      switchMap(() =>
        this.struttureService.getTipologieStrutture().pipe(
          map((res) => ({
            tipologieStrutture: res,
          })),
          catchError(() => of({ tipologieStrutture: [] })),
          startWith({ tipologieStruttureAreLoading: true }),
          endWith({ tipologieStruttureAreLoading: false }),
        ),
      ),
    );
    this.state.connect(fetchTipologieStrutture$);

    const fetchAccount$ = of(null).pipe(
      map(() => {
        const res = this.authService.getCurrentUser();
        const hasAdminRole =
          res?.roles?.some((role) => role.roleCode === 'ADMIN') ?? false;
        this.hasAdminRole = hasAdminRole;

        return {
          account: res ?? undefined,
        };
      }),
      catchError(() => of({ account: undefined })),
      startWith({ tipologieStruttureAreLoading: true }),
      endWith({ tipologieStruttureAreLoading: false }),
    );

    this.state.connect(fetchAccount$);

    this.getTipiGestione();
    this.getTipiStrutture();
    this.getStipulatori();
  }

  getStipulatori() {
    const stipulatori$ = this.struttureService.getStipulatori().pipe(
      map((res) => ({ stipulatori: res })),
      catchError(() => {
        this.messages = [
          {
            severity: 'error',
            life: 5000,
            summary: 'Attenzione!',
            detail:
              'Impossibile recuperare la lista di stipulatori al momento, riprova più tardi',
          },
        ];
        return of({});
      }),
      startWith({ stipulatoriIsLoading: true }),
      endWith({ stipulatoriIsLoading: false }),
    );
    this.state.connect(stipulatori$);
  }

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

  getTipiGestione() {
    const tipiGestione$ = this.struttureService.getTipoGestione().pipe(
      map((res) => ({ tipoGestione: res.rows })),
      catchError(() => {
        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$);
  }

  getTipiStrutture() {
    const tipiStrutture$ = this.struttureService.getTipoStrutture().pipe(
      map((res) => ({ tipoStruttura: res.rows })),
      catchError(() => {
        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$);
  }

  createStruttura() {
    this.childDialogRef = this.dialogService.open(
      ManageStruttureDialogComponent,
      {
        header: 'Crea struttura',
        modal: true,
        closable: true,
        focusOnShow: false,
        style: { width: '120vw', maxWidth: '800px', minWidth: '400px' },
        data: {
          isLoading: false,
          dialogComponentName: ManageStruttureDialogComponent.name,
        } satisfies DynamicDialogData<ManageStruttureDialogComponentState>,
      },
    );

    this.childDialogRef?.onClose.subscribe((struttura: Struttura) => {
      if (!struttura) return;
      this.openConvenzioniDialog(struttura, -1);
      this.messages = [
        {
          severity: 'success',
          life: 5000,
          summary: 'Operazione completata',
          detail: 'Struttura creato con successo',
        },
      ];
    });
  }

  resetFilters() {
    this.cercaStruttureForm.reset();
  }

  search() {
    if (this.cercaStruttureForm.invalid) {
      this.filterMessage = [
        {
          severity: 'error',
          life: 5000,
          summary: 'Attenzione!',
          detail: 'Inserire almeno un filtro.',
        },
      ];
      return;
    }
    this.state.set({
      cercaStruttureFormSubmitted: true,
    });
    const _form = this.cercaStruttureForm.getRawValue();
    this.cercaStruttureForm.disable();
    const param: FindStrutture = {
      filters: {
        flagOnlyStrutturaMadre: false,
        nome: _form.nome,
        idTipoStruttura: _form.tipologiaStruttura,
        dataInizio: _form.dataInizioConvenzione?.map((x) =>
          dayjs(x).toISOString(),
        ),
        dataFine: _form.dataFineConvenzione?.map((x) => dayjs(x).toISOString()),
        statoConvenzione: _form.statoConvenzione,
        ...(_form.luogo?.luogo && _form.luogo?.tipo
          ? {
              luogo: {
                luogo: _form.luogo?.luogo,
                tipo: _form.luogo?.tipo,
              },
            }
          : {}),
      },
    };
    const strutture$ = this.struttureService.getStrutture(param).pipe(
      map((res) => {
        const strutture = res.rows.map((row: any) => ({
          ...row,
          descrizioniTipiStruttura:
            row.struttureTipiStrutture
              ?.map((s: any) => s.tipo.descrizione)
              .join(', ') || '',
        }));

        return {
          strutture,
          struttureTotalCount: strutture.length,
        };
      }),
      tap(() => {
        this.cercaStruttureForm.enable();
      }),
      catchError(() => {
        this.messages = [
          {
            severity: 'error',
            life: 5000,
            summary: 'Attenzione!',
            detail:
              'Impossibile recuperare la lista di strutture al momento, riprova più tardi',
          },
        ];
        this.cercaStruttureForm.enable();
        return of({
          strutture: [],
          struttureTotalCount: 0,
        });
      }),
      startWith({ struttureAreLoading: true, isSearching: true }),
      endWith({ struttureAreLoading: false, isSearching: false }),
    );

    this.state.connect(strutture$);
  }

  editStruttura(struttura: StrutturaExcerptRes, rowIndex: number) {
    this.editStrutturaIsLoading[rowIndex] = true;
    const fetchUserById$ = of(EMPTY).pipe(
      switchMap(() =>
        this.struttureService.getStrutturaById(struttura.id).pipe(
          catchError(() => of(null)),
          tap((res) => {
            this.editStrutturaIsLoading[rowIndex] = false;
            if (!res) return;
            this.childDialogRef = this.dialogService.open(
              ManageStruttureDialogComponent,
              {
                header: 'Modifica struttura ' + struttura.nome,
                modal: true,
                closable: true,
                focusOnShow: false,
                style: { width: '90vw', maxWidth: '800px', minWidth: '400px' },
                data: {
                  selectedStruttura: res,
                  dialogComponentName: ManageStruttureDialogComponent.name,
                } satisfies DynamicDialogData<ManageStruttureDialogComponentState>,
              },
            );

            this.childDialogRef?.onClose.subscribe((result: Struttura) => {
              if (!result) return;

              this.messages = [
                {
                  severity: 'success',
                  summary: 'Operazione completata',
                  life: 5000,
                  detail: 'Struttura modificata con successo',
                },
              ];

              this.search();
            });
          }),
          map(() => ({})),
        ),
      ),
    );
    this.state.connect(fetchUserById$);
  }

  editVisibilitaStruttura(struttura: StrutturaExcerptRes, rowIndex: number) {
    this.editVisibilitaStrutturaIsLoading[rowIndex] = true;

    const fetchStrutturaById$ = of(EMPTY).pipe(
      switchMap(() =>
        this.struttureService.getStrutturaById(struttura.id).pipe(
          catchError(() => of(null)),
          tap((res) => {
            this.editVisibilitaStrutturaIsLoading[rowIndex] = false;
            if (!res) return;

            this.childDialogRef = this.dialogService.open(
              GestioneVisibilitaDialogComponent,
              {
                header: 'Gestione visibilità struttura ' + struttura.nome,
                modal: true,
                closable: true,
                focusOnShow: false,
                style: { width: '90vw', maxWidth: '800px', minWidth: '400px' },
                data: {
                  selectedStruttura: res,
                  dialogComponentName: GestioneVisibilitaDialogComponent.name,
                } satisfies DynamicDialogData<GestioneVisibilitaDialogComponentState>,
              },
            );

            this.childDialogRef?.onClose.subscribe((result: string) => {
              if (!result) return;
              this.messages = [
                {
                  severity: 'success',
                  summary: 'Visibilità aggiornata',
                  life: 5000,
                  detail:
                    'La visibilità della struttura è stata modificata con successo',
                },
              ];
            });
          }),
          map(() => ({})),
        ),
      ),
    );

    this.state.connect(fetchStrutturaById$);
  }

  documentazioneUploaded(res: GenericMessageDto) {
    this.utilsService.showAlert(
      'success',
      'Operazione completata',
      res.message,
    );
    this.visualizzaAllegatiConvenzione(
      this.state.get('selectedConvenzione'),
      0,
    );
    this.closeDialog();
  }

  closeDialog() {
    this.state.set({
      showUploadDialog: false,
    });
  }

  openConvenzioniDialog(struttura: Struttura, rowIndex: number) {
    this.convenzioneStrutturaPrivilegio =
      struttura.visibilitaStrutture[0].flagProprietario;
    if (rowIndex !== -1) this.convenzioniAreLoading[rowIndex] = true;
    const fetchPrivilegiById$ = of(EMPTY).pipe(
      switchMap(() =>
        this.struttureService.getConvenzioniByStrutturaId(struttura.id).pipe(
          catchError(() => of(null)),
          tap((res) => {
            if (!res) return;
            this.state.set({
              selectedStruttura: struttura,
              showConvenzioniDialog: true,
              convenzioni: res.map((x) => ({
                ...x,
                dataInizio: new Date(x.dataInizio).toISOString(),
                dataFine: new Date(x.dataFine).toISOString(),
              })),
              modalMode: 'show',
              modalName: 'Convenzioni per la struttura ' + struttura?.nome,
            });
            if (rowIndex !== -1) this.convenzioniAreLoading[rowIndex] = false;
          }),
          map(() => ({})),
          startWith({ convenzioniAreLoading: true }),
          endWith({ convenzioniAreLoading: false }),
        ),
      ),
    );
    this.state.connect(fetchPrivilegiById$);
  }

  exportCSV() {
    this.state.set({
      csvExportIsLoading: true,
    });
    const exportCSV$ = this.struttureService.exportCSV().pipe(
      catchError(() => {
        this.messages = [
          {
            severity: 'error',
            life: 5000,
            summary: 'Attenzione!',
            detail:
              'Impossibile esportare il csv delle strutture al momento, riprova più tardi',
          },
        ];
        return of(null);
      }),
      tap((blob) => {
        if (!blob) return;
        const timestamp = dayjs().format('YYYYMMDD_HHmmss');
        const fileName = `nomenclatore_export_${timestamp}.csv`;
        saveAs(blob, fileName);
        this.state.set({
          csvExportIsLoading: false,
        });
      }),
    );
    exportCSV$.subscribe();
  }

  onCloseConvenzioniDialog(event: Event) {
    this.state.set({
      modalMode: '',
      selectedConvenzione: undefined,
      modalName: '',
      convenzioneFormSubmitted: false,
    });
  }

  onBackDialog(event: Event) {
    if (
      this.state.get('modalMode') === 'edit' ||
      this.state.get('modalMode') === 'allegati'
    ) {
      this.state.set({ modalMode: 'show' });
    } else {
      this.state.set({ showConvenzioniDialog: false });
      this.onCloseConvenzioniDialog(event);
    }
  }

  editConvenzione(convenzione: ConvenzioniStrutture) {
    this.convenzioneForm.patchValue(
      new FormGroup(convenzioneFormGroupFunc(convenzione)).value,
    );
    this.state.set({
      modalMode: 'edit',
      selectedConvenzione: convenzione,
      modalName: 'Modifica convenzione',
    });
  }

  addConvenzione() {
    this.convenzioneForm.patchValue(
      new FormGroup(convenzioneFormGroupFunc()).value,
    );
    this.state.set({
      modalMode: 'edit',
      selectedConvenzione: undefined,
      modalName: 'Aggiungi convenzione',
    });
  }

  visualizzaAllegatiConvenzione(
    convenzione: ConvenzioniStrutture,
    rowIndex: number,
  ) {
    this.allegatiAreLoading[rowIndex] = true;
    this.state.set({
      selectedConvenzione: convenzione,
    });
    const fetchAllegati$ = of(EMPTY).pipe(
      switchMap(() =>
        this.struttureService
          .getAllegatiByConvenzioneId(
            this.state.get('selectedStruttura').id,
            convenzione.id,
          )
          .pipe(
            catchError(() => of(null)),
            tap((res) => {
              if (!res) return;
              this.state.set({
                modalMode: 'allegati',
                allegati: res,
                modalName: 'Allegati della convenzione',
              });
              this.convenzioniAreLoading[rowIndex] = false;
            }),
            map(() => ({})),
            startWith({ loadingAllegatiTable: true }),
            endWith({ loadingAllegatiTable: false }),
          ),
      ),
    );
    this.state.connect(fetchAllegati$);
  }

  showDeleteDialog(event: Event, file: FileDetails): void {
    this.confirmationService.confirm({
      target: event.target as EventTarget,
      key: 'cd',
      header: 'Elimina allegato',
      icon: 'pi pi-exclamation-triangle',
      message: 'Sei sicuro di voler eliminare questo allegato?',
      acceptLabel: 'Sì, elimina',
      acceptButtonStyleClass: 'p-button-rounded p-button-danger',
      rejectButtonStyleClass: 'p-button-rounded p-button-outlined',
      acceptIcon: 'pi pi-trash',
      rejectLabel: 'Annulla',
      accept: () => {
        const params = {
          id: this.state.get('selectedStruttura')?.id,
          idConvenzione: this.state.get('selectedConvenzione').id,
          fileName: file.name,
        };
        const saveDeleteChannel$ = this.struttureService
          .deleteAllegato(params)
          .pipe(
            catchError(() => {
              this.utilsService.showAlert(
                'error',
                'Qualcosa è andato storto',
                `Impossibile eliminare l'allegato al momento`,
              );
              return of(null);
            }),
            tap((res) => {
              if (!res) return;
              this.utilsService.showAlert(
                'success',
                'Operazione completata',
                'Allegato eliminato con successo',
              );
              this.visualizzaAllegatiConvenzione(
                this.state.get('selectedConvenzione'),
                0,
              );
            }),
            map(() => ({})),
            startWith({ cdIsLoading: true }),
            endWith({ cdIsLoading: false }),
          );
        this.state.connect(saveDeleteChannel$);
      },
    });
  }

  submitConvenzioneForm() {
    this.state.set({
      convenzioneFormSubmitted: true,
    });
    if (this.convenzioneForm.invalid) return;
    const _form = this.convenzioneForm.getRawValue();
    const params = {
      condizioniGeneraliConvenzione:
        _form.condizioniGeneraliConvenzione ?? null,
      dataInizio: dayjs(_form.dataInizio).toISOString(),
      dataFine: _form.dataFine ? dayjs(_form.dataFine).toISOString() : null,
      flagConvenzioneDiretta: _form.flagConvenzioneDiretta ?? false,
      flagConvenzioneIndiretta: _form.flagConvenzioneIndiretta ?? false,
      flagConvenzioneTicket: _form.flagConvenzioneTicket ?? false,
      flagSospesa: _form.flagSospesa ?? false,
      flagSceltaMedico: _form.flagSceltaMedico ?? false,
      flagUsaTariffarioCasaMadre: _form.flagUsaTariffarioCasaMadre ?? false,
      idStipulatore: _form.stipulata ?? 0,
    };

    const saveAddAutore$ = (
      !this.state.get('selectedConvenzione')
        ? this.struttureService.createConvenzione(
            this.state.get('selectedStruttura')?.id ?? 0,
            params,
          )
        : this.struttureService.editConvenzione(
            params,
            this.state.get('selectedStruttura')?.id ?? 0,
            this.state.get('selectedConvenzione').id,
          )
    ).pipe(
      catchError(() => of(null)),
      tap((res) => {
        if (!res) return;
        // this.getStrutture(this.state.get('currentFilter')!);

        this.openConvenzioniDialog(this.state.get('selectedStruttura'), 0);
        this.state.set(() => ({
          convenzioneFormSubmitted: false,
          modalMode: 'show',
        }));
      }),
      map(() => ({})),
      startWith({ isSaving: true }),
      endWith({ isSaving: false }),
    );
    this.state.connect(saveAddAutore$);
  }

  openStrutturaDialog(idStruttura: number, rowIndex: number) {
    this.dettaglioStrutturaIsLoading[rowIndex] = true;
    const fetchStrutturaById$ = of(EMPTY).pipe(
      switchMap(() =>
        this.struttureService.getStrutturaById(idStruttura).pipe(
          catchError(() => of(null)),
          tap((res) => {
            if (!res) return;
            this.state.set({
              showStrutturaDialog: true,
              selectedStruttura: res,
            });
            this.dettaglioStrutturaIsLoading[rowIndex] = false;
          }),
          map(() => ({})),
        ),
      ),
    );
    this.state.connect(fetchStrutturaById$);
  }

  openUploadDialog() {
    this.state.set({
      showUploadDialog: true,
    });
  }

  getFormattedAddress(row: Struttura): string {
    return [
      row.indirizzo,
      row.cap,
      row.codiceLuogo?.comune,
      row.codiceLuogo?.siglaProvincia,
      row.codiceLuogo?.regione,
      row.codiceLuogo?.stato,
    ]
      .filter((field) => field)
      .join(', ');
  }

  downloadFile(file: FolderFile, rowIndex: number) {
    this.downloading[rowIndex] = true;
    const fetchAllegati$ = of(EMPTY).pipe(
      switchMap(() =>
        this.struttureService
          .downloadAllegato(
            this.state.get('selectedStruttura').id,
            this.state.get('selectedConvenzione').id,
            file.name,
          )
          .pipe(
            catchError(() => of({} as DownloadFile)),
            tap((res: any) => {
              if (!res) return;
              this.downloading[rowIndex] = false;
              if (res.buffer) {
                const myArr = new Uint8Array(res.buffer.data);
                const blob = new Blob([myArr], {
                  type: 'application/octet-stream',
                });
                saveAs(blob, file.name);
              } else {
                this.utilsService.showAlert(
                  'error',
                  'Ops! qualcosa è andato storto',
                  'Impossibile scaricare il file richiesto, riprova più tardi!',
                );
              }
            }),
            map(() => ({})),
          ),
      ),
    );
    this.state.connect(fetchAllegati$);
  }
}

export function atLeastOneFilterRequired(
  control: AbstractControl,
): ValidationErrors | null {
  if (control instanceof FormGroup) {
    const groupAndOrderFields = ['flagOnlyStrutturaMadre'];

    const controls = Object.entries(control.controls)
      .filter(([key]) => !groupAndOrderFields.includes(key))
      .map(([, ctrl]) => ctrl);

    const filtersHaveOneValue = controls.some(
      (ctrl) =>
        ctrl.value != null &&
        ctrl.value !== '' &&
        !(Array.isArray(ctrl.value) && ctrl.value.length === 0),
    );

    return filtersHaveOneValue ? null : { atLeastOneRequired: true };
  }

  return null;
}
