import { Injectable } from '@angular/core';
import { uuid } from '@msfa-utils/uuid';

export interface TreeNode {
  label: string;
  isSelected: boolean;
  value: unknown;
  childItemType?: string;
  grandChildrenItemType?: string;
  grandChildrenInfo?: string;
  toggleAllChildrenLabel?: string;
  children?: Array<TreeNode>;
}

export interface TreeNodeModel extends TreeNode {
  uuid?: string;
  expandedChildUuid?: string;
  children?: Array<TreeNodeModel>;
  hasFocus?: boolean;
  toggleAllHasFocus?: boolean;
  hideTreeNode?: boolean;
  isRoot?: boolean;
  filterText?: string;
}

export interface FilterTreeNodeData {
  text: string;
  treeNode: TreeNodeModel;
}

@Injectable({
  providedIn: 'root',
})
export class TreeNodesSelectorService {
  getTreeNodeModelFromTreeNode(treeNode: TreeNode): TreeNodeModel | null {
    let children: Array<TreeNode> = [];

    if (!treeNode) {
      return null;
    }

    children = treeNode.children?.map(childNode => this.getTreeNodeModelFromTreeNode(childNode));

    return {
      ...treeNode,
      uuid: uuid(),
      hasFocus: false,
      children,
      hideTreeNode: false,
    };
  }

  getBasicTreeNodeFromModel(treeNodeModel: TreeNodeModel): TreeNode {
    const children = treeNodeModel?.children?.map(childNode => this.getBasicTreeNodeFromModel(childNode));

    const treeNode: TreeNode = {
      isSelected: treeNodeModel.isSelected,
      value: treeNodeModel.value,
      label: treeNodeModel.label,
      grandChildrenItemType: treeNodeModel.grandChildrenItemType,
      grandChildrenInfo: treeNodeModel.grandChildrenInfo,
      childItemType: treeNodeModel.childItemType,
      toggleAllChildrenLabel: treeNodeModel.toggleAllChildrenLabel,
      children,
    };

    return treeNode;
  }

  hasSelectedLeafNodeDescendant(treeNode: TreeNodeModel): boolean {
    if (!treeNode || !treeNode.children) {
      return false;
    }

    return treeNode.children.some(
      childNode => (this.isLeafNode(childNode) && childNode.isSelected) || this.hasSelectedLeafNodeDescendant(childNode)
    );
  }

  /*
    Taking expandedChildUuid as a seperate param despite being a property of the parent tree node
    because there is no change detection for properties within objects.*/
  getAriaLabelTextForExpansionButton(
    parentTreeNode: TreeNodeModel,
    treeNode: TreeNodeModel,
    expandedChildUuid: string
  ): string {
    const isActiveNode = parentTreeNode && treeNode ? expandedChildUuid === treeNode.uuid : false;

    return `${isActiveNode ? 'Döljer' : 'Visar'} ${
      parentTreeNode.grandChildrenItemType ? this.getCleanedText(parentTreeNode.grandChildrenItemType) : 'innehåll'
    } för ${treeNode.label}`;
  }

  hasChildLeafNodes(node: TreeNode): boolean {
    if (!node || !node.children || node.children.length === 0) {
      return false;
    }

    return node.children.some(child => this.isLeafNode(child));
  }

  isLeafNode(node: TreeNodeModel): boolean {
    return node && (!node.children || node.children.length === 0);
  }

  allChildLeafNodesAreSelected(node: TreeNodeModel): boolean {
    if (!node || !node.children || node.children.length === 0) {
      return true;
    }

    return !node.children.some(child => this.isLeafNode(child) && !child.isSelected);
  }

  isExpandedNode(parentTreeNode: TreeNodeModel, treeNode: TreeNodeModel): boolean {
    if (!parentTreeNode || !treeNode) {
      return false;
    }

    return !parentTreeNode.hideTreeNode && !treeNode.hideTreeNode && parentTreeNode.expandedChildUuid === treeNode.uuid;
  }

  getVisibleChildren(treeNode: TreeNodeModel): Array<TreeNodeModel> | null {
    if (!treeNode || !treeNode.children) {
      return null;
    }

    return treeNode.children.filter(childNode => !childNode.hideTreeNode);
  }

  getTreeNodeWithNoNodesSelected(treeNode: TreeNodeModel): TreeNodeModel {
    return this.getTreeNodeWithAllOrNoNodesSelected(treeNode, false);
  }

  getTreeNodeWithAllNodesSelected(treeNode: TreeNodeModel): TreeNodeModel {
    return this.getTreeNodeWithAllOrNoNodesSelected(treeNode, true);
  }

  private getTreeNodeWithAllOrNoNodesSelected(treeNode: TreeNodeModel, selectingAll: boolean): TreeNodeModel {
    if (!treeNode) {
      return null;
    }

    return {
      ...treeNode,
      isSelected: selectingAll,
      children: treeNode.children
        ? treeNode.children.map(child => this.getTreeNodeWithAllOrNoNodesSelected(child, selectingAll))
        : undefined,
    };
  }

