/**
 * A value type for custom paywall variables that can be passed to paywalls at runtime.
 *
 * Custom variables allow developers to personalize paywall text with dynamic values.
 * Variables are defined in the RevenueCat dashboard and can be overridden at runtime.
 *
 * @example
 * ```typescript
 * RevenueCatUI.presentPaywall({
 *   customVariables: {
 *     'player_name': CustomVariableValue.string('John'),
 *     'level': CustomVariableValue.number(42),
 *     'is_premium': CustomVariableValue.boolean(true),
 *   },
 * });
 * ```
 *
 * In the paywall text (configured in the dashboard), use the `custom.` prefix:
 * ```
 * Hello {{ custom.player_name }}!
 * ```
 */
export type CustomVariableValue =
  | { readonly type: 'string'; readonly value: string }
  | { readonly type: 'number'; readonly value: number }
  | { readonly type: 'boolean'; readonly value: boolean };

/**
 * Factory methods for creating CustomVariableValue instances.
 */
export const CustomVariableValue = {
  /**
   * Creates a string custom variable value.
   * @param value The string value for the custom variable.
   * @returns A CustomVariableValue containing the string.
   */
  string: (value: string): CustomVariableValue => ({ type: 'string', value }),

  /**
   * Creates a numeric custom variable value.
   * @param value The numeric value for the custom variable.
   * @returns A CustomVariableValue containing the number.
   */
  number: (value: number): CustomVariableValue => ({ type: 'number', value }),

  /**
   * Creates a boolean custom variable value.
   * @param value The boolean value for the custom variable.
   * @returns A CustomVariableValue containing the boolean.
   */
  boolean: (value: boolean): CustomVariableValue => ({ type: 'boolean', value }),
} as const;

/**
 * A map of custom variable names to their values.
 */
export type CustomVariables = { [key: string]: CustomVariableValue };

/**
 * Internal type for custom variables as sent to native bridge.
 * Values preserve their native types (string, number, boolean).
 * @internal
 */
export type NativeCustomVariables = { [key: string]: string | number | boolean };

/**
 * Converts CustomVariables to a native-typed map for the bridge.
 * @internal
 * @visibleForTesting
 */
export function convertCustomVariablesToNativeMap(
  customVariables: CustomVariables | undefined
): NativeCustomVariables | null {
  if (!customVariables) return null;
  const result: NativeCustomVariables = {};
  for (const key of Object.keys(customVariables)) {
    const variable = customVariables[key];
    if (variable) {
      if (variable.type === 'number' && !Number.isFinite(variable.value)) {
        console.warn(
          `[RevenueCatUI] Custom variable '${key}' has non-finite number value (${variable.value}). Skipping.`
        );
        continue;
      }
      result[key] = variable.value;
    }
  }
  return result;
}

/**
 * @internal
 * @visibleForTesting
 */
export const convertCustomVariablesToStringMap = convertCustomVariablesToNativeMap;

/**
 * Transforms options to native format, converting CustomVariables to native-typed map.
 * @internal
 * @visibleForTesting
 */
export function transformOptionsForNative<T extends { customVariables?: CustomVariables }>(
  options: T | undefined
): (Omit<T, 'customVariables'> & { customVariables?: NativeCustomVariables | null }) | undefined {
  if (!options) return undefined;
  const { customVariables, ...rest } = options;
  return {
    ...rest as Omit<T, 'customVariables'>,
    customVariables: convertCustomVariablesToNativeMap(customVariables),
  };
}
