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

import { useEffect, useMemo } from "react";
import { useDispatch } from "react-redux";

import { useLayoutPresets } from "@applicaster/zapp-react-native-redux/hooks/useLayoutPresets";
import { useZappPipesFeeds } from "@applicaster/zapp-react-native-redux/hooks/useZappPipesFeeds";
import { loadPipesData } from "@applicaster/zapp-react-native-redux/ZappPipes";
import { isEmptyOrNil } from "@applicaster/zapp-react-native-utils/cellUtils";
import { Categories } from "./logger";
import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
import {
  ZappPipesEntryContext,
  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";
// types reference

declare type CurationEntry = { preset_name: string; feed_url: string };

type Feeds = Record<string, ZappPipesData>;

type LayoutPresets = PresetsMapping["presets_mappings"];

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 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 dispatch = useDispatch();

  const smartComponents = useMemo(
    () => components?.filter?.(isSmartComponent) ?? [],
    [components]
  );

  const { pathname } = useRoute();
  const [entryContext] = ZappPipesEntryContext.useZappPipesContext(pathname);
  const [searchContext] = ZappPipesSearchContext.useZappPipesContext();
  const [screenContext] = ZappPipesScreenContext.useZappPipesContext();

  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] = mapping
        ? getInflatedDataSourceUrl({
            source: url,
            contexts: {
              entry: entryContext,
              screen: screenContext,
              search: getSearchContext(searchContext, mapping),
            },
            mapping,
          })
        : url;
    });

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

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

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

  const feeds = useZappPipesFeeds(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
  );
};
