import { Injectable, inject } from '@angular/core';
import type {
  CreateNomenclatoreLivello1,
  CreateNomenclatoreLivello2,
  CreateNomenclatoreLivello3,
  CreateNomenclatoreLivello4,
  NomenclatoreExcerptRes,
  NomenclatoreLivello1ResDto,
  NomenclatoreLivello2ResDto,
  NomenclatoreLivello3ResDto,
  NomenclatoreLivello4ResDto,
  NomenclatoreRes,
  UpdateNomenclatoreLivello1,
  UpdateNomenclatoreLivello2,
  UpdateNomenclatoreLivello3,
  UpdateNomenclatoreLivello4,
} from '@api/models';
import { NomenclatoreApiClient } from '@api/services';
import { sortBy } from 'lodash';
import type { TreeNode } from 'primeng/api';
import { MessageService } from 'primeng/api';
import {
  Observable,
  ReplaySubject,
  catchError,
  map,
  of,
  shareReplay,
  switchMap,
  tap,
  throwError,
} from 'rxjs';

export type NomenclatoreExcerpt = NomenclatoreExcerptRes;
export type Nomenclatore = NomenclatoreRes;
export type NomenclatoreLiv1 = NomenclatoreLivello1ResDto;
export type NomenclatoreLiv2 = NomenclatoreLivello2ResDto;
export type NomenclatoreLiv3 = NomenclatoreLivello3ResDto;
export type NomenclatoreLiv4 = NomenclatoreLivello4ResDto;

export type NomenclatoreNodeType = 'voce nomenclatore' | 'livello';
export type NomenclatoreTreeNodeData = {
  id: number;
  codice: string;
  descrizione: string;
  idLivello1: number | null;
  idLivello2: number | null;
  idLivello3: number | null;
  idLivello4: number | null;
  type: NomenclatoreNodeType;
};

@Injectable({ providedIn: 'root' })
export class NomenclatoreService {
  nomenclatoreApiClient = inject(NomenclatoreApiClient);
  messageService = inject(MessageService);

  readonly refresh$ = new ReplaySubject<void>(1);
  readonly refreshTree$ = new ReplaySubject<void>(1);

  nomenclatoreExcerpt$ = this.refreshTree$.pipe(
    switchMap(() => this.findManyNomenclatore()),
    shareReplay(1),
  );

  nomenclatoreLivello1$ = this.refreshTree$.pipe(
    switchMap(() => this.findManyLivello1Nomenclatore()),
    map((values) => sortBy(values, (item) => (item.id <= 0 ? -1 : 1))),
    shareReplay(1),
  );

  nomenclatoreLivello2$ = this.refreshTree$.pipe(
    switchMap(() => this.findManyLivello2Nomenclatore()),
    map((values) => sortBy(values, (item) => (item.id <= 0 ? -1 : 1))),
    shareReplay(1),
  );

  nomenclatoreLivello3$ = this.refreshTree$.pipe(
    switchMap(() => this.findManyLivello3Nomenclatore()),
    map((values) => sortBy(values, (item) => (item.id <= 0 ? -1 : 1))),
    shareReplay(1),
  );

  nomenclatoreLivello4$ = this.refreshTree$.pipe(
    switchMap(() => this.findManyLivello4Nomenclatore()),
    map((values) => sortBy(values, (item) => (item.id <= 0 ? -1 : 1))),
    shareReplay(1),
  );

  categoriePrestazione$ = this.refresh$.pipe(
    switchMap(() => this.findManyCategoriePrestazione()),
    map((values) => sortBy(values, (item) => (item.id <= 0 ? -1 : 1))),
    shareReplay(1),
  );

  elementi$ = this.refresh$.pipe(
    switchMap(() => this.findManyElementi()),
    map((values) => sortBy(values, (item) => (item.id <= 0 ? -1 : 1))),
    shareReplay(1),
  );

  tipiPrestazione$ = this.refresh$.pipe(
    switchMap(() => this.findManyTipiPrestazione()),
    map((values) => sortBy(values, (item) => (item.id <= 0 ? -1 : 1))),
    shareReplay(1),
  );

  tipiStruttura$ = this.refresh$.pipe(
    switchMap(() => this.findManyTipiStruttura()),
    map((values) => sortBy(values, (item) => (item.id <= 0 ? -1 : 1))),
    shareReplay(1),
  );

  unitaDiMisura$ = this.refresh$.pipe(
    switchMap(() => this.findManyUnitaDiMisura()),
    map((values) => sortBy(values, (item) => (item.id <= 0 ? -1 : 1))),
    shareReplay(1),
  );

