/* TreeViewCustomizer */
import { Component, NgZone, inject, OnInit } from '@angular/core';
import { ActivatedRoute, Data, ParamMap, Router, RouterModule } from '@angular/router';
import { combineLatest, filter, Observable, Subscription, tap, concatMap } from 'rxjs';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { HttpResponse } from '@angular/common/http';

import SharedModule from 'app/shared/shared.module';
import { sortStateSignal, SortDirective, SortByDirective, type SortState, SortService } from 'app/shared/sort';
import { DurationPipe, FormatMediumDatetimePipe, FormatMediumDatePipe } from 'app/shared/date';
import { FormsModule } from '@angular/forms';
import { SORT, ITEM_DELETED_EVENT, DEFAULT_SORT_DATA } from 'app/config/navigation.constants';
import { DataUtils } from 'app/core/util/data-util.service';
import { IOrganization } from '../organization.model';
import { IOrganizationType } from 'app/entities/organization-type/organization-type.model';
import { OrganizationTypeService } from 'app/entities/organization-type/service/organization-type.service';
import { EntityArrayResponseType, OrganizationService } from '../service/organization.service';
import { OrganizationDeleteDialogComponent } from '../delete/organization-delete-dialog.component';

/* Treeview - BEGIN */
/* Treeview imports */
/* See: https://way2rock00.medium.com/creating-dynamic-tree-using-mattree-in-angular-aabca88b3cfa */
import { CommonModule } from '@angular/common';
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
import {MatSliderModule} from '@angular/material/slider';
import {FlatTreeControl} from '@angular/cdk/tree';
import {MatTreeFlatDataSource, MatTreeFlattener} from '@angular/material/tree';

import {MatProgressBarModule} from '@angular/material/progress-bar';
import {MatIconModule} from '@angular/material/icon';
import {MatButtonModule} from '@angular/material/button';
import {MatTreeModule} from '@angular/material/tree';

import {OrganizationTreeActionsComponent} from './organization-tree-actions/organization-tree-actions.component';

interface TreeNodeFlat {
 name: string;
 Id: number;
 parentId: number|null;
 org: IOrganization;
 children?: TreeNodeFlat[];
}

/** Flat node with expandable and level information */
interface TreeNode {
 expandable: boolean;
 name: string;
 id: number;
 level: number;
 org: IOrganization;
}

interface TreeOption {
 title: string;
 routerLink: string[];
 queryParams: {};
}
/* Treeview - END */

@Component({
  standalone: true,
  selector: 'jhi-organization',
  templateUrl: './organization.component.html',
  imports: [
    RouterModule,
    FormsModule,
    SharedModule,
    SortDirective,
    SortByDirective,
    DurationPipe,
    FormatMediumDatetimePipe,
    FormatMediumDatePipe,
  	MatTreeModule, 
	MatButtonModule, 
	MatIconModule, 
	MatProgressBarModule,
	NgbDropdownModule,
	CommonModule,
	OrganizationTreeActionsComponent,
  ],
})

export class OrganizationComponent implements OnInit {
  /* Treeview - BEGIN */
  private _transformer = (node: TreeNodeFlat, level: number) => {
    return {
	  expandable: !!node.children && node.children.length > 0,
	  name: node.name,
	  id: node.Id,
	  level: level,
	  org: node.org
	};
  }
  
