import iabResolver from '../../../../graphql/services/configuration/resolvers/iab';

export type IABNodeType = {
  id: string;
  name: string;
  highlight?: boolean;
  probability?: number;
  version?: string;
  categories?: IABNodeType[];
};

let iabList: IABNodeType[] = [];

if (process.env.NODE_ENV === 'development') {
  iabList = iabResolver();
}

export const setIAB = (data: string) => {
  iabList = JSON.parse(data);
};

export const getIAB = () => iabList;

export const iabFindLeafNodes = (rootNode: IABNodeType) => {
  const leafNodes: IABNodeType[] = [];

  function findRecursively(node: IABNodeType) {
    if (node.categories?.length) {
      node.categories.forEach((childNode) => findRecursively(childNode));
    } else if (node.id && node.name) {
      leafNodes.push(node);
    }
  }

  findRecursively(rootNode);

  return leafNodes;
};

export const findNodes = (iabCategories: IABNodeType[], ids: string[] = []) => {
  const nodes: IABNodeType[] = [];

  const allNodes = () => nodes.length === ids.length;

  const findNode = (categories: IABNodeType[]) => {
    categories.forEach((category) => {
      if (ids.includes(category.id)) {
        nodes.push(category);
      }
      if (!allNodes() && category.categories?.length) {
        findNode(category.categories);
      }
    });
  };

  findNode(iabCategories);

  return nodes;
};

export const splitTree = (tree: IABNodeType) => {
  const items: IABNodeType[] = [];
  const parents: IABNodeType[] = [];

  const findNode = (categories: IABNodeType[]) => {
    categories.forEach((category) => {
      if (category.categories?.length) {
        parents.push(category);
        findNode(category.categories);
      } else {
        items.push(category);
      }
    });
  };

  if (tree?.categories) {
    findNode(tree.categories);
  }

  return [items, parents];
};

export const mapCategories = (categories: IABNodeType[]) =>
  categories.map(({ id, name, version, probability }) => ({
    id,
    name,
    version,
    probability,
  }));

export const mergeTree = (tree: IABNodeType, items: IABNodeType[], parents: IABNodeType[]) => {
  const treeList: { [key: string]: IABNodeType } = {};

  const findNode = (categories: IABNodeType[]) => {
    categories.forEach((category) => {
      treeList[category.id] = category;
      if (category.categories?.length) {
        findNode(category.categories);
      }
    });
  };

  if (tree) {
    findNode(tree.categories as IABNodeType[]);
  }

  const newItems = mapCategories(items.map((item) => treeList[item.id] || item));
  const newParents = mapCategories(parents.map((item) => treeList[item.id] || item));

  return [newItems, newParents];
};

export const getTreeByChildItems = (items: IABNodeType[]) => {
  if (!items?.length) {
    return [];
  }

  const treeList: { [key: string]: IABNodeType } = {};
  const treeListParentMapping: { [key: string]: string | null } = {};
  const requestIABsTree: { [key: string]: IABNodeType } = {};

  const findNode = (categories: IABNodeType[], parentNodeId: string | null) => {
    categories.forEach((category) => {
      treeList[category.id] = category;

      treeListParentMapping[category.id] = parentNodeId;

      if (category.categories?.length) {
        findNode(category.categories, category.id);
      }
    });
  };

  findNode(getIAB(), null);

  const getParentTree = (id: string) => {
    const parentId = treeListParentMapping[id];

    if (parentId) {
      const parentIAB = treeList[parentId];

      if (!requestIABsTree[parentId]) {
        requestIABsTree[parentId] = {
          id: parentIAB.id,
          name: parentIAB.name,
          probability: parentIAB.probability,
          version: parentIAB.version,
        };
      }

      getParentTree(parentIAB.id);
    }
  };

  items.forEach((iab) => {
    requestIABsTree[iab.id] = {
      id: iab.id,
      name: iab.name,
      probability: iab.probability,
      version: iab.version,
    };
  });

  items.forEach(({ id }) => getParentTree(id));

  return Object.keys(requestIABsTree)
    .reduce((acc, key) => {
      const rawIAB = requestIABsTree[key];

      return acc.concat(
        Object.keys(rawIAB).reduce(
          (acc$, key$) =>
            rawIAB[key$ as keyof IABNodeType] !== undefined
              ? { ...acc$, [key$]: rawIAB[key$ as keyof IABNodeType] }
              : acc$,
          {} as IABNodeType,
        ),
      );
    }, [] as IABNodeType[])
    .sort((a, b) => a.id.localeCompare(b.id));
};

export const iabFlatToTree = (iab: IABNodeType[]) => {
  const map: { [key: number | string]: number | string } = {};
  const roots = [];
  const array: Array<{ parentId: string } & IABNodeType> = iab.map((o) => ({
    ...o,
    parentId: o.id?.split('.').slice(0, -1).join('.'),
  }));

  for (let i = 0; i < array.length; i += 1) {
    map[array[i].id] = i;
    array[i].categories = [];
  }

  for (let i = 0; i < array.length; i += 1) {
    const node = array[i];
    if (node.parentId) {
      (array[map[node.parentId] as number].categories as IABNodeType[]).push(node);
    } else {
      roots.push(node);
    }
  }

  return roots;
};
