/// <reference types="@applicaster/applicaster-types" />
/// <reference types="@applicaster/zapp-react-native-ui-components" />
import React, { useEffect, useMemo } from "react";
import * as R from "ramda";
import { Platform } from "react-native";
import Url from "url";
import { favoritesListener } from "@applicaster/zapp-react-native-bridge/Favorites";
import {
  getInflatedDataSourceUrl,
  getSearchContext,
  useFeedLoader,
  useFeedRefresh,
  useRoute,
} from "@applicaster/zapp-react-native-utils/reactHooks";

import { ComponentDataSourceContext, ZappPipesDataProps } from "../types";
import { useScreenStateStore } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useScreenStateStore";
import { isVerticalListOrGrid } from "../utils";
import { useFilter } from "../utils/useFilter";
import { subscribeForKeyChanges } from "@applicaster/zapp-pipes-v2-client";

const FAVORITES_TYPE = "FAVOURITES";

function getDataSourceUrl({
  component,
  feedUrl,
  screenData,
  entryContext,
  searchContext,
  screenContext,
}): { dataSourceUrl: string | null; type: string | null } {
  if (feedUrl) {
    return { dataSourceUrl: feedUrl, type: null };
  }

  if (component.data) {
    const {
      data: { source, type, mapping },
    } = component;

    if (type === FAVORITES_TYPE) {
      return { dataSourceUrl: source || FAVORITES_TYPE, type };
    }

    if (source) {
      if (type === "APPLICASTER_COLLECTION" && !R.includes("://", source)) {
        return {
          dataSourceUrl: `applicaster://fetchData?type=${type}&collectionId=${source}`,
          type,
        };
      }

      if (type === "APPLICASTER_CATEGORY" && !R.includes("://", source)) {
        return {
          dataSourceUrl: `applicaster://fetchData?type=${type}&categoryId=${source}&platform=${Platform.OS}`,
          type,
        };
      }

      if (mapping) {
        const contexts = {
          entry: entryContext,
          screen: screenContext || screenData,
          search: getSearchContext(searchContext, mapping),
        };

        const dataSourceUrl = getInflatedDataSourceUrl({
          source,
          mapping,
          contexts,
        });

        if (!dataSourceUrl) {
          return { dataSourceUrl: null, type: null };
        }

        return { dataSourceUrl, type };
      }

      return { dataSourceUrl: source, type };
    }
  }

  return { dataSourceUrl: null, type: null };
}

function getMethodAndParams(component) {
  const method: "get" | "post" = R.pathOr("get", ["data", "method"], component);

  const bodyParams: Record<string, unknown> = R.pathOr(
    {},
    ["data", "bodyParams"],
    component
  );

  return {
    method,
    bodyParams,
  };
}

function getListenerFromPlugin(
  dataSourceUrl: string,
  plugins: QuickBrickPlugin[]
): AddDataSourceListener | void {
  const url = Url.parse(dataSourceUrl, false);

  const addListener = R.compose(
    R.path(["module", "addDataSourceListener"]),
    R.find(R.propEq("identifier", url.host))
  )(plugins);

  return addListener;
}

function applyItemLimit(zappPipesData, component) {
  if (!zappPipesData || !zappPipesData.data) {
    return zappPipesData;
  }

  const { data } = zappPipesData;

  const {
    rules: { item_limit },
  } = component;

  if (item_limit && data.entry && data.entry.length) {
    return {
      ...zappPipesData,
      data: {
        ...data,
        entry: R.slice(
          0,
          Math.min(Number(item_limit), R.length(data.entry)),
          data.entry
        ),
      },
    };
  }

  return zappPipesData;
}

type UrlFeedResolverProps = ComponentDataSourceContext & {
  children: (dataProps: ZappPipesDataProps) => React.ReactNode;
};

export function UrlFeedResolver({
  children,
  component,
  feedUrl,
  isLast,
  entryContext,
  screenContext,
  searchContext,
  screenData,
  plugins,
}: UrlFeedResolverProps) {
  const { dataSourceUrl, type } = useMemo(
    () =>
      getDataSourceUrl({
        component,
        feedUrl,
        screenData,
        entryContext,
        searchContext,
        screenContext,
      }),
    [
      component?.id,
      feedUrl,
      entryContext,
      searchContext,
      screenContext,
      screenData?.id,
    ]
  );

  const shouldClearCache = useMemo(
    () => Boolean(component?.rules?.clear_cache_on_reload),
    [component?.id]
  );

  const pipesOptions = useMemo(
    () => ({
      clearCache: type === FAVORITES_TYPE,
      loadLocalFavorites: type === FAVORITES_TYPE,
      silentRefresh: !shouldClearCache,
      ...getMethodAndParams(component),
    }),
    [type, shouldClearCache, component]
  );

  const { reloadData, loadNext, error, loading, url, data } = useFeedLoader({
    feedUrl: dataSourceUrl ?? "",
    pipesOptions,
  });

  const { pathname } = useRoute();
  const screenStateStore = useScreenStateStore();

  // Setup listeners for data source URL
  useEffect(() => {
    if (!reloadData) {
      return;
    }

    if (dataSourceUrl?.includes("pipesv2://")) {
      (
        getListenerFromPlugin(dataSourceUrl, plugins) as AddDataSourceListener
      )?.(reloadData);
    } else {
      return subscribeForKeyChanges({
        url: dataSourceUrl,
        pathname,
        screenStateStore,
        callback: reloadData,
      });
    }
  }, [dataSourceUrl, reloadData, pathname, screenStateStore]);

  // Setup favorites listener
  useEffect(() => {
    if (type === FAVORITES_TYPE && reloadData) {
      favoritesListener.on("FAVORITES_CHANGED", reloadData);

      return () => {
        favoritesListener.removeHandler("FAVORITES_CHANGED", reloadData);
      };
    }
  }, [type, reloadData]);

  // Apply feed refresh hook
  useFeedRefresh({
    reloadData,
    component,
  });

  const loadNextData = useMemo(
    () => (!isLast && isVerticalListOrGrid(component) ? undefined : loadNext),
    [isLast, component, loadNext]
  );

  const applyItemFilter = useFilter({ url, loading, data, error }, component);

  const zappPipesDataProps = useMemo(() => {
    const pipeData = { url, loading, data, error };

    return {
      zappPipesData: applyItemLimit(applyItemFilter(pipeData), component),
      reloadData,
      loadNextData,
    };
  }, [
    url,
    loading,
    data,
    error,
    component,
    reloadData,
    loadNextData,
    applyItemFilter,
  ]);

  return <>{children(zappPipesDataProps)}</>;
}