  treeControl = new FlatTreeControl<TreeNode>(node => node.level, node => node.expandable);
  treeFlattener = new MatTreeFlattener(this._transformer, node => node.level, node => node.expandable, node => node.children);
  dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener);
  
  hasChild = (_: number, node: TreeNode) => node.expandable;
  showTreeActionsId: number = -1;
  /* Treeview - END */

  subscription: Subscription | null = null;
  organizations?: IOrganization[];
  isLoading = false;

  organizationTypes?: IOrganizationType[];

  sortState = sortStateSignal({});

  public router = inject(Router);
  protected organizationService = inject(OrganizationService);
  protected organizationTypeService = inject(OrganizationTypeService);
  protected activatedRoute = inject(ActivatedRoute);
  protected sortService = inject(SortService);
  protected dataUtils = inject(DataUtils);
  protected modalService = inject(NgbModal);
  protected ngZone = inject(NgZone);

  trackId = (_index: number, item: IOrganization): number => this.organizationService.getEntityIdentifier(item);

  ngOnInit(): void {
    this.subscription = combineLatest([
      this.activatedRoute.queryParamMap, 
      this.activatedRoute.data
    ]).pipe(
      // Load a single list of OrganizatonType's, to use as source for icons
      concatMap(() => 
        this.organizationTypeService.query().pipe(
          tap((types: HttpResponse<IOrganizationType[]>) => {
            if (types.body) {
              this.organizationTypes = types.body;
            }
          })
        )
      ),
      // Load and build the organization graph
      tap(() => {
        if (!this.organizations || this.organizations.length === 0) {
          this.load();
        }
      })
    ).subscribe();
  }


  getType(org: IOrganization): IOrganizationType | undefined {
    const selectedOrganizationType = this.organizationTypes?.find(type => type.id === org?.organizationType?.id);
    return selectedOrganizationType;
  }
  
  /* TreeView - BEGIN */
  //constructTree recursively iterates through the tree to create nested tree structure.
  //We only need Id and parentId Columns in the flat data to construct this tree properly.
  treeConstructOrg(treeData: IOrganization[]) {
    let constructedTree: TreeNodeFlat[] = [];
	
    for (let i of treeData) {
      let treeObjOrg = i;
      let assigned = false;
	  
      let treeObj: TreeNodeFlat = {
        name: treeObjOrg.name as string,
        Id: treeObjOrg.id,
        parentId: treeObjOrg.parent==null?null:treeObjOrg.parent.id,
        org: treeObjOrg
      };
	  
      this.constructTree(constructedTree, treeObj, assigned)
    }
	
    return constructedTree;
  }
  
  treeConstruct(treeData: TreeNodeFlat[]) {
    let constructedTree: TreeNodeFlat[] = [];
    for (let i of treeData) {
      let treeObj = i;
      let assigned = false;
      this.constructTree(constructedTree, treeObj, assigned)
    }
    return constructedTree;
  }
  
  /**
  * Recursive method to build the tree. Pay attention to the constructedTree: any arg.
  * This will have 2 types: TreeNodeFlat[] OR TreeNodeFlat. Depending on the level being evaluated,
  * the arg might be the all tree (TreeNodeFlat[]) or a branch of the tree (TreeNodeFlat).
  */
  constructTree(constructedTree: any, treeObj: TreeNodeFlat, assigned: boolean) {
    if (treeObj.parentId == null) {
      treeObj.children = [];
      constructedTree.push(treeObj);
      return true;
    } else if (treeObj.parentId == constructedTree.Id) {
      treeObj.children = [];
      constructedTree.children.push(treeObj);
      return true;
    }
    else {
      if (constructedTree.children != undefined) {
        for (let index = 0; index < constructedTree.children.length; index++) {
          let constructedObj = constructedTree.children[index];
          if (assigned == false) {
            assigned = this.constructTree(constructedObj, treeObj, assigned);
          }
        }
      } else {
        for (let index = 0; index < constructedTree.length; index++) {
          let constructedObj = constructedTree[index];
          if (assigned == false) {
            assigned = this.constructTree(constructedObj, treeObj, assigned);
          }
        }
      }
      return false;
    }
  }
  
  onMouseEnter(node: TreeNode): void {
	this.showTreeActionsId = node.id;
    //console.log('On Mouse Enter ');
  }
  
  onMouseLeave(node: TreeNode): void {
	this.showTreeActionsId = -1;
	// console.log('On Mouse Leave');
  }
  
  protected handleActionDeletedEvent(data: { organization: IOrganization }): void {
    this.delete(data.organization);
  }
  /* TreeView - END */

  byteSize(base64String: string): string {
    return this.dataUtils.byteSize(base64String);
  }

  openFile(base64String: string, contentType: string | null | undefined): void {
    return this.dataUtils.openFile(base64String, contentType);
  }

  delete(organization: IOrganization): void {
    const modalRef = this.modalService.open(OrganizationDeleteDialogComponent, { size: 'lg', backdrop: 'static' });
    modalRef.componentInstance.organization = organization;
    // unsubscribe not needed because closed completes on modal close
    modalRef.closed
      .pipe(
        filter(reason => reason === ITEM_DELETED_EVENT),
        tap(() => this.load()),
      )
      .subscribe();
  }

  load(): void {
    this.queryBackend().subscribe({
      next: (res: EntityArrayResponseType) => {
        this.onResponseSuccess(res);
      },
    });
  }

  navigateToWithComponentValues(event: SortState): void {
    this.handleNavigation(event);
  }

/*
  protected fillComponentAttributeFromRoute(params: ParamMap, data: Data): void {
    this.sortState.set(this.sortService.parseSortParam(params.get(SORT) ?? data[DEFAULT_SORT_DATA]));
  }
*/
  protected onResponseSuccess(response: EntityArrayResponseType): void {
    // const dataFromBody = this.fillComponentAttributesFromResponseBody(response.body);
    // this.organizations = this.refineData(dataFromBody);
    this.organizations = this.fillComponentAttributesFromResponseBody(response.body);
  	this.dataSource.data = this.treeConstructOrg(this.organizations as IOrganization[]);	/* TreeView - Load tree*/
    
    //Auto-Expand the first level
    this.treeControl.dataNodes
      .filter(node => node.level === 0) // Only expand root-level nodes
      .forEach(node => this.treeControl.expand(node));
  }

  protected fillComponentAttributesFromResponseBody(data: IOrganization[] | null): IOrganization[] {
    return data ?? [];
  }

  protected queryBackend(): Observable<EntityArrayResponseType> {
    this.isLoading = true;
    const queryObject: any = {
      eagerload: true,
      sort: this.sortService.buildSortParam(this.sortState()),
    };
    return this.organizationService.query(queryObject).pipe(tap(() => (this.isLoading = false)));
  }

  protected handleNavigation(sortState: SortState): void {
    const queryParamsObj = {
      sort: this.sortService.buildSortParam(sortState),
    };

    this.ngZone.run(() => {
      this.router.navigate(['./'], {
        relativeTo: this.activatedRoute,
        queryParams: queryParamsObj,
      });
    });
  }
}
