import { useEffect } from "react";
import {
  CompleteObj,
  FormatedSelections,
  ItemId,
  MenuGroup,
  MenuGroupMap,
  SelectedItemType,
} from "../types";

/**
 * convert menuGroup tree to map
 * for each node access without need to store the node details redundantly
 * and simplify the management complexity
 *
 * downward propogation through childIds
 * upward propogation through parentId
 */
export const menuGroupTreeToMap = (menuGroup: MenuGroup) => {
  const menuGroupTreeToMapHelper = (
    node: MenuGroup,
    parentId: ItemId,
    map: MenuGroupMap
  ) => {
    if (!node) return;
    map[node.id] = {
      id: node.id,
      label: node.label,
      value: node.value,
      groupHeading: node.groupHeading,
      childIds: node.options?.map((e) => e.id),
      parentId: parentId,
    };
    node.options?.forEach((e) => {
      menuGroupTreeToMapHelper(e, node.id, map);
    });
  };
  const mapObj: MenuGroupMap = {};
  menuGroupTreeToMapHelper(menuGroup, "", mapObj);
  return mapObj;
};

export const getConnectedItemByDirection = (
  obj: MenuGroupMap,
  isForward = true,
  nodeId: ItemId,
  selectedItems: SelectedItemType,
  isSingleSelection: boolean = false // valid only for forward propogation
): SelectedItemType => {
  const connectedResult: SelectedItemType = isSingleSelection
    ? {}
    : { ...selectedItems };
  // console.log(isForward ? "forward" : "backward");

  const getConnectedItemByDirectionHelper = (
    obj: MenuGroupMap,
    isForward = true,
    nodeId: ItemId,
    selectedItems: SelectedItemType,
    connectedResult: SelectedItemType
  ) => {
    try {
      if (!nodeId) return connectedResult;

      // forward propogation using childIds of selections
      if (isForward) {
        let childIds = selectedItems[nodeId].childIds;
        if (isSingleSelection) {
          // pick the first child of all the children if single selection
          const firstChild = selectedItems[nodeId].childIds?.[0];
          childIds = firstChild ? [firstChild] : [];
        }
        // console.log("picking", childIds, "for", nodeId);
        connectedResult[nodeId] = {
          ...selectedItems[nodeId],
          childIds,
        };
        if (childIds) {
          childIds.forEach((childId) => {
            getConnectedItemByDirectionHelper(
              obj,
              isForward,
              childId,
              selectedItems,
              connectedResult
            );
          });
        }
        return connectedResult;
      }

      // backward propofation using parentId of selections
      const parentId = obj[nodeId].parentId;
      // add the nodeId to the connectedResult if not present.
      if (!connectedResult[nodeId]) {
        // console.log("adding", nodeId, "to connectedResult");
        connectedResult[nodeId] = {
          id: nodeId,
          childIds: [
            ...(isSingleSelection
              ? [nodeId]
              : selectedItems[nodeId]?.childIds || []),
          ],
        };
      }

      // console.log("parentId", parentId);
      // able to connect the path even if the item is not part of the selections, works for back propogation
      if (parentId && !selectedItems[nodeId]) {
        connectedResult[parentId] = {
          id: parentId,
          childIds: isSingleSelection
            ? [nodeId]
            : [...(selectedItems[parentId]?.childIds || []), nodeId],
        };
      }
      // console.log("connectedResult", connectedResult);

      if (parentId) {
        getConnectedItemByDirectionHelper(
          obj,
          isForward,
          parentId,
          selectedItems,
          connectedResult
        );
      }
      return connectedResult;
    } catch (e) {
      console.log("issue while getting connected item", e);
      return connectedResult;
    }
  };

  getConnectedItemByDirectionHelper(
    obj,
    isForward,
    nodeId,
    selectedItems,
    connectedResult
  );
  return connectedResult;
};

export const getConnectedItems = (
  obj: MenuGroupMap,
  nodeId: ItemId,
  selectedItems: SelectedItemType,
  isSingleSelection: boolean = false
): SelectedItemType => {
  const selectedItemsUsed = selectedItems;
  const prevPath = getConnectedItemByDirection(
    obj,
    false,
    nodeId,
    selectedItemsUsed,
    isSingleSelection
  );
  const forwardPath = getConnectedItemByDirection(
    obj,
    true,
    nodeId,
    selectedItemsUsed,
    isSingleSelection
  );

  return {
    ...prevPath,
    ...forwardPath,
  };
};

export const addItemSelection = (
  menuGroupMap: MenuGroupMap,
  selectedItems: SelectedItemType,
  itemId: ItemId
): SelectedItemType => {
  let newSelectedItems = { ...selectedItems };
  try {
    const parentId = menuGroupMap[itemId].parentId || "";
    const parentItem = menuGroupMap[parentId];
    let isMultiSelection = true;
    if (parentId && menuGroupMap[parentId].isMultiSelection !== undefined) {
      isMultiSelection = menuGroupMap[parentId].isMultiSelection ?? true; // Provide default value
    }
    // cut the previous selections in the group if its not mulitselect
    // already has some values in the current group, so need to clear them
    if (
      !isMultiSelection &&
      parentItem?.childIds?.[0] &&
      newSelectedItems?.[parentItem?.childIds[0]]
    ) {
      // "remove the prev selection in the group as it is single selection"
      const { newSelectedItems: newSelections } =
        cascadeSelectionRemovalWithProps(
          menuGroupMap,
          newSelectedItems,
          parentItem?.childIds?.[0],
          { isParentUpdateRequired: true, isMultiSelection }
        );
      newSelectedItems = newSelections;
    }

    // item addition, works even if direct parent is not selected
    newSelectedItems = getConnectedItemByDirection(
      menuGroupMap,
      false,
      itemId,
      newSelectedItems,
      !isMultiSelection
    );
  } catch (e) {
    // setError("issue while adding item");
    console.log("issue while adding item", e, selectedItems, itemId);
  } finally {
    return newSelectedItems;
  }
};

