import { CommonModule } from '@angular/common';
import {
  Component,
  ViewChild,
  inject,
  signal,
  type OnInit,
} from '@angular/core';
import { toObservable } from '@angular/core/rxjs-interop';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MyDirectiveModule } from '@core/directives/my-directive.module';
import { HightlightPipe } from '@core/pipes';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { cloneDeep, identity } from 'lodash';
import { ToastMessageOptions, type TreeNode } from 'primeng/api';
import { ButtonModule } from 'primeng/button';
import { CardModule } from 'primeng/card';
import { DialogModule } from 'primeng/dialog';
import { DropdownModule } from 'primeng/dropdown';
import { InputGroupModule } from 'primeng/inputgroup';
import { InputTextModule } from 'primeng/inputtext';
import { SplitButtonModule } from 'primeng/splitbutton';
import { TreeModule } from 'primeng/tree';
import { TreeTableModule, type TreeTable } from 'primeng/treetable';
import {
  catchError,
  combineLatest,
  debounceTime,
  distinctUntilKeyChanged,
  filter,
  lastValueFrom,
  map,
  of,
  switchMap,
  tap,
} from 'rxjs';
// import {
//   NomenclatoreDialogComponent,
//   type NomenclatoreDialogEntry,
//   type NomenclatoreDialogType,
// } from './nomenclatore-dialog.component.js';
import dayjs from 'dayjs';
import { saveAs } from 'file-saver';
import { InputGroupAddonModule } from 'primeng/inputgroupaddon';
import { MessagesModule } from 'primeng/messages';
import {
  NomenclatoreDialogComponent,
  NomenclatoreDialogEntry,
  NomenclatoreDialogType,
} from './nomenclatore-dialog/nomenclatore-dialog.component.js';
import type { NomenclatoreTreeNodeData } from './nomenclatore.service.js';
import {
  NomenclatoreService,
  buildNomenclatoreTree,
} from './nomenclatore.service.js';

@UntilDestroy()
@Component({
  selector: 'app-nomenclatore',
  imports: [
    CommonModule,
    CardModule,
    FormsModule,
    ButtonModule,
    SplitButtonModule,
    InputTextModule,
    InputGroupModule,
    TreeModule,
    TreeTableModule,
    HightlightPipe,
    DialogModule,
    DropdownModule,
    ReactiveFormsModule,
    NomenclatoreDialogComponent,
    InputGroupAddonModule,
    MessagesModule,
    MyDirectiveModule,
  ],
  providers: [],
  templateUrl: './nomenclatore.component.html',
  styleUrl: './nomenclatore.component.scss',
})
export class NomenclatoreComponent implements OnInit {
  nomenclatoreService = inject(NomenclatoreService);

  nomenclatoreLiv1$ = this.nomenclatoreService.nomenclatoreLivello1$;
  nomenclatoreLiv2$ = this.nomenclatoreService.nomenclatoreLivello2$;
  nomenclatoreLiv3$ = this.nomenclatoreService.nomenclatoreLivello3$;
  nomenclatoreLiv4$ = this.nomenclatoreService.nomenclatoreLivello4$;
  nomenclatore$ = this.nomenclatoreService.nomenclatoreExcerpt$;

  messages: ToastMessageOptions[] = [];
  showTransitionOptions = '500ms';

  globalSearch = signal<string | null>(null);
  nomenclatoreTableIsLoading = signal(false);
  csvExportIsLoading = signal(false);

  isLoading = signal(false);
  isEditingRow = new Map<TreeNode, true>();

  isOpen = signal(false);
  selectedNomenclatoreNode = signal<
    | {
        mode: NomenclatoreDialogType;
        nomenclatore: NomenclatoreDialogEntry | null;
      }
    | undefined
  >(undefined);

  table?: TreeTable;
  @ViewChild('nomenclatoreTable') set setNomenclatoreTable(
    table: TreeTable | null | undefined,
  ) {
    if (!table) return;
    this.table = table;

    this.table.onFilter
      .pipe(
        untilDestroyed(this),
        map(({ filters, filteredValue }) => ({
          globalFilter: filters?.['global']?.value as string | undefined,
          filteredValue,
        })),
        distinctUntilKeyChanged('globalFilter'),
        tap(({ globalFilter, filteredValue }) => {
          const filter = globalFilter
            ? new RegExp(globalFilter, 'gi')
            : undefined;
          filteredValue?.forEach((node) => {
            this.expandHighlightedNodes(
              node as TreeNode<NomenclatoreTreeNodeData>,
              filter,
            );
          });
          this.nomenclatoreTableIsLoading.set(false);
        }),
      )
      .subscribe();
  }

