/**
 * JSON Parsing Utilities
 *
 * Shared utilities for safely parsing JSON values that may be strings or already parsed.
 */

/**
 * Parses a value that might be a JSON string or already an object.
 * Returns a typed object with fallback support.
 *
 * @param value - The value to parse (string, object, or undefined)
 * @param fallback - Fallback value if parsing fails (defaults to empty object)
 * @returns The parsed object or fallback
 *
 * @example
 * const schema = parseJsonObject<ConstructorSchema>(page.ui_schema_json, {});
 * const settings = parseJsonObject<Settings>(settingsValue, { enabled: false });
 */
export const parseJsonObject = <T>(value?: unknown, fallback?: T): T => {
  if (!value) return (fallback || ({} as T)) as T;

  try {
    if (typeof value === 'string') {
      const parsed = JSON.parse(value);
      return (parsed || fallback || {}) as T;
    }

    if (typeof value === 'object') {
      return value as T;
    }

    return (fallback || ({} as T)) as T;
  } catch {
    return (fallback || ({} as T)) as T;
  }
};

/**
 * Parses a JSON field that might be a string, object, or null.
 * More lenient than parseJsonObject - returns the original value on parse error.
 *
 * @param value - The value to parse
 * @returns The parsed value, original value, or null
 *
 * @example
 * const content = parseJsonField(element.content_json);
 * const style = parseJsonField(element.style_json);
 */
export const parseJsonField = (value: unknown): unknown => {
  if (value === null || value === undefined) return null;
  if (typeof value !== 'string') return value;
  if (!value.trim()) return null;

  try {
    return JSON.parse(value);
  } catch {
    return value;
  }
};

/**
 * Extracts preview text from parsed content by checking common text field names.
 *
 * @param content - Parsed content object
 * @returns The first text value found, or null
 *
 * @example
 * const previewText = getElementPreviewText(parseJsonField(element.content_json));
 */
export const getElementPreviewText = (content: unknown): string | null => {
  if (typeof content === 'string') return content;
  if (!content || typeof content !== 'object') return null;

  const candidateKeys = [
    'title',
    'text',
    'subtitle',
    'description',
    'body',
    'label',
    'value',
  ];
  const contentRecord = content as Record<string, unknown>;
  const firstTextKey = candidateKeys.find(
    (key) => typeof contentRecord[key] === 'string',
  );

  return firstTextKey ? String(contentRecord[firstTextKey]) : null;
};