  refresh(opts?: { treeOnly: boolean }) {
    this.refreshTree$.next();
    if (!opts?.treeOnly) {
      this.refresh$.next();
    }
  }

  getOneNomenclatoreById(params: { id: number }) {
    return this.nomenclatoreApiClient
      .nomenclatoreControllerGetOneNomenclatoreById(params)
      .pipe(
        catchError((err: unknown) => {
          this.messageService.add({
            severity: 'error',
            summary: 'Oops, qualcosa è andato storto!',
            detail: `${err}`,
          });
          return throwError(() => err);
        }),
      );
  }

  exportCSV() {
    return this.nomenclatoreApiClient
      .nomenclatoreControllerExportNomenclatore()
      .pipe(
        catchError((err: unknown) => {
          this.messageService.add({
            severity: 'error',
            summary: 'Oops, qualcosa è andato storto!',
            detail: `${err}`,
          });
          return throwError(() => err);
        }),
      );
  }

  findManyNomenclatore() {
    return this.nomenclatoreApiClient
      .nomenclatoreControllerFindManyNomenclatore()
      .pipe(
        catchError((err: unknown) => {
          this.messageService.add({
            severity: 'error',
            summary: 'Oops, qualcosa è andato storto!',
            detail: `${err}`,
          });
          return throwError(() => err);
        }),
      );
  }

  findManyLivello1Nomenclatore() {
    return this.nomenclatoreApiClient
      .nomenclatoreControllerFindManyLivello1Nomenclatore()
      .pipe(
        catchError((err: unknown) => {
          this.messageService.add({
            severity: 'error',
            summary: 'Oops, qualcosa è andato storto!',
            detail: `${err}`,
          });
          return throwError(() => err);
        }),
      );
  }

  findManyLivello2Nomenclatore() {
    return this.nomenclatoreApiClient
      .nomenclatoreControllerFindManyLivello2Nomenclatore()
      .pipe(
        catchError((err: unknown) => {
          this.messageService.add({
            severity: 'error',
            summary: 'Oops, qualcosa è andato storto!',
            detail: `${err}`,
          });
          return throwError(() => err);
        }),
      );
  }

  findManyLivello3Nomenclatore() {
    return this.nomenclatoreApiClient
      .nomenclatoreControllerFindManyLivello3Nomenclatore()
      .pipe(
        catchError((err: unknown) => {
          this.messageService.add({
            severity: 'error',
            summary: 'Oops, qualcosa è andato storto!',
            detail: `${err}`,
          });
          return throwError(() => err);
        }),
      );
  }

  findManyLivello4Nomenclatore() {
    return this.nomenclatoreApiClient
      .nomenclatoreControllerFindManyLivello4Nomenclatore()
      .pipe(
        catchError((err: unknown) => {
          this.messageService.add({
            severity: 'error',
            summary: 'Oops, qualcosa è andato storto!',
            detail: `${err}`,
          });
          return throwError(() => err);
        }),
      );
  }

  findManyCategoriePrestazione() {
    return this.nomenclatoreApiClient
      .nomenclatoreControllerFindManyCategoriePrestazione()
      .pipe(
        catchError((err: unknown) => {
          this.messageService.add({
            severity: 'error',
            summary: 'Oops, qualcosa è andato storto!',
            detail: `${err}`,
          });
          return throwError(() => err);
        }),
      );
  }

  findManyElementi() {
    return this.nomenclatoreApiClient
      .nomenclatoreControllerFindManyElementi()
      .pipe(
        catchError((err: unknown) => {
          this.messageService.add({
            severity: 'error',
            summary: 'Oops, qualcosa è andato storto!',
            detail: `${err}`,
          });
          return throwError(() => err);
        }),
      );
  }

  findManyTipiPrestazione() {
    return this.nomenclatoreApiClient
      .nomenclatoreControllerFindManyTipiPrestazione()
      .pipe(
        catchError((err: unknown) => {
          this.messageService.add({
            severity: 'error',
            summary: 'Oops, qualcosa è andato storto!',
            detail: `${err}`,
          });
          return throwError(() => err);
        }),
      );
  }

  findManyTipiStruttura() {
    return this.nomenclatoreApiClient
      .nomenclatoreControllerFindManyTipiStruttura()
      .pipe(
        catchError((err: unknown) => {
          this.messageService.add({
            severity: 'error',
            summary: 'Oops, qualcosa è andato storto!',
            detail: `${err}`,
          });
          return throwError(() => err);
        }),
      );
    return of([] as { id: number }[]);
  }