/**
 *
 * cascading item removal and highlights the next available selection
 */
export const cascadeSelectionRemovalWithProps = (
  menuGroupMap: MenuGroupMap,
  selectedItems: SelectedItemType,
  itemId: ItemId,
  additionalProps?: {
    isParentUpdateRequired?: boolean;
    getNextAvailableSelection?: boolean;
    isMultiSelection?: boolean;
  }
): {
  newSelectedItems: SelectedItemType;
  newChildId?: ItemId;
} => {
  const {
    isParentUpdateRequired = false,
    getNextAvailableSelection = false,
    isMultiSelection = false, // TODO: is there dependency on this?
  } = additionalProps || {};
  // taking the orginal complete selections
  try {
    // cascading item removal
    const updatedSelections = cascadeSelectionRemoval(
      menuGroupMap,
      selectedItems,
      itemId
    );
    // console.log("updatedSelections", updatedSelections);

    // apply the passed props

    // if there are other elements in the same group then make it active
    if (isParentUpdateRequired) {
      // update the parent's childIds
      let parentId = menuGroupMap[itemId].parentId;
      if (parentId && selectedItems?.[parentId]) {
        const updatedChildren =
          selectedItems[parentId].childIds?.filter((e) => e !== itemId) || [];
        updatedSelections[parentId] = {
          ...selectedItems[parentId],
          childIds: updatedChildren,
        };
        // console.log("updated childIds in parent", updatedChildren, parentId);

        if (getNextAvailableSelection) {
          // follow the selection order and pick the latest selection
          let childId;
          let children = [...updatedChildren];
          childId = children?.[children.length - 1]; //latest selection
          // console.log("potential childId found", childId);

          if (childId && selectedItems[childId]) {
            const otherPath = getConnectedItems(
              menuGroupMap,
              childId,
              updatedSelections
            );
            // console.log("found other path", otherPath);

            return {
              newSelectedItems: otherPath,
              newChildId: childId,
            };
          } else {
            // no childId found, so return the updatedSelections with parentId
            return {
              newSelectedItems: updatedSelections,
              newChildId: parentId,
            };
          }
        }
      }
    } else if (getNextAvailableSelection) {
      /**
       * if there is any other item in the same group having no parent i.e, in the first level
       */
      // TODO: can't use selectedItems as, we should will the group of deleted ids not just item.id
    }
    // TODO, need to return some childId, because activeItemLeaf may be looking for it.
    return { newSelectedItems: updatedSelections };
  } catch (e) {
    console.log(
      "error in highlighting the other selection in the same group",
      e
    );
    return { newSelectedItems: selectedItems };
  }
};

export const initPreSelections = (selections: FormatedSelections | null) => {
  const newSelectedItems: SelectedItemType = {};
  const newActiveItems = {};

  if (!selections) return { newSelectedItems, newActiveItems };

  const initPreSelectionsHelper = (e: FormatedSelections) => {
    if (!e) return;
    newSelectedItems[e.id] = {
      id: e.id,
      childIds: e.options?.map((e) => e?.id) || [],
    };
    e.options.forEach((opt) => {
      initPreSelectionsHelper(opt);
    });
  };

  initPreSelectionsHelper(selections);

  return { newSelectedItems };
};

/**
 * Cascading item removal(current+children)
 * Removes the current selection and its children selections
 */
export const cascadeSelectionRemoval = (
  obj: MenuGroupMap,
  selectedItems: SelectedItemType,
  nodeId: ItemId
): SelectedItemType => {
  try {
    if (!nodeId) return {};
    const result = { ...selectedItems };

    const cascadeSelectionRemovalHelper = (
      obj: MenuGroupMap,
      selectedItems: SelectedItemType,
      nodeId: ItemId,
      result: SelectedItemType
    ) => {
      const childIds = selectedItems[nodeId]?.childIds;
      delete result[nodeId];

      if (childIds) {
        childIds.forEach((childId) => {
          cascadeSelectionRemovalHelper(obj, selectedItems, childId, result);
        });
      }
    };

    cascadeSelectionRemovalHelper(obj, selectedItems, nodeId, result);
    // console.log("cascadeSelectionRemoval", result);
    return result;
  } catch (e) {
    console.log("Issue while remove item", e);
    // setError("Issue while remove item");
    return selectedItems;
  }
};

// /**
//  * get all leaf-nodes with paths through indexes
//  * used for search
//  */
export function getAllLeafNodes(
  treeObj: MenuGroup,
  index: number
): CompleteObj[] {
  try {
    const { label, options } = treeObj;
    if (!options?.length) {
      return [{ label, indexes: [index] }];
    }

    const childRes = options.reduce(
      (acc: CompleteObj[], item, index: number): CompleteObj[] => {
        return [...acc, ...getAllLeafNodes(item, index)];
      },
      []
    );

    return childRes.map((e) => {
      const { label, indexes } = e;
      if (!treeObj.label) {
        return e;
      }
      return {
        label: `${treeObj.label}=>${label}`,
        indexes: [index, ...indexes],
      };
    });
  } catch (e) {
    console.log("error in finding all the leafs", e);
  }
  // not necessary, will not be able to reach this
  return [{ label: treeObj.label, indexes: [index] }];
}
