import { CollectionViewer, DataSource, SelectionChange } from '@angular/cdk/collections';
import { FlatTreeControl } from '@angular/cdk/tree';
import { Component, OnInit } from '@angular/core';
import { BehaviorSubject, lastValueFrom, map, merge, Observable } from 'rxjs';
import { EquipmentManagementService } from '../../../services/equipment-management.service';
import { ResourcesService } from '../../../services/resources.service';
import { EncService } from '../../../services/enc.service';
import { EquipmentsListResponse, FamiliesListResponse, SubfamiliesListResponse } from '../../../interfaces/equipment-management.interface';
import { MatDialogRef } from '@angular/material/dialog';

@Component({
    selector: 'app-graphic-equipment-search',
    templateUrl: './graphic-equipment-search.component.html',
    styleUrl: './graphic-equipment-search.component.css',
    standalone: false
})
export class GraphicEquipmentSearchComponent {
  treeControl: FlatTreeControl<DynamicFlatNode>;
  dataSource: DynamicDataSource;
  rootLevelNode: string[] = ['Línea 1'];

  constructor(
    private _equipmentManagementService: EquipmentManagementService,
    private _resourcesService: ResourcesService,
    private _encService: EncService,
    private _dialogRef: MatDialogRef<GraphicEquipmentSearchComponent>,
  ){
    this.treeControl = new FlatTreeControl<DynamicFlatNode>(this.getLevel, this.isExpandible);
    this.dataSource = new DynamicDataSource(this.treeControl, _equipmentManagementService, _resourcesService, _encService);

    this.dataSource.data = this.initialData();
  }

  getLevel = (node: DynamicFlatNode) => node.level;
  isExpandible = (node: DynamicFlatNode) => node.expandable;
  hasChild = (_: number, _nodeData: DynamicFlatNode) => _nodeData.expandable;

  initialData(): DynamicFlatNode[] {
    return this.rootLevelNode.map(name => new DynamicFlatNode(name, 0, true));
  }

  assignEquipment(equipment: string){
    this._dialogRef.close(equipment);
  }
}

export class DynamicFlatNode {
  constructor(
    public item: string,
    public level = 1,
    public expandable = false,
    public isLoading = false,
  ) {}
}

export class DynamicDataSource implements DataSource<DynamicFlatNode>{
  dataChange = new BehaviorSubject<DynamicFlatNode[]>([]);

  get data(): DynamicFlatNode[] {
    return this.dataChange.value;
  }

  set data(value: DynamicFlatNode[]) {
    this._treeControl.dataNodes = value;
    this.dataChange.next(value);
  }

  constructor(
    private _treeControl: FlatTreeControl<DynamicFlatNode>,
    private _equipmentManagementService: EquipmentManagementService,
    private _resourcesService: ResourcesService,
    private _encService: EncService,
  ) {}

  connect(collectionViewer: CollectionViewer): Observable<DynamicFlatNode[]> {
    this._treeControl.expansionModel.changed.subscribe(change => {
      if(
        (change as SelectionChange<DynamicFlatNode>).added || 
        (change as SelectionChange<DynamicFlatNode>).removed
      ) {
        this.handleTreeControl(change as SelectionChange<DynamicFlatNode>);
      }
    });

    return merge(collectionViewer.viewChange, this.dataChange).pipe(map(() => this.data));
  }

  disconnect(collectionViewer: CollectionViewer): void {}

  handleTreeControl(change: SelectionChange<DynamicFlatNode>){
    if(change.added){
      change.added.forEach(node => this.toggleNode(node, true));
    }
    
    if(change.removed){
      change.removed.slice().reverse().forEach(node => this.toggleNode(node, false));
    }
  }

