/// <reference types="@applicaster/applicaster-types" />

import { isString } from "../stringUtils";

/* eslint-disable no-dupe-class-members */

export class TreeNode {
  public component: FocusManager.TouchableReactRef | null;
  public children: TreeNode[];
  public id: string;
  public parentId: string;
  public isFocusableCell: boolean;

  constructor(
    component: FocusManager.TouchableReactRef | null,
    id: string,
    parentId: string,
    isFocusableCell: boolean
  ) {
    this.component = component;
    this.children = [];
    this.id = id;
    this.parentId = parentId;
    this.isFocusableCell = isFocusableCell;
  }

  addChild(node: TreeNode): void;
  addChild(
    node: FocusManager.TouchableReactRef,
    id: string,
    isFocusableCell: boolean
  ): void;

  addChild(
    node: FocusManager.TouchableReactRef | TreeNode,
    id?: string,
    isFocusableCell?: boolean
  ) {
    const newNode: TreeNode =
      node instanceof TreeNode
        ? node
        : new TreeNode(node, id as string, this.id, isFocusableCell);

    if (this.children.some((child) => child.id === newNode.id)) {
      // add log that child with this ID already exists
      return;
    }

    this.children.push(newNode);
  }

  findNode(
    component: FocusManager.TouchableReactRef | string
  ): TreeNode | null {
    if (isString(component)) {
      if (this.id === component) {
        return this;
      }
    } else if (this.component === component) {
      return this;
    }

    for (const child of this.children) {
      const found = child.findNode(component);

      if (found) {
        return found;
      }
    }

    return null;
  }

  removeNode(component: FocusManager.TouchableReactRef | string): TreeNode[] {
    this.children = this.children.filter(
      (child) =>
        !(
          (isString(component) && child.id === component) ||
          (!isString(component) && child.component === component)
        )
    );

    this.children.forEach((child) => {
      child.removeNode(component);
    });

    return this.children;
  }

  updateNode(component: FocusManager.TouchableReactRef) {
    if (!component?.current) {
      throw new Error(
        "Component needs to contain reference to FocusManager TouchableComponent"
      );
    }

    if (this.id === component.current?.props?.id) {
      this.component = component;

      return this;
    } else {
      // log Error that component id needs to match this of the node
    }

    return null;
  }
}