  findManyUnitaDiMisura() {
    return this.nomenclatoreApiClient
      .nomenclatoreControllerFindManyUnitaDiMisura()
      .pipe(
        catchError((err: unknown) => {
          this.messageService.add({
            severity: 'error',
            summary: 'Oops, qualcosa è andato storto!',
            detail: `${err}`,
          });
          return throwError(() => err);
        }),
      );
  }

  createNomenclatore(dto: any) {
    return this.nomenclatoreApiClient
      .nomenclatoreControllerCreateNomenclatore({ body: dto })
      .pipe(
        tap((res) => {
          this.messageService.add({
            severity: 'success',
            summary: 'Modifiche effettuate',
            detail: 'Voce nomenclatore creata con successo',
          });
        }),
        catchError((err: unknown) => {
          this.messageService.add({
            severity: 'error',
            summary: 'Oops, qualcosa è andato storto!',
            detail: `${err}`,
          });
          return throwError(() => err);
        }),
      );
  }

  updateNomenclatore(id: number, dto: any) {
    return this.nomenclatoreApiClient
      .nomenclatoreControllerUpdateNomenclatore({ id, body: dto })
      .pipe(
        tap((res) => {
          this.messageService.add({
            severity: 'success',
            summary: 'Modifiche effettuate',
            detail: 'Voce nomenclatore modificata con successo',
          });
        }),
        catchError((err: unknown) => {
          this.messageService.add({
            severity: 'error',
            summary: 'Oops, qualcosa è andato storto!',
            detail: `${err}`,
          });
          return throwError(() => err);
        }),
      );
  }

  createNomenclatoreLivello(
    data:
      | { livello: 1; dto: CreateNomenclatoreLivello1 }
      | { livello: 2; dto: CreateNomenclatoreLivello2 }
      | { livello: 3; dto: CreateNomenclatoreLivello3 }
      | { livello: 4; dto: CreateNomenclatoreLivello4 },
  ) {
    const { livello, dto } = data;
    const getObs = (): Observable<
      | CreateNomenclatoreLivello1
      | CreateNomenclatoreLivello2
      | CreateNomenclatoreLivello3
      | CreateNomenclatoreLivello4
    > => {
      switch (livello) {
        case 1:
          return this.nomenclatoreApiClient.nomenclatoreControllerCreateNomenclatoreLivello1(
            { body: dto },
          );
        case 2:
          return this.nomenclatoreApiClient.nomenclatoreControllerCreateNomenclatoreLivello2(
            { body: dto },
          );
        case 3:
          return this.nomenclatoreApiClient.nomenclatoreControllerCreateNomenclatoreLivello3(
            { body: dto },
          );
        case 4:
          return this.nomenclatoreApiClient.nomenclatoreControllerCreateNomenclatoreLivello4(
            { body: dto },
          );
        default:
          throw Error();
      }
    };
    return getObs().pipe(
      tap((res) => {
        this.messageService.add({
          severity: 'success',
          summary: 'Modifiche effettuate',
          detail: `Voce livello ${livello} nomenclatore creata con successo`,
        });
      }),
      catchError((err: unknown) => {
        this.messageService.add({
          severity: 'error',
          summary: 'Oops, qualcosa è andato storto!',
          detail: `${err}`,
        });
        return throwError(() => err);
      }),
    );
  }

  updateNomenclatoreLivello(
    id: number,
    data:
      | { livello: 1; dto: UpdateNomenclatoreLivello1 }
      | { livello: 2; dto: UpdateNomenclatoreLivello2 }
      | { livello: 3; dto: UpdateNomenclatoreLivello3 }
      | { livello: 4; dto: UpdateNomenclatoreLivello4 },
  ) {
    const { livello, dto } = data;
    const getObs = (): Observable<
      | UpdateNomenclatoreLivello1
      | UpdateNomenclatoreLivello2
      | UpdateNomenclatoreLivello3
      | UpdateNomenclatoreLivello4
    > => {
      switch (livello) {
        case 1:
          return this.nomenclatoreApiClient.nomenclatoreControllerUpdateNomenclatoreLivello1(
            {
              id,
              body: dto,
            },
          );
        case 2:
          return this.nomenclatoreApiClient.nomenclatoreControllerUpdateNomenclatoreLivello2(
            {
              id,
              body: dto,
            },
          );
        case 3:
          return this.nomenclatoreApiClient.nomenclatoreControllerUpdateNomenclatoreLivello3(
            {
              id,
              body: dto,
            },
          );
        case 4:
          return this.nomenclatoreApiClient.nomenclatoreControllerUpdateNomenclatoreLivello4(
            {
              id,
              body: dto,
            },
          );
        default:
          throw Error();
      }
    };
    return getObs().pipe(
      tap((res) => {
        this.messageService.add({
          severity: 'success',
          summary: 'Modifiche effettuate',
          detail: `Voce livello ${livello} nomenclatore modificata con successo`,
        });
      }),
      catchError((err: unknown) => {
        this.messageService.add({
          severity: 'error',
          summary: 'Oops, qualcosa è andato storto!',
          detail: `${err}`,
        });
        return throwError(() => err);
      }),
    );
  }
}