  getAriaLabelForToggleAllButton(treeNode: TreeNodeModel): string {
    const allChildNodesAreSelected = this.allChildLeafNodesAreSelected(treeNode);

    return `${allChildNodesAreSelected ? 'Tar bort' : 'Väljer'} samtliga alternativ under: ${treeNode.label}`;
  }

  getFilterButtonAriaLabelText(treeNode: TreeNodeModel): string {
    if (!treeNode) {
      return '';
    }

    return `Filtrera ${treeNode.label}`;
  }

  getCleanedText(text: string): string {
    return text?.trim().toLowerCase();
  }

  getFilteredTreeNodeChildren(filterTreeNodeData: FilterTreeNodeData): Array<TreeNodeModel> {
    let cleanedFilterText: string = null;

    if (!filterTreeNodeData || !filterTreeNodeData.treeNode || !filterTreeNodeData.treeNode.children) {
      return [];
    }

    cleanedFilterText = this.getCleanedText(filterTreeNodeData.text);

    if (!cleanedFilterText || cleanedFilterText.length === 0) {
      return filterTreeNodeData.treeNode.children.map(childNode => {
        return { ...childNode, hideTreeNode: false };
      });
    }

    return filterTreeNodeData.treeNode.children.map(childNode => {
      return { ...childNode, hideTreeNode: this.getCleanedText(childNode?.label).indexOf(cleanedFilterText) === -1 };
    });
  }

  getTreeNodeFilteredBySelectedLeafOrDecsendants(treeNode: TreeNode): TreeNode {
    const filteredTreeNode: TreeNode = {
      ...treeNode,
      children: treeNode.children
        ? treeNode.children
            .filter(
              childNode =>
                this.hasSelectedLeafNodeDescendant(childNode) || (this.isLeafNode(childNode) && childNode.isSelected)
            )
            .map(childNode => this.getTreeNodeFilteredBySelectedLeafOrDecsendants(childNode))
        : undefined,
    };

    return filteredTreeNode;
  }

  getTreeNodeSelectorLabel(treeNode: TreeNode): string {
    const levelInfoList: Array<{ levelItemType: string; sumOfItems: number }> = [];
    let selectorLabel = '';

    if (!treeNode) {
      return selectorLabel;
    }

    selectorLabel = `Välj ${this.getCleanedText(treeNode.label)}`;

    if (!treeNode.children || treeNode.children.length === 0) {
      return selectorLabel;
    }

    this.updateLevelInfoList(treeNode, 0, levelInfoList);

    if (!levelInfoList || levelInfoList.length === 0 || levelInfoList[0].sumOfItems === 0) {
      return selectorLabel;
    }

    selectorLabel = levelInfoList.map(levelInfo => `${levelInfo.sumOfItems} ${levelInfo.levelItemType}`).join(', ');

    return selectorLabel;
  }

  private updateLevelInfoList(
    treeNode: TreeNode,
    level: number,
    levelInfoList: Array<{ levelItemType: string; sumOfItems: number }>
  ): void {
    let selectedDescendants: Array<TreeNode> = [];
    let nodeSum = 0;
    let i: number;

    if (!treeNode || !treeNode.children || treeNode.children.length === 0) {
      return;
    }

    selectedDescendants = treeNode.children.filter(
      childNode => this.hasSelectedLeafNodeDescendant(childNode) || (this.isLeafNode(childNode) && childNode.isSelected)
    );
    nodeSum = selectedDescendants.length;

    if (levelInfoList.length > level && levelInfoList[level]) {
      levelInfoList[level].sumOfItems += nodeSum;
    } else {
      levelInfoList[level] = { levelItemType: treeNode.childItemType, sumOfItems: nodeSum };
    }

    if (this.isLeafNode(treeNode)) {
      return;
    }

    for (i = 0; i < selectedDescendants.length; i++) {
      this.updateLevelInfoList(selectedDescendants[i], level + 1, levelInfoList);
    }
  }

  getClonedTreeNode(treeNode: TreeNodeModel): TreeNodeModel | null {
    let clonedTreeNode: TreeNodeModel | null;

    try {
      clonedTreeNode = JSON.parse(JSON.stringify(treeNode)) as TreeNode;
    } catch (error) {
      console.error(error, 'Failed to clone tree node');
    }

    return clonedTreeNode;
  }

  hasSelectedAllLeafNodes(treeNode: TreeNode): boolean {
    if (!treeNode) {
      return false;
    }

    return this.isLeafNode(treeNode)
      ? treeNode.isSelected
      : !treeNode.children.some(childNode => !this.hasSelectedAllLeafNodes(childNode));
  }

  hasExpandedChild(treeNode: TreeNode): boolean {
    if (!treeNode || !treeNode.children) {
      return false;
    }

    return treeNode.children.some(childNode => this.isExpandedNode(treeNode, childNode));
  }

  showGeneralInfoTemplate(treeNode: TreeNode): boolean {
    if (!treeNode || !treeNode.grandChildrenInfo || this.hasExpandedChild(treeNode)) {
      return false;
    }

    return this.getCleanedText(treeNode.grandChildrenInfo).length > 0;
  }
}
