'use client';

import { useDeepCompareMemo } from '@react-hookz/web';
import { defaults } from 'lodash-es';
import { createContext, useContext, useMemo } from 'react';

/**
 * General properties that can be set for logging recommendation and/or logging
 * context for given items
 */
export type TelemetryContextData = {
  recommendationRunId?: string;
  recommendationItemId?: string;
  clientData?: Record<string, string | number | boolean | null>;
};

interface TelemetryContextType {
  id?: string;
  data: TelemetryContextData;
  parent?: TelemetryContextType;
}

/**
 * NOTE: Do not use `<TelemetryContext>` directly.
 *
 * Use `TelemetryContextProvider` to set a context and `useTelemetryData` or
 * `useTelemetryAncestorData` to consume it.
 */
export const TelemetryContext = createContext<TelemetryContextType | undefined>(
  undefined
);

export type TelemetryProviderProps = React.PropsWithChildren<
  TelemetryContextData & {
    id?: string;
    merge?: boolean;
  }
>;

/**
 * Sets telemetry context props, including recommendation run and/or item ID, as well as logging context type and ID
 */
export const TelemetryContextProvider: React.FC<TelemetryProviderProps> = (
  props
) => {
  const { children, id, merge, ...restProps } = props;

  const telemetryData = useDeepCompareMemo(() => restProps, [restProps]);
  const parentTelemetryValue = useContext(TelemetryContext);
  const telemetryContextValue = useMemo(
    () => ({
      id,
      data: merge
        ? defaults({}, telemetryData, parentTelemetryValue?.data)
        : telemetryData,
      parent: parentTelemetryValue,
    }),
    [id, telemetryData, parentTelemetryValue, merge]
  );

  return (
    <TelemetryContext.Provider value={telemetryContextValue}>
      {children}
    </TelemetryContext.Provider>
  );
};

/**
 * Accesses the contextual telemetry data
 */
export function useTelemetryData() {
  const telemetryContextValue = useContext(TelemetryContext);
  return telemetryContextValue?.data;
}

/**
 * Accesses the contextual telemetry data along with any ancestor data
 * available from multiple `<TelemetryContextProvider>` wrappers
 */
export function useTelemetryAncestorData() {
  const telemetryContextValue = useContext(TelemetryContext);
  return useMemo(() => {
    const ancestorData: Array<{ id?: string; data: TelemetryContextData }> = [];
    let current = telemetryContextValue;
    while (current) {
      const { id, data, parent } = current;
      ancestorData.push({ id, data });
      current = parent;
    }
    return ancestorData;
  }, [telemetryContextValue]);
}