  async toggleNode(node: DynamicFlatNode, expand: boolean){
    try{
      node.isLoading = true;

      let idUser = localStorage.getItem('idusuario')!;
      let index = this.data.indexOf(node);
      if(index < 0) return;

      if(node.level == 0){
        let families: FamiliesListResponse = await lastValueFrom(this._equipmentManagementService.familyConsult(idUser, 1));
        if(families.error){
          this._resourcesService.openSnackBar(families.msg);
        }else{
          let nodes: DynamicFlatNode[] = [];
          for(const family of families.response){
            let codeDec = await this._encService.decrypt(family.CODIGOFAMILIA);
            let familyName = `${family.NOMBREFAMILIA} (${codeDec})`;

            if(family.ESTADO == 'Activo'){
              nodes.push(new DynamicFlatNode(familyName, node.level + 1, family.SUBFAMILIAS > 0));
            }
          }

          if(expand){
            this.data.splice(index + 1, 0, ...nodes);
          }else{
            let count = 0;
            for(let i = index + 1; i < this.data.length && this.data[i].level > node.level; i++, count++) {}

            this.data.splice(index + 1, count);
          }
        }
      }else if(node.level == 1){
        let familyNameArr = node.item.split('(').reverse();
        let familyCode = familyNameArr[0].replace(')', '');
        let familyCodeEnc = await this._encService.encrypt(familyCode);

        let subfamilies: SubfamiliesListResponse = await lastValueFrom(this._equipmentManagementService.subfamilyConsult(familyCodeEnc, idUser, 1));
        if(subfamilies.error){
          this._resourcesService.openSnackBar(subfamilies.msg);
        }else{
          let nodes: DynamicFlatNode[] = [];
          for(const subfamily of subfamilies.response){
            let codeDec = await this._encService.decrypt(subfamily.CODIGOSUBFAMILIA);
            let subfamilyName = `${familyCode} - ${subfamily.NOMBRESUBFAMILIA} (${codeDec})`;

            if(subfamily.ESTADO == 'Activo'){
              nodes.push(new DynamicFlatNode(subfamilyName, node.level + 1, subfamily.TIENE_HIJOS));
            }
          }
          
          if(expand){
            this.data.splice(index + 1, 0, ...nodes);
          }else{
            let count = 0;
            for(let i = index + 1; i < this.data.length && this.data[i].level > node.level; i++, count++) {}

            this.data.splice(index + 1, count);
          }
        }
      }else if(node.level == 2){
        let subfamilyNameArr = node.item.split(' - ');
        if(subfamilyNameArr.length > 1){
          let subfamilyArr = subfamilyNameArr[1].split('(').reverse();
          let subfamilyCode = subfamilyArr[0].replace(')', '');
          let subfamilyCodeEnc = await this._encService.encrypt(subfamilyCode);
          let familyCodeEnc = await this._encService.encrypt(subfamilyNameArr[0]);

          let equipments: EquipmentsListResponse = await lastValueFrom(this._equipmentManagementService.equipmentConsultBySubfamily(
            familyCodeEnc, 
            subfamilyCodeEnc, 
            idUser, 
            1
          ));

          if(equipments.error){
            this._resourcesService.openSnackBar(equipments.msg);
          }else{
            let nodes: DynamicFlatNode[] = [];
            for(const equipment of equipments.response){
              let codeDec = await this._encService.decrypt(equipment.CODIGO);
              let idDec = await this._encService.decrypt(equipment.ID_EQUIPO);
              let equipmentName = `${codeDec} - ${equipment.TIPO} - ${equipment.MODELO} (${idDec})`;
  
              nodes.push(new DynamicFlatNode(equipmentName, node.level + 1, equipment.TIENE_HIJOS == 'Si'));
            }
          
            if(expand){
              this.data.splice(index + 1, 0, ...nodes);
            }else{
              let count = 0;
              for(let i = index + 1; i < this.data.length && this.data[i].level > node.level; i++, count++) {}
  
              this.data.splice(index + 1, count);
            }
          }
        }
      }else if(node.level > 2){
        let equipmentArr = node.item.split(' - ');
        let equipmentCodeEnc = await this._encService.encrypt(equipmentArr[0]);
        let equipments: EquipmentsListResponse = await lastValueFrom(this._equipmentManagementService.equipmentConsultByParent(equipmentCodeEnc, idUser, 1));

        if(equipments.error){
          this._resourcesService.openSnackBar(equipments.msg);
        }else{
          let nodes: DynamicFlatNode[] = [];
          for(const equipment of equipments.response){
            let codeDec = await this._encService.decrypt(equipment.CODIGO);
            let idDec = await this._encService.decrypt(equipment.ID_EQUIPO);
            let equipmentName = `${codeDec} - ${equipment.TIPO} - ${equipment.MODELO} (${idDec})`;

            nodes.push(new DynamicFlatNode(equipmentName, node.level + 1, equipment.TIENE_HIJOS == 'Si'));
          }
        
          if(expand){
            this.data.splice(index + 1, 0, ...nodes);
          }else{
            let count = 0;
            for(let i = index + 1; i < this.data.length && this.data[i].level > node.level; i++, count++) {}

            this.data.splice(index + 1, count);
          }
        }
      }

      this.dataChange.next(this.data);
      node.isLoading = false;
    }catch(error: any){
      console.log(error);
      
      this._resourcesService.openSnackBar('Ocurrió un error en la consulta del nodo.');
      this.dataChange.next(this.data);
      node.isLoading = false;
    }
  }
}
