import { all, equals, isEmpty, path, pluck, prop, values } from "ramda";

import { useEffect, useMemo } from "react";

import {
  useLayoutPresets,
  useZappPipesFeed,
} from "@applicaster/zapp-react-native-redux";
import { isEmptyOrNil } from "@applicaster/zapp-react-native-utils/cellUtils";
import { Categories } from "./logger";
import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
import { useScreenContext } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";

import {
  ZappPipesScreenContext,
  ZappPipesSearchContext,
} from "@applicaster/zapp-react-native-ui-components/Contexts";

import {
  getInflatedDataSourceUrl,
  getSearchContext,
} from "@applicaster/zapp-react-native-utils/reactHooks/feed/useInflatedUrl";

import { produce } from "immer";
import { useLoadPipesDataDispatch } from "@applicaster/zapp-react-native-utils/reactHooks";

// types reference

declare interface CurationEntry {
  preset_name: string;
  feed_url: string;
}

type Feeds = Record<string, ZappPipesData>;

type LayoutPresets = PresetsMapping["presets_mappings"];

const TABS_SCREEN_TYPE = "tabs_screen";
const QB_TABS_SCREEN_TYPE = "quick-brick-tabs";
const SMART_COMPONENT_TYPE = "quick-brick-smart-component";
const SOURCE_PATH = ["data", "source"];
const MAPPING_PATH = ["data", "mapping"];

const isSmartComponent = (component) =>
  component.component_type === SMART_COMPONENT_TYPE;

const logger = createLogger({ category: Categories.CURATION_API });

export const getTransformedPreset = (
  preset: CurationEntry,
  componentId: ZappUIComponent["id"],
  index: number,
  layoutPresets: LayoutPresets
): ZappUIComponent | undefined => {
  const presetComponent = layoutPresets?.[preset?.preset_name];

  if (!presetComponent) {
    logger.log_error(
      `Preset "${preset?.preset_name}" missing or wrong data format`,
      { entry: preset }
    );

    return;
  }

  const updatedPresetComponent = produce(presetComponent, (draft) => {
    draft.id = `${componentId}-${preset?.feed_url}-${index}`;
    draft.data.source = preset?.feed_url;

    if (draft.ui_components) {
      draft.ui_components.forEach((comp, uiIndex) => {
        comp.id = `${draft.id}-${uiIndex}`;
      });
    }
  });

  return updatedPresetComponent;
};

export const enrichComponent = (
  comp: ZappUIComponent,
  index: number,
  feeds: Feeds,
  layoutPresets: LayoutPresets,
  urlsMap: { [key: string]: string }
): ZappUIComponent[] | null => {
  if (!isSmartComponent(comp)) {
    return [comp];
  }

  const presets = prop<string[]>(urlsMap[comp.id], feeds);

  if (presets && presets.data && presets.data.entry) {
    return presets.data.entry
      .map((preset: CurationEntry, _index) =>
        getTransformedPreset(preset, comp.id, index + _index, layoutPresets)
      )
      .filter(Boolean);
  }

  return null;
};

export const getFinalComponents = (
  enrichedComponents: Array<ZappUIComponent>,
  smartComponents: ZappUIComponent[],
  urls: string[],
  feeds: Feeds,
  components: ZappUIComponent[]
): ZappUIComponent[] => {
  if (isEmpty(smartComponents)) {
    return components;
  }

  if (all(isEmptyOrNil)(urls)) {
    return enrichedComponents;
  }

  if (isEmptyOrNil(feeds)) {
    return [];
  }

  if (all(equals(false))(values(pluck("loading", feeds)))) {
    return enrichedComponents;
  } else {
    return [];
  }
};

export const useCurationAPI = (
  components: Array<ZappUIComponent>
): ZappUIComponent[] => {
  const smartComponents = useMemo(
    () => components?.filter?.(isSmartComponent) ?? [],
    [components]
  );

  const [searchContext] = ZappPipesSearchContext.useZappPipesContext();
  const [screenContext] = ZappPipesScreenContext.useZappPipesContext();

  const screenContextType = screenContext?.type;

  const isNestedScreen =
    screenContextType === TABS_SCREEN_TYPE ||
    screenContextType === QB_TABS_SCREEN_TYPE;

  const screenContextData = useScreenContext();

  const entryContext = ((isNestedScreen && screenContextData?.nested?.entry
    ? screenContextData?.nested?.entry
    : (screenContextData?.entry?.payload ?? screenContextData?.entry)) ||
    {}) as ZappEntry;

  const urlsMap = useMemo<{ [key: string]: string }>(() => {
    const map = {};

    smartComponents?.forEach?.((component) => {
      const url = path(SOURCE_PATH, component);
      const mapping = path(MAPPING_PATH, component);

      map[component.id] = getInflatedDataSourceUrl({
        source: url,
        contexts: {
          entry: entryContext,
          screen: screenContext,
          search: getSearchContext(searchContext, mapping),
        },
        mapping,
      });
    });

    return map;
  }, [smartComponents, entryContext, screenContext, searchContext]);

  const urls = useMemo<string[]>(() => Object.values(urlsMap), [urlsMap]);

  const loadPipesDataDispatcher = useLoadPipesDataDispatch();

  useEffect(() => {
    urls.forEach((url, index) => {
      if (url) {
        loadPipesDataDispatcher(url, { clearCache: false });
      } else {
        logger.log_error("Curation url is empty", {
          componentId: smartComponents?.[index]?.id,
        });
      }
    });
  }, [urls, loadPipesDataDispatcher]);

  const feeds = useZappPipesFeed(urls);
  const layoutPresets = useLayoutPresets();

  const enrichedComponents = useMemo(() => {
    if (!components) return [];

    return components.reduce((acc, comp, index) => {
      const enrichedComp = enrichComponent(
        comp,
        index,
        feeds,
        layoutPresets,
        urlsMap
      );

      return enrichedComp ? [...acc, ...enrichedComp] : acc;
    }, [] as Array<ZappUIComponent>);
  }, [components, feeds, layoutPresets, urlsMap]);

  return getFinalComponents(
    enrichedComponents,
    smartComponents,
    urls,
    feeds,
    components
  );
};
