import type { ReactNode } from "react";
import type { PageProps, ParallelModalSearchParams } from "@/core/types/common";

import getStores from "@/app/_actions/getStores";
import getProductList from "@/app/(visualization)/project/@modals/(.)customization/_actions/getProductList";
import getProductFilters from "@/app/(visualization)/project/@modals/(.)customization/_actions/getProductFilters";
import { getServerSession } from "next-auth";
import { getFavourites } from "@/app/(visualization)/project/@modals/(.)customization/_actions/favourites";
import { authOptions } from "@/core/lib/auth/options";
import Logger from "@/core/utils/helpers/logger";

import ParallelModalWrapper from "@/core/components/ParallelModal";
import CustomizationComponent from "@/app/(visualization)/project/@modals/(.)customization/_components/Customization";

type Params = { segments: string[] };
/**
 * This is separated _**server page**_ component which placed in the parallel slot `@modals`.
 * The {@link CustomizationComponent} has a large infrastructure, so this way we separate it as completed _logical_ part.</br>
 * Such as this the server **page**, we're able to fetch required initial data on the server side and then put it to the children client components.
 * The fetched data could be **revalidated** by some conditions as well if needed, with revalidation by tags – {@link revalidateTag}.</br>
 * @param props {@link PageParams<P, S>} props of the server page. In the current case, it is a **_parallel route_** slot `modals`.
 * @returns {ReactNode} – client component node which got from _server-side_ fetched data as its `props`.
 * @constructor
 */
export default async function CustomizationModal(
  props: PageProps<Params, ParallelModalSearchParams & Record<string, string>>): Promise<ReactNode> {
  Logger.dev("CustomizationModal.props.searchParams: ", props.searchParams);

  const session = await getServerSession(authOptions);
  Logger.dev("[CustomizationModal.session]: ", session);

  /** Route segments which defines the selected categories & subcategories of product lists */
  const category: string = props.params.segments[0].replace("textures", "texture"); // TODO
  const subcategory: string | undefined = props.params.segments[1];
  const subcategoryType: string | undefined = props.searchParams?.subcategoryType;
  const savedProducts: boolean = (props.searchParams?.["saved"] !== undefined);

  const shopIds = props.searchParams["shopIds"] as string | string[];
  const shops: string[] = shopIds
    ? typeof shopIds === "string"
      ? props.searchParams["shopIds"]?.split(",")
      : shopIds as string[]
    : [];
  Logger.log("SELECTED SHOPS: ", shops);

  /** Markers for conditional fetching data */
  const isPatternTiles: boolean = props.params.segments.includes("tile_patterns");
  const isGrout: boolean = props.params.segments.includes("grout");

  /** Request filter search params */
  const requestFilterParams = new URLSearchParams();
  /** Object to combine same params keys */
  const combinedParams: Record<string, string> = {};
  /** Combine params .forEach callback */
  const combineParams = (key: string) => {
    const filterName = key.replace(/^(bar|common)/, '').toLowerCase();
    if (combinedParams.hasOwnProperty(filterName)) {
      combinedParams[filterName] += `,${props.searchParams[key]}`;
    } else {
      combinedParams[filterName] = props.searchParams[key];
    }
  }
  /** Collecting common filter options below the product lists */
  Object
    .keys(props.searchParams)
    .filter(o => o !== "modal" && o !== "origin" && o.startsWith("common"))
    .forEach(combineParams);
  /** Collecting barFilter options if barFilter has been applied */
  if (props.searchParams?.["barFilter"] === "applied") {
    Object
      .keys(props.searchParams)
      .filter(o => o !== "modal" && o !== "origin" && o !== "barFilter" && o.startsWith("bar"))
      .forEach(combineParams);
  }
  /** Collecting unique param values in alphabetical order to correct caching the results */
  const paramsEntries = Object.entries(combinedParams)
    .sort(([keyA], [keyB]) => keyA.localeCompare(keyB))
    .map(([key, value]) => [key, value.split(',').sort().join(',')]);
  if (paramsEntries.length) {
    for (const [key, value] of paramsEntries) {
      const uniqueValues = Array.from(new Set(value.split(','))).join(',');
      requestFilterParams.append(key.toLowerCase(), uniqueValues);
    }
  }
  /** Development logging actual request params */
  Logger.dev("Products request search params: ", requestFilterParams)
  /**
   * Create cached requests with keyParts signatures. Request caching will depend on changes of these key signatures
   * TODO: Also, the dynamic data such as gotten from cookies() OR headers() will not supported by Next's cache.
   * TODO: So if needed, this data should be gotten outside the cached function and passed to them as arguments.
   * */
  //const getCachedStores = cache
  //  .unstable_cache(getStores, [], { revalidate: 360, tags: ["cached_stores"] });
  //const getCachedFilters = cache
  //  .unstable_cache(getProductFilters, [props.searchParams?.shopIds?.toString()], { tags: ["cached_filters"] });
  ///** Cached Products */
  //const getCachedProducts = cache.unstable_cache(
  //  getProductList,
  //  [requestFilterParams.toString(), isPatternTiles.toString(), isGrout.toString(), props.searchParams?.shopIds?.toString()],
  //  { tags: ["cached_products"] });
  //const getCachedTiles = cache.unstable_cache(
  //  getProductList,
  //  [requestFilterParams.toString(), isPatternTiles.toString(), props.searchParams?.shopIds?.toString()],
  //  { tags: ["cached_tiles"] });
  //const getCachedGrout = cache.unstable_cache(
  //  getProductList,
  //  [requestFilterParams.toString(), props.searchParams?.shopIds?.toString()],
  //  { tags: ["cached_grout"] });

  /** Fetch stores data first */
  const stores = await getStores(shops);
  /** The concurrent conditional fetching data */
  const [filters, products, grout, tiles, favourites] = await Promise.all([
    shops ? getProductFilters(shops) : [],
    (!isPatternTiles && !isGrout)
      ? getProductList(
          category, subcategory, subcategoryType,
        requestFilterParams, shops, undefined, savedProducts) : [],
    getProductList("texture", "grout", undefined, undefined, shops),
    isPatternTiles ? getProductList("texture", "floor_tile", undefined, undefined, shops) : undefined,
    getFavourites(session?.user.id || "")
  ]);
  /** Development logging */
  Logger.dev("PROMISE.ALL - STORES: ", stores);
  Logger.dev("PROMISE.ALL - FILTERS: ", filters);
  Logger.dev("PROMISE.ALL - PRODUCTS.length: ", products?.length);
  Logger.dev("PROMISE.ALL - FAVOURITES: ", favourites);
  Logger.dev("PROMISE.ALL - GROUT.length: ", grout?.length);
  Logger.dev("PROMISE.ALL - TILES.length: ", tiles?.length);

  return (
    <ParallelModalWrapper
      {...props.searchParams}
      className={`absolute top-[181px] left-[29px]`}
      hideOnQuery={["perspective", "SetWalk"]}
      id={"customization-parallel-modal"}
    >
      <CustomizationComponent {...props.params}
                              initialStores={stores}
                              initialGrout={grout}
                              initialTiles={tiles}
                              initialFilters={filters}
                              initialFavourites={favourites}
                              initialProducts={products} />
    </ParallelModalWrapper>
  );
}
