/// <reference types="@applicaster/applicaster-types" />
import * as R from "ramda";
import uuid from "uuid/v4";
import { createZappPipesContext } from "./ZappPipesContextFactory";

const initialContext = {
  context: null,
  setContext: () => {},
};

type EntryWithParent = ZappEntry & { parent?: EntryWithParent };

type SingleEntry = {
  data: EntryWithParent;
  route: string;
  id: string;
  parentId?: string;
};

type EntryContextType = { [K in string]: SingleEntry };

const getParentRoute = R.compose(
  R.concat("/"),
  R.join("/"),
  R.flatten,
  R.init,
  R.splitEvery(2),
  R.tail,
  R.split("/")
);

export function findParent(context, route, getChildren = false) {
  const parentRoute = getParentRoute(route);
  const parent = R.propOr(null, parentRoute, context);

  const children = getChildren
    ? R.compose(
        R.defaultTo(null),
        R.find(R.propEq("parentId", parent?.id)),
        R.values
      )(context)
    : null;

  return children || parent;
}

export function selector(
  context: EntryContextType,
  route: string,
  getChildren: boolean
): EntryWithParent {
  const parent = findParent(context, route, getChildren);

  return parent?.data;
}

function prepareContext(
  newContext: ZappEntry,
  currentContext: EntryContextType,
  route: string,
  getChildren: boolean
): EntryContextType {
  const parent = findParent(currentContext, route, getChildren);

  const newEntry = {
    route,
    id: uuid(),
    data: newContext,
  } as SingleEntry;

  const entryClone = R.clone(newEntry);

  if (
    parent?.data?.entry?.includes(newEntry?.data) ||
    R.equals(parent?.data, newEntry?.data)
  ) {
    // to avoid circular dependencies when a parent feed contains
    // the current entry, we remove entries from the parent
    const parentClone = R.clone(parent);
    delete parentClone.data.entry;

    entryClone.data.parent = parentClone.data;
    entryClone.parentId = parentClone.id as string;
  } else {
    if (parent) {
      entryClone.data.parent = parent.data;
      entryClone.parentId = parent.id as string;
    }
  }

  return R.assoc(route, entryClone, currentContext);
}

export const ZappPipesEntryContext = createZappPipesContext<
  EntryWithParent,
  EntryContextType
>(initialContext, {
  selector,
  prepareContext,
});
