import { isDev } from "@djangocfg/ui-core/lib";
import consola from 'consola';

import { RJSFSchema } from '@rjsf/utils';

/**
 * Utility functions for JSON Schema Form
 */

/**
 * Safely validates and normalizes JSON Schema
 * Ensures schema is valid before rendering
 */
export function validateSchema(schema: any): RJSFSchema | null {
  if (!schema || typeof schema !== 'object') {
    if (isDev) {
      consola.error('[JsonSchemaForm] Invalid schema: must be an object', schema);
    }
    return null;
  }

  // Basic schema validation - more permissive
  // Schema is valid if it has type OR properties OR $ref OR $schema
  const hasValidStructure =
    schema.type ||
    schema.properties ||
    schema.$ref ||
    schema.$schema;

  if (!hasValidStructure) {
    if (isDev) {
      consola.error('[JsonSchemaForm] Invalid schema: missing type, properties, $ref, or $schema', schema);
    }
    return null;
  }

  if (isDev) {
    consola.success('[JsonSchemaForm] Schema validated successfully:', {
      type: schema.type,
      title: schema.title,
      hasProperties: !!schema.properties,
      hasRequired: !!schema.required,
    });
  }

  return schema as RJSFSchema;
}

/**
 * Safely normalizes form data
 * Removes undefined values and ensures data structure matches schema
 */
export function normalizeFormData<T = any>(
  formData: any,
  schema: RJSFSchema
): T {
  if (formData === null || formData === undefined) {
    return (schema.type === 'object' ? {} : schema.type === 'array' ? [] : null) as T;
  }

  // Deep clone to avoid mutations
  const normalized = JSON.parse(JSON.stringify(formData));

  // Remove undefined values recursively
  return removeUndefined(normalized) as T;
}

/**
 * Recursively removes undefined values from an object
 */
function removeUndefined(obj: any): any {
  if (obj === null || obj === undefined) {
    return obj;
  }

  if (Array.isArray(obj)) {
    return obj.map(removeUndefined).filter((item) => item !== undefined);
  }

  if (typeof obj === 'object') {
    const cleaned: any = {};
    for (const key in obj) {
      if (obj[key] !== undefined) {
        cleaned[key] = removeUndefined(obj[key]);
      }
    }
    return cleaned;
  }

  return obj;
}

/**
 * Merges schema defaults with form data
 */
export function mergeDefaults(
  formData: any,
  schema: RJSFSchema
): any {
  if (!schema) return formData;

  const result = { ...formData };

  if (schema.type === 'object' && schema.properties) {
    for (const [key, propSchema] of Object.entries(schema.properties)) {
      const prop = propSchema as RJSFSchema;

      // Apply default if field is missing
      if (result[key] === undefined && prop.default !== undefined) {
        result[key] = prop.default;
      }

      // Recursively merge nested objects
      if (prop.type === 'object' && result[key]) {
        result[key] = mergeDefaults(result[key], prop);
      }
    }
  }

  return result;
}

/**
 * Safely parses JSON string with error handling
 */
export function safeJsonParse<T = any>(
  jsonString: string,
  fallback: T
): T {
  try {
    return JSON.parse(jsonString);
  } catch (error) {
    consola.error('[JsonSchemaForm] JSON parse error:', error);
    return fallback;
  }
}

/**
 * Safely stringifies object to JSON
 */
export function safeJsonStringify(
  obj: any,
  pretty: boolean = true
): string {
  try {
    return JSON.stringify(obj, null, pretty ? 2 : 0);
  } catch (error) {
    consola.error('[JsonSchemaForm] JSON stringify error:', error);
    return '{}';
  }
}

/**
 * Checks if schema has required fields
 */
export function hasRequiredFields(schema: RJSFSchema): boolean {
  return Array.isArray(schema.required) && schema.required.length > 0;
}

/**
 * Gets all required field paths from schema
 */
export function getRequiredFields(
  schema: RJSFSchema,
  prefix: string = ''
): string[] {
  const required: string[] = [];

  if (schema.required && Array.isArray(schema.required)) {
    required.push(...schema.required.map((field) =>
      prefix ? `${prefix}.${field}` : field
    ));
  }

  if (schema.type === 'object' && schema.properties) {
    for (const [key, propSchema] of Object.entries(schema.properties)) {
      const prop = propSchema as RJSFSchema;
      const fieldPath = prefix ? `${prefix}.${key}` : key;
      required.push(...getRequiredFields(prop, fieldPath));
    }
  }

  return required;
}

/**
 * Validates form data against required fields
 */
export function validateRequiredFields(
  formData: any,
  schema: RJSFSchema
): { valid: boolean; missing: string[] } {
  const requiredFields = getRequiredFields(schema);
  const missing: string[] = [];

  for (const field of requiredFields) {
    const value = getNestedValue(formData, field);
    if (value === undefined || value === null || value === '') {
      missing.push(field);
    }
  }

  return {
    valid: missing.length === 0,
    missing,
  };
}

/**
 * Gets nested value from object by path
 */
function getNestedValue(obj: any, path: string): any {
  return path.split('.').reduce((current, key) => current?.[key], obj);
}