  constructor() {
    const globalSearch$ = toObservable(this.globalSearch);
    globalSearch$
      .pipe(
        untilDestroyed(this),
        filter(
          (value) =>
            value !== null && (value.length === 0 || value.length >= 3),
        ),
        debounceTime(300),
        tap((value) => {
          this.table?.filterGlobal(value ?? '', 'contains');
          this.nomenclatoreTableIsLoading.set(true);
        }),
      )
      .subscribe();

    const rebuildTreeOnRefresh$ = this.nomenclatoreService.refresh$.pipe(
      untilDestroyed(this),
      tap(() => {
        this.nomenclatoreTableIsLoading.set(true);
      }),
      switchMap(() =>
        combineLatest([
          this.nomenclatoreLiv1$,
          this.nomenclatoreLiv2$,
          this.nomenclatoreLiv3$,
          this.nomenclatoreLiv4$,
          this.nomenclatore$,
        ]),
      ),
      tap(
        ([
          nomenclatoreLiv1,
          nomenclatoreLiv2,
          nomenclatoreLiv3,
          nomenclatoreLiv4,
          nomenclatore,
        ]) => {
          this.nodes = buildNomenclatoreTree(
            nomenclatoreLiv1,
            nomenclatoreLiv2,
            nomenclatoreLiv3,
            nomenclatoreLiv4,
            nomenclatore,
          );
          this.nomenclatoreTableIsLoading.set(false);
        },
      ),
    );
    rebuildTreeOnRefresh$.subscribe();
  }

  nodes: TreeNode<NomenclatoreTreeNodeData>[] = [];

  expandAll() {
    this.expandNodes(this.nodes, true);
  }

  collapseAll() {
    this.expandNodes(this.nodes, false);
  }

  refresh() {
    this.nomenclatoreService.refresh();
  }

  exportCSV() {
    this.csvExportIsLoading.set(true);
    const exportCSV$ = this.nomenclatoreService.exportCSV().pipe(
      catchError(() => {
        this.messages = [
          {
            severity: 'error',
            life: 5000,
            summary: 'Attenzione!',
            detail:
              'Impossibile esportare il csv dei nomenclatori 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.csvExportIsLoading.set(false);
      }),
    );

    exportCSV$.subscribe();
  }

  createNomenclatore(type: NomenclatoreDialogType) {
    this.selectedNomenclatoreNode.set({ mode: type, nomenclatore: null });
    this.isOpen.set(true);
  }

  async editNomenclatore(event: {
    node: TreeNode<NomenclatoreDialogEntry & { type: NomenclatoreDialogType }>;
  }) {
    const { node } = event;
    if (!node.data) return;

    this.isEditingRow.set(node, true);

    if (node.data.type === 'voce nomenclatore') {
      const fullVoceNomenclatore = await lastValueFrom(
        this.nomenclatoreService.getOneNomenclatoreById({ id: node.data.id }),
      );
      Object.assign(node.data, fullVoceNomenclatore);
    }
    this.isEditingRow.delete(node);

    this.isOpen.set(true);
    this.selectedNomenclatoreNode.set({
      mode: node.data.type,
      nomenclatore: node.data,
    });
  }

  private expandNodes(nodes: TreeNode[], toggle: boolean) {
    if (this.nomenclatoreTableIsLoading()) return;

    const expandNodes = (nodes: TreeNode[], toggle: boolean) => {
      nodes.forEach((node) => {
        node.expanded = toggle;
        if (node.children) expandNodes(node.children, toggle);
      });
    };

    this.nomenclatoreTableIsLoading.set(true);
    setTimeout(() => {
      expandNodes(nodes, toggle);
      this.nodes = cloneDeep(this.nodes);
      this.nomenclatoreTableIsLoading.set(false);
    }, 50);
  }

  private expandHighlightedNodes(
    node: TreeNode<NomenclatoreTreeNodeData>,
    filter?: RegExp,
  ): boolean {
    const globalFilterFields = this.table
      ?.globalFilterFields as (keyof NomenclatoreTreeNodeData)[];
    globalFilterFields;
    const matches = (
      node: TreeNode<NomenclatoreTreeNodeData>,
      filter?: RegExp,
    ) => {
      return (
        filter != null &&
        globalFilterFields
          .map((field) => node.data?.[field])
          .some((value) => {
            return value?.toString().match(filter);
          })
      );
    };

    if (!node.children?.length) {
      return matches(node, filter);
    }

    node.expanded = node.children
      .map((child) => this.expandHighlightedNodes(child, filter))
      .some(identity);
    return matches(node, filter) || node.expanded;
  }

  ngOnInit(): void {
    this.refresh();
  }
}
