/**
 * Merge With Defaults
 *
 * Utilities for safely merging API data with form defaults.
 * Handles missing fields, type mismatches, and nested objects.
 *
 * @module lib/forms/utils/mergeWithDefaults
 */

/**
 * Safely get a value with type checking and default fallback
 *
 * @param value - Value to check
 * @param defaultValue - Default to use if value is invalid
 * @param typeCheck - Type checking function
 * @returns Valid value or default
 *
 * @example
 * ```ts
 * const temp = safeValue(apiData.temperature, 0.7, (v) => typeof v === 'number');
 * ```
 */
export function safeValue<T>(
  value: unknown,
  defaultValue: T,
  typeCheck: (v: unknown) => v is T
): T {
  return typeCheck(value) ? value : defaultValue;
}

/**
 * Type guard for numbers
 */
export function isNumber(v: unknown): v is number {
  return typeof v === 'number' && !Number.isNaN(v);
}

/**
 * Type guard for strings
 */
export function isString(v: unknown): v is string {
  return typeof v === 'string';
}

/**
 * Type guard for booleans
 */
export function isBoolean(v: unknown): v is boolean {
  return typeof v === 'boolean';
}

/**
 * Type guard for arrays
 */
export function isArray<T>(v: unknown, itemCheck?: (item: unknown) => item is T): v is T[] {
  if (!Array.isArray(v)) return false;
  if (itemCheck) return v.every(itemCheck);
  return true;
}

/**
 * Type guard for objects (non-null, non-array)
 */
export function isObject(v: unknown): v is Record<string, unknown> {
  return typeof v === 'object' && v !== null && !Array.isArray(v);
}

/**
 * Deep merge two objects, with source values taking precedence
 * Only merges values that pass type checking against defaults
 *
 * @param defaults - Default values (used as type template)
 * @param source - Source values to merge (from API)
 * @returns Merged object with all fields from defaults
 *
 * @example
 * ```ts
 * const defaults = { temperature: 0.7, max_tokens: 8192, top_p: 0.95 };
 * const apiData = { temperature: 0.5 }; // missing max_tokens and top_p
 * const merged = deepMergeWithDefaults(defaults, apiData);
 * // { temperature: 0.5, max_tokens: 8192, top_p: 0.95 }
 * ```
 */
export function deepMergeWithDefaults<T extends Record<string, unknown>>(
  defaults: T,
  source: Partial<T> | null | undefined
): T {
  if (!source) return { ...defaults };

  const result = { ...defaults } as T;

  for (const key of Object.keys(defaults) as Array<keyof T>) {
    const defaultValue = defaults[key];
    const sourceValue = source[key];

    // Skip if source doesn't have this key
    if (!(key in source) || sourceValue === undefined) {
      continue;
    }

    // Handle nested objects recursively
    if (isObject(defaultValue) && isObject(sourceValue)) {
      result[key] = deepMergeWithDefaults(
        defaultValue as Record<string, unknown>,
        sourceValue as Record<string, unknown>
      ) as T[keyof T];
      continue;
    }

    // For primitives and arrays, only use source if types match
    if (typeof sourceValue === typeof defaultValue) {
      result[key] = sourceValue as T[keyof T];
    }
    // Special case: default is null but source has a value (nullable fields)
    else if (defaultValue === null && sourceValue !== null) {
      result[key] = sourceValue as T[keyof T];
    }
    // Special case: allow null for nullable fields
    else if (sourceValue === null && defaultValue === null) {
      result[key] = null as T[keyof T];
    }
  }

  return result;
}

/**
 * Create a form values initializer from API data
 *
 * This is a higher-order function that creates a type-safe initializer
 * for transforming API responses into form values with defaults.
 *
 * @param defaults - Default form values
 * @param transform - Optional transform function for custom mapping
 * @returns Function that takes API data and returns form values
 *
 * @example
 * ```ts
 * const initializeAgentForm = createFormInitializer(
 *   defaultAgentTypeValues,
 *   (apiData, defaults) => ({
 *     ...defaults,
 *     name: apiData?.name ?? defaults.name,
 *     model_params: deepMergeWithDefaults(
 *       defaults.model_params,
 *       apiData?.model_params
 *     ),
 *   })
 * );
 *
 * // Usage
 * const formValues = initializeAgentForm(apiResponse);
 * ```
 */
export function createFormInitializer<TForm, TApi = Partial<TForm>>(
  defaults: TForm,
  transform?: (apiData: TApi | null | undefined, defaults: TForm) => TForm
): (apiData: TApi | null | undefined) => TForm {
  return (apiData) => {
    if (transform) {
      return transform(apiData, defaults);
    }
    // Default behavior: deep merge
    return deepMergeWithDefaults(
      defaults as Record<string, unknown>,
      apiData as Record<string, unknown> | null | undefined
    ) as TForm;
  };
}
