import { ButtonSize } from '@af/digi-ng/_button/button';
import {
  ChangeDetectionStrategy,
  Component,
  ElementRef,
  EventEmitter,
  Input,
  OnInit,
  Output,
  ViewChild,
} from '@angular/core';
import {
  FilterTreeNodeData,
  TreeNodeModel,
  TreeNodesSelectorService,
} from '../../services/tree-nodes-selector.service';

@Component({
  selector: 'msfa-tree-nodes-selector-panel',
  templateUrl: './tree-nodes-selector-panel.component.html',
  styleUrls: ['./tree-nodes-selector-panel.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TreeNodesSelectorPanelComponent implements OnInit {
  @Input() rootNode: TreeNodeModel | null = null;
  @Input() headingText: string;
  @Input() confirmationButtonText = 'Spara';
  @Output() selectedChangesConfirmed = new EventEmitter<TreeNodeModel>();
  @Output() closePanelRequested = new EventEmitter<void>();

  @ViewChild('treeNodesSelectorPanel') treeNodesSelector: ElementRef;

  readonly ButtonSize = ButtonSize;

  pendingRootNode: TreeNodeModel | null = null;
  expandedTreeNodes: Array<TreeNodeModel> | null = null;
  latestParentActionKey: string = null;

  constructor(private treeNodesSelectorService: TreeNodesSelectorService) {}

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

  private setupPendingRootNode(): void {
    this.pendingRootNode = this.treeNodesSelectorService.getClonedTreeNode(this.rootNode);
    this.expandedTreeNodes = this.treeNodesSelectorService.getExpandedTreeNodes(this.pendingRootNode);
  }

  onToggleSelected(treeNode: TreeNodeModel, event: CustomEvent<{ target: { checked: boolean } }>): void {
    if (!treeNode || !event?.detail?.target) {
      return;
    }

    treeNode.isSelected = event?.detail?.target?.checked;

    // eslint-disable-next-line
    this.latestParentActionKey = `${treeNode.uuid}-selected-${treeNode.isSelected}`;
  }

  setExpandedChild(parentTreeNode: TreeNodeModel, treeNode: TreeNodeModel): void {
    if (!parentTreeNode || !treeNode) {
      return;
    }

    parentTreeNode.expandedChildUuid = parentTreeNode.expandedChildUuid === treeNode.uuid ? null : treeNode.uuid;

    this.expandedTreeNodes = this.treeNodesSelectorService.getExpandedTreeNodes(this.pendingRootNode);

    this.latestParentActionKey = `${parentTreeNode.uuid}-expanded-child-${parentTreeNode.expandedChildUuid}`;
  }

  setFocusOnToggleAll(treeNode: TreeNodeModel, hasFocus: boolean): void {
    let presentationToggleAllElement: HTMLElement = null;
    let expansionColumnElement: HTMLElement = null;

    if (!treeNode) {
      return;
    }

    treeNode.toggleAllHasFocus = hasFocus;

    if (!hasFocus) {
      return;
    }

    presentationToggleAllElement = this.treeNodesSelectorService.getPresentationToggleAllElement(
      this.treeNodesSelector,
      treeNode
    );

    expansionColumnElement = this.treeNodesSelectorService.getExpansionColumnElement(presentationToggleAllElement);

    this.scrollElementInContainer(expansionColumnElement);
  }

  private scrollElementInContainer(element: HTMLElement): void {
    let elementRect: DOMRect = null;
    let parentElementRect: DOMRect = null;

    if (!element || !element.parentElement) {
      return;
    }

    if (this.treeNodesSelectorService.isFullyVisibleElement(element, element.parentElement)) {
      return;
    }

    elementRect = element.getBoundingClientRect();

    parentElementRect = element.parentElement.getBoundingClientRect();

    element.parentElement.scrollTop = elementRect.top + element.parentElement.scrollTop - parentElementRect.top;
    element.parentElement.scrollLeft = elementRect.left + element.parentElement.scrollLeft - parentElementRect.left;
  }

  setFocusOnNodeItem(treeNode: TreeNodeModel, hasFocus: boolean): void {
    let presentationItemElement: HTMLElement = null;
    let expansionColumnElement: HTMLElement = null;

    if (!treeNode) {
      return;
    }

    treeNode.hasFocus = hasFocus;

    if (!hasFocus) {
      return;
    }

    this.latestParentActionKey = `${treeNode.uuid}-focused-item`;

    presentationItemElement = this.treeNodesSelectorService.getPresentationItemElement(
      this.treeNodesSelector,
      treeNode
    );

    expansionColumnElement = this.treeNodesSelectorService.getExpansionColumnElement(presentationItemElement);

    this.scrollElementInContainer(presentationItemElement);
    this.scrollElementInContainer(expansionColumnElement);
  }

  toggleAllChildLeafNodes(treeNode: TreeNodeModel): void {
    let allChildLeafNodesAreSelected = false;

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

    allChildLeafNodesAreSelected = this.allChildLeafNodesAreSelected(treeNode);

    treeNode.children = treeNode.children.map(child => {
      return { ...child, isSelected: !allChildLeafNodesAreSelected };
    });

    this.latestParentActionKey = `${treeNode.uuid}-selected-${allChildLeafNodesAreSelected.toString()}`;
  }

  clearSelections(): void {
    this.pendingRootNode = this.treeNodesSelectorService.getTreeNodeWithNoNodesSelected(this.pendingRootNode);
    this.latestParentActionKey = `cleared-selections`;
    this.expandedTreeNodes = this.treeNodesSelectorService.getExpandedTreeNodes(this.pendingRootNode);
  }

  closePanel(): void {
    this.selectedChangesConfirmed.emit(this.pendingRootNode);
    this.closePanelRequested.emit();
  }

  filterTreeNode(filterTreeNodeData: FilterTreeNodeData): void {
    if (!filterTreeNodeData || !filterTreeNodeData.treeNode) {
      return;
    }

    filterTreeNodeData.treeNode.children = this.treeNodesSelectorService.getFilteredTreeNodeChildren(
      filterTreeNodeData
    );

    this.latestParentActionKey = `${filterTreeNodeData.treeNode.uuid}-filtered-${filterTreeNodeData.text}`;
    this.expandedTreeNodes = this.treeNodesSelectorService.getExpandedTreeNodes(this.pendingRootNode);
  }

  clickAndFocusElementByQuerySelector(selector: string): void {
    const selectedItem = this.treeNodesSelector
      ? this.treeNodesSelectorService.getElementByQuerySelector(this.treeNodesSelector, selector)
      : null;

    if (!selectedItem) {
      return;
    }

    selectedItem.focus();
    selectedItem.click();
  }

  getVisibleChildren(treeNode: TreeNodeModel): Array<TreeNodeModel> | null {
    return this.treeNodesSelectorService.getVisibleChildren(treeNode);
  }

  hasChildLeafNodes(node: TreeNodeModel): boolean {
    return this.treeNodesSelectorService.hasChildLeafNodes(node);
  }

  allChildLeafNodesAreSelected(node: TreeNodeModel): boolean {
    return this.treeNodesSelectorService.allChildLeafNodesAreSelected(node);
  }

  hasSelectedDescendant(treeNode: TreeNodeModel): boolean {
    return this.treeNodesSelectorService.hasSelectedLeafNodeDescendant(treeNode);
  }

  getAriaLabelTextForExpansionButton(parentTreeNode: TreeNodeModel, treeNode: TreeNodeModel): string {
    return this.treeNodesSelectorService.getAriaLabelTextForExpansionButton(parentTreeNode, treeNode);
  }

  getAriaLabelForToggleAllButton(treeNode: TreeNodeModel): string {
    return this.treeNodesSelectorService.getAriaLabelForToggleAllButton(treeNode);
  }

  isExpandedNode(parentTreeNode: TreeNodeModel, treeNode: TreeNodeModel): boolean {
    return this.treeNodesSelectorService.isExpandedNode(parentTreeNode, treeNode);
  }

  getToggleAllButtonId(treeNode: TreeNodeModel): string {
    return this.treeNodesSelectorService.getToggleAllButtonId(treeNode);
  }

  getItemButtonId(treeNode: TreeNodeModel): string {
    return this.treeNodesSelectorService.getItemButtonId(treeNode);
  }
}