export function buildNomenclatoreTree(
  nomenclatoreLiv1: NomenclatoreLiv1[],
  nomenclatoreLiv2: NomenclatoreLiv2[],
  nomenclatoreLiv3: NomenclatoreLiv3[],
  nomenclatoreLiv4: NomenclatoreLiv4[],
  nomenclatore: NomenclatoreExcerpt[],
): TreeNode<NomenclatoreTreeNodeData>[] {
  const tassonomia = nomenclatoreLiv1
    .filter((l1) => l1.id > 0)
    .map((l1) => ({
      data: {
        type: 'livello' as NomenclatoreNodeType,
        ...l1,
        idLivello1: 0,
        idLivello2: 0,
        idLivello3: 0,
        idLivello4: 0,
      },
      children: [
        ...nomenclatoreLiv2
          .filter((l2) => l2.idLivello1 === l1.id)
          .map((l2) => ({
            data: {
              type: 'livello' as NomenclatoreNodeType,
              ...l2,
              idLivello1: l1.id,
              idLivello2: 0,
              idLivello3: 0,
              idLivello4: 0,
            },
            children: [
              ...nomenclatoreLiv3
                .filter((l3) => l3.idLivello2 === l2.id)
                .map((l3) => ({
                  data: {
                    type: 'livello' as NomenclatoreNodeType,
                    ...l3,
                    idLivello1: l1.id,
                    idLivello2: l2.id,
                    idLivello3: 0,
                    idLivello4: 0,
                  },
                  children: [
                    ...nomenclatoreLiv4
                      .filter((l4) => l4.idLivello3 === l3.id)
                      .map((l4) => ({
                        data: {
                          type: 'livello' as NomenclatoreNodeType,
                          ...l4,
                          idLivello1: l1.id,
                          idLivello2: l2.id,
                          idLivello3: l3.id,
                          idLivello4: 0,
                        },
                        children: nomenclatore
                          .filter(
                            (n) =>
                              n.idLivello1 === l1.id &&
                              n.idLivello2 === l2.id &&
                              n.idLivello3 === l3.id &&
                              n.idLivello4 === l4.id,
                          )
                          .map((n) => ({
                            data: {
                              type: 'voce nomenclatore' as NomenclatoreNodeType,
                              ...n,
                            },
                          })),
                      })),
                    ...nomenclatore
                      .filter(
                        (n) =>
                          n.idLivello1 === l1.id &&
                          n.idLivello2 === l2.id &&
                          n.idLivello3 === l3.id &&
                          n.idLivello4 === 0,
                      )
                      .map((n) => ({
                        data: {
                          type: 'voce nomenclatore' as NomenclatoreNodeType,
                          ...n,
                        },
                      })),
                  ],
                })),
              ...nomenclatore
                .filter(
                  (n) =>
                    n.idLivello1 === l1.id &&
                    n.idLivello2 === l2.id &&
                    n.idLivello3 === 0 &&
                    n.idLivello4 === 0,
                )
                .map((n) => ({
                  data: {
                    type: 'voce nomenclatore' as NomenclatoreNodeType,
                    ...n,
                  },
                })),
            ],
          })),
        ...nomenclatore
          .filter(
            (n) =>
              n.idLivello1 === l1.id &&
              n.idLivello2 === 0 &&
              n.idLivello3 === 0 &&
              n.idLivello4 === 0,
          )
          .map((n) => ({
            data: { type: 'voce nomenclatore' as NomenclatoreNodeType, ...n },
          })),
      ],
    }));

  const fuoriTassonomia = nomenclatore
    .filter(
      (n) =>
        n.idLivello1 === 0 &&
        n.idLivello2 === 0 &&
        n.idLivello3 === 0 &&
        n.idLivello4 === 0,
    )
    .map((n) => ({
      data: { type: 'voce nomenclatore' as NomenclatoreNodeType, ...n },
    }));

  return [...tassonomia, ...fuoriTassonomia];
}
