import { JSX } from 'react';
import settings from 'settings';
import {
  ResolvedPageProps,
  ResolvedLayoutProps,
  ResolvedRootLayoutProps
} from '../../types';
import { redirect } from 'next/navigation';
import { ServerVariables } from '../../utils/server-variables';
import { ROUTES } from 'routes';
import logger from '../../utils/log';
import {
  decodePzValue,
  getPzSegmentsConfig,
  getBuiltInSegments,
  isLegacyMode
} from '../../utils/pz-segments';

type SegmentType = 'root-layout' | 'layout' | 'page';

interface SegmentDefaultsOptions {
  segmentType: SegmentType;
}

export const withSegmentDefaults =
  <T extends ResolvedPageProps | ResolvedLayoutProps | ResolvedRootLayoutProps>(
    Component: (
      props?: T
    ) => null | JSX.Element | Promise<JSX.Element> | Promise<JSX.Element[]>,
    options: SegmentDefaultsOptions
  ) =>
  async (props: any) => {
    const resolvedParams = await props.params;
    const resolvedSearchParams = props.searchParams
      ? await props.searchParams
      : undefined;

    // Normalize Next 16's raw Record<string, string | string[]> shape into a
    // URLSearchParams instance so v1 brands (which expect URLSearchParams) can
    // call .get/.has/etc without runtime breakage.
    const normalizedSearchParams =
      resolvedSearchParams instanceof URLSearchParams
        ? resolvedSearchParams
        : new URLSearchParams(
            Object.entries(resolvedSearchParams ?? {}).flatMap(([k, v]) =>
              Array.isArray(v)
                ? v.map((item): [string, string] => [k, String(item)])
                : v != null
                  ? ([[k, String(v)]] as [string, string][])
                  : []
            )
          );

    let componentProps = {
      ...props,
      params: resolvedParams,
      searchParams: normalizedSearchParams,
      ...('children' in props ? { children: (props as any).children } : {})
    } as T;

    let localeValue: string;
    let currencyValue: string;

    if (isLegacyMode(settings)) {
      localeValue = resolvedParams.locale;
      currencyValue = resolvedParams.currency;
    } else {
      const pzConfig = getPzSegmentsConfig(settings);
      const parsed = decodePzValue(resolvedParams.pz, pzConfig);
      const builtIn = getBuiltInSegments(parsed, settings);
      localeValue = builtIn.locale;
      currencyValue = builtIn.currency;
    }

    if (options.segmentType === 'root-layout') {
      componentProps = (await addRootLayoutProps(
        componentProps as ResolvedRootLayoutProps,
        localeValue
      )) as T;

      checkRedisVariables();
    }

    ServerVariables.locale = localeValue;
    ServerVariables.currency = currencyValue;

    return await (
      <>
        <Component {...componentProps} />
      </>
    );
  };

const addRootLayoutProps = async (
  componentProps: ResolvedRootLayoutProps,
  localeValue: string
) => {
  if (isLegacyMode(settings)) {
    const params = componentProps.params;
    if (
      params.commerce !==
        encodeURIComponent(decodeURI(settings.commerceUrl)) ||
      !settings.localization.locales.find((l) => l.value === localeValue)
    ) {
      return redirect(ROUTES.HOME);
    }
  } else if (
    !settings.localization.locales.find((l) => l.value === localeValue)
  ) {
    return redirect(ROUTES.HOME);
  }

  const { getTranslations } = settings.useOptimizedTranslations
    ? require('translations')
    : require('../../utils/server-translation');
  const translations = await getTranslations(localeValue);

  componentProps.translations = translations;

  const locale = settings.localization.locales.find(
    (l) => l.value === localeValue
  );
  const [isoCode] = locale.value.split('-');

  componentProps.locale = {
    ...locale,
    isoCode
  };

  return componentProps;
};

const checkRedisVariables = () => {
  const requiredVariableValues = [
    process.env.CACHE_HOST,
    process.env.CACHE_PORT
  ];

  if (!settings.usePrettyUrlRoute) {
    requiredVariableValues.push(process.env.CACHE_SECRET);
  }

  if (
    !requiredVariableValues.every((v) => v) &&
    process.env.NODE_ENV === 'production'
  ) {
    logger.warn('Redis cache variables are not set', {
      requiredVariables: ['CACHE_HOST', 'CACHE_PORT', 'CACHE_SECRET'],
      values: requiredVariableValues
    });
  }
};
