import {
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  ElementRef,
  EventEmitter,
  forwardRef,
  Input,
  Output,
  Renderer2,
  ViewChild,
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { uuid } from '@msfa-utils/uuid';
import { TreeNode, TreeNodeModel, TreeNodesSelectorService } from '../../services/tree-nodes-selector.service';
import { TreeNodesSelectorPanelComponent } from '../tree-nodes-selector-panel/tree-nodes-selector-panel.component';

interface PropagateChangeFn {
  (_: unknown): void;
}

interface PropagateTouchedFn {
  (): void;
}

@Component({
  selector: 'msfa-tree-nodes-selector',
  templateUrl: './tree-nodes-selector.component.html',
  styleUrls: ['./tree-nodes-selector.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => TreeNodesSelectorComponent),
      multi: true,
    },
  ],
})
export class TreeNodesSelectorComponent implements ControlValueAccessor {
  @Input() buttonElementId: string = uuid();
  @Input() headingText: string;
  @Input() confirmationButtonText = 'Spara';
  @Input() isInvalid = false;
  @Input() showValidation = false;
  @Input() validationMessages: Array<string>;
  @Output() selectedTreeNodesChanged = new EventEmitter<void>();

  @ViewChild('togglePanelBtn') togglePanelBtn: ElementRef;
  @ViewChild(TreeNodesSelectorPanelComponent)
  private treeNodesSelectorPanel: TreeNodesSelectorPanelComponent;

  rootNode: TreeNodeModel | null = null;
  displayPanel = false;
  selectionLabel: string | null = null;
  isDisabled = false;

  panelId = `panel-${uuid()}`;

  private propagateChange: PropagateChangeFn;
  private propagateTouched: PropagateTouchedFn;

  constructor(
    private treeNodesSelectorService: TreeNodesSelectorService,
    private renderer: Renderer2,
    private changeDetectorRef: ChangeDetectorRef
  ) {}

  writeValue(rootNode: TreeNode | null): void {
    this.rootNode = this.treeNodesSelectorService.getTreeNodeModelFromTreeNode(rootNode);
    this.selectionLabel = this.treeNodesSelectorService.getTreeNodeSelectorLabel(this.rootNode);
    this.changeDetectorRef.detectChanges();
  }
  registerOnChange(fn: PropagateChangeFn): void {
    this.propagateChange = fn;
  }
  registerOnTouched(fn: PropagateTouchedFn): void {
    this.propagateTouched = fn;
  }
  setDisabledState?(isDisabled: boolean): void {
    this.renderer.setProperty(this.togglePanelBtn.nativeElement, 'disabled', isDisabled);

    this.closePanel();
  }

  updateTreeNodeSelectorWithSelectedChanges(treeNode: TreeNodeModel): void {
    this.rootNode = treeNode;
    this.selectionLabel = this.treeNodesSelectorService.getTreeNodeSelectorLabel(this.rootNode);
    this.propagateTouched();
    this.propagateChange(this.treeNodesSelectorService.getBasicTreeNodeFromModel(this.rootNode));
    this.selectedTreeNodesChanged.emit();
  }

  closePanel(): void {
    this.displayPanel = false;
  }

  onClickOutsidePanel(): void {
    if (!this.treeNodesSelectorPanel) {
      this.closePanel();
      return;
    }

    this.treeNodesSelectorPanel.closePanel();
  }

  togglePanel(): void {
    this.displayPanel = !this.displayPanel;
  }

  focusControlToggleBtn(): void {
    const togglePanelBtnElement = this.togglePanelBtn.nativeElement as HTMLElement;

    togglePanelBtnElement.focus();
  }
}
