/**
 * Schema Form Generator
 * Dynamically generates form fields based on JSON Schema
 * Used for Helm Chart values configuration
 */
import React from 'react';
import { AlertCircle, ChevronDown, ChevronRight } from 'lucide-react';
import { SearchableSelect } from './SearchableSelect';
import { DropdownSelect } from './DropdownSelect';

type JsonSchemaPrimitiveType = 'string' | 'integer' | 'number' | 'boolean' | 'object' | 'array';

export interface JsonSchema extends Record<string, unknown> {
  type?: JsonSchemaPrimitiveType | string;
  title?: string;
  description?: string;
  default?: unknown;
  enum?: unknown[];
  format?: string;
  minimum?: number;
  maximum?: number;
  properties?: Record<string, JsonSchema>;
  required?: string[];
  items?: JsonSchema;
}

interface SchemaFormGeneratorProps {
  schema: JsonSchema;
  values: Record<string, unknown>;
  onChange: (values: Record<string, unknown>) => void;
  path?: string;
}

export const SchemaFormGenerator: React.FC<SchemaFormGeneratorProps> = ({
  schema,
  values,
  onChange,
  path = '',
}) => {
  const properties = schema?.properties ?? {};

  if (!schema || Object.keys(properties).length === 0) {
    return (
      <div className="flex items-center gap-2 p-3 bg-yellow-900/20 border border-yellow-700/50 rounded-lg text-yellow-300 text-sm">
        <AlertCircle className="w-4 h-4" />
        <span>No configuration schema available</span>
      </div>
    );
  }

  const handleFieldChange = (key: string, value: unknown) => {
    const newValues = { ...values };
    setNestedValue(newValues, key, value);
    onChange(newValues);
  };

  const required = Array.isArray(schema.required) ? schema.required : [];

  return (
    <div className="space-y-4">
      {Object.entries(properties as Record<string, JsonSchema>).map(([key, prop]) => (
        <FormField
          key={key}
          name={key}
          schema={prop}
          value={values[key]}
          onChange={(val) => handleFieldChange(key, val)}
          required={required.includes(key)}
          path={path ? `${path}.${key}` : key}
        />
      ))}
    </div>
  );
};

interface FormFieldProps {
  name: string;
  schema: JsonSchema;
  value: unknown;
  onChange: (value: unknown) => void;
  required?: boolean;
  path: string;
}

const FormField: React.FC<FormFieldProps> = ({
  name,
  schema,
  value,
  onChange,
  required = false,
  path,
}) => {
  const [isExpanded, setIsExpanded] = React.useState(true);

  // Get field label and description
  const label = typeof schema.title === 'string' ? schema.title : formatFieldName(name);
  const description = typeof schema.description === 'string' ? schema.description : undefined;
  const defaultValue = schema.default;

  // Determine field type
  const type = (typeof schema.type === 'string' ? schema.type : 'string') as JsonSchemaPrimitiveType | string;

  // Handle enum fields
  if (Array.isArray(schema.enum)) {
    const enumOptions = schema.enum;
    const SEARCHABLE_THRESHOLD = 10; // Use searchable select when > 10 options
    const enumOptionStrings = enumOptions.map((option) => String(option));
    
    // Use searchable select for large option lists
    if (enumOptions.length > SEARCHABLE_THRESHOLD) {
      return (
        <div>
          <label className="block text-sm font-medium text-slate-700 mb-2">
            {label}
            {required && <span className="text-red-400 ml-1">*</span>}
            <span className="ml-2 text-xs text-slate-500">
              ({enumOptions.length} options - searchable)
            </span>
          </label>
          <SearchableSelect
            value={toStringValue(value ?? defaultValue)}
            onChange={(val) => onChange(val)}
            options={enumOptionStrings}
            placeholder="Select or search..."
            required={required}
          />
          {description && (
            <p className="text-xs text-slate-500 mt-1">{description}</p>
          )}
        </div>
      );
    }
    
    // Use DropdownSelect for small option lists
    return (
      <div>
        <label className="block text-sm font-medium text-slate-700 mb-2">
          {label}
          {required && <span className="text-red-400 ml-1">*</span>}
        </label>
        <DropdownSelect
          value={toStringValue(value ?? defaultValue)}
          onChange={(val) => onChange(val)}
          options={[
            ...(required ? [] : [{ value: '', label: 'Select...' }]),
            ...enumOptionStrings.map((option) => ({
              value: option,
              label: option,
            })),
          ]}
          placeholder="Select..."
          required={required}
        />
        {description && (
          <p className="text-xs text-slate-500 mt-1">{description}</p>
        )}
      </div>
    );
  }

  switch (type) {
    case 'string':
      // Check if it's a multiline string
      const isMultiline =
        schema.format === 'text' ||
        (description && description.toLowerCase().includes('yaml'));
      
      return (
        <div>
          <label className="block text-sm font-medium text-slate-700 mb-2">
            {label}
            {required && <span className="text-red-400 ml-1">*</span>}
          </label>
          {isMultiline ? (
            <textarea
              value={toStringValue(value ?? defaultValue)}
              onChange={(e) => onChange(e.target.value)}
              placeholder={description || `Enter ${label.toLowerCase()}`}
              rows={4}
              className="w-full px-3 py-2 bg-white border border-slate-200 text-slate-900 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono text-sm"
              required={required}
            />
          ) : (
            <input
              type="text"
              value={toStringValue(value ?? defaultValue)}
              onChange={(e) => onChange(e.target.value)}
              placeholder={description || `Enter ${label.toLowerCase()}`}
              className="w-full px-3 py-2 bg-white border border-slate-200 text-slate-900 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
              required={required}
            />
          )}
          {description && (
            <p className="text-xs text-slate-500 mt-1">{description}</p>
          )}
          {defaultValue !== undefined && (
            <p className="text-xs text-slate-400 mt-1">Default: {String(defaultValue)}</p>
          )}
        </div>
      );

    case 'integer':
    case 'number':
      return (
        <div>
          <label className="block text-sm font-medium text-slate-700 mb-2">
            {label}
            {required && <span className="text-red-400 ml-1">*</span>}
          </label>
          <input
            type="number"
            value={toNumberInputValue(value, defaultValue)}
            onChange={(e) => {
              const numValue = e.target.value === '' ? undefined : Number(e.target.value);
              onChange(numValue);
            }}
            step={type === 'integer' ? '1' : 'any'}
            min={typeof schema.minimum === 'number' ? schema.minimum : undefined}
            max={typeof schema.maximum === 'number' ? schema.maximum : undefined}
            placeholder={description}
            className="w-full px-3 py-2 bg-white border border-slate-200 text-slate-900 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
            required={required}
          />
          {description && (
            <p className="text-xs text-slate-500 mt-1">{description}</p>
          )}
          {(schema.minimum !== undefined || schema.maximum !== undefined) && (
            <p className="text-xs text-slate-400 mt-1">
              Range: {schema.minimum !== undefined ? schema.minimum : '∞'} - {schema.maximum !== undefined ? schema.maximum : '∞'}
            </p>
          )}
        </div>
      );

    case 'boolean':
      return (
        <div className="flex items-start gap-3 p-3 bg-slate-50 rounded-lg">
          <input
            type="checkbox"
            checked={toBooleanValue(value, defaultValue)}
            onChange={(e) => onChange(e.target.checked)}
            className="mt-1 w-4 h-4 text-blue-600 bg-slate-100 border-slate-300 rounded focus:ring-blue-500"
          />
          <div className="flex-1">
            <label className="text-sm font-medium text-slate-700">
              {label}
              {required && <span className="text-red-400 ml-1">*</span>}
            </label>
            {description && (
              <p className="text-xs text-slate-500 mt-1">{description}</p>
            )}
          </div>
        </div>
      );

    case 'object': {
      const currentValue = isRecord(value) ? value : {};
      const objectSchema = isRecord(schema) ? schema : {};
      return (
        <div className="border border-slate-200 rounded-lg overflow-hidden">
          <button
            type="button"
            onClick={() => setIsExpanded(!isExpanded)}
            className="w-full flex items-center justify-between p-3 bg-slate-50 hover:bg-white transition"
          >
            <div className="flex items-center gap-2">
              {isExpanded ? (
                <ChevronDown className="w-4 h-4 text-slate-500" />
              ) : (
                <ChevronRight className="w-4 h-4 text-slate-500" />
              )}
              <span className="font-medium text-slate-700">
                {label}
                {required && <span className="text-red-400 ml-1">*</span>}
              </span>
            </div>
            <span className="text-xs text-slate-500">Object</span>
          </button>
          
          {isExpanded && (
            <div className="p-4 space-y-4 bg-slate-50">
              {description && (
                <p className="text-xs text-slate-500 -mt-2 mb-2">{description}</p>
              )}
              {schema.properties ? (
                <SchemaFormGenerator
                  schema={objectSchema as JsonSchema}
                  values={currentValue}
                  onChange={(updated) => onChange(updated)}
                  path={path}
                />
              ) : (
                <p className="text-xs text-slate-500 italic">
                  No properties defined for this object
                </p>
              )}
            </div>
          )}
        </div>
      );
    }

    case 'array': {
      const arrayText = Array.isArray(value)
        ? value.map((item) => String(item)).join('\n')
        : toStringValue(value);
      return (
        <div>
          <label className="block text-sm font-medium text-slate-700 mb-2">
            {label}
            {required && <span className="text-red-400 ml-1">*</span>}
            <span className="ml-2 text-xs text-slate-500">(Array)</span>
          </label>
          <textarea
            value={arrayText}
            onChange={(e) => {
              const lines = e.target.value.split('\n').filter(line => line.trim());
              onChange(lines);
            }}
            placeholder="Enter one item per line"
            rows={4}
            className="w-full px-3 py-2 bg-white border border-slate-200 text-slate-900 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono text-sm"
            required={required}
          />
          {description && (
            <p className="text-xs text-slate-500 mt-1">{description}</p>
          )}
          <p className="text-xs text-slate-400 mt-1">Enter one item per line</p>
        </div>
      );
    }

    default:
      // Fallback for unknown types
      return (
        <div>
          <label className="block text-sm font-medium text-slate-700 mb-2">
            {label}
            {required && <span className="text-red-400 ml-1">*</span>}
            <span className="ml-2 text-xs text-slate-500">({type})</span>
          </label>
          <input
            type="text"
            value={toStringValue(value ?? defaultValue)}
            onChange={(e) => onChange(e.target.value)}
            placeholder={description}
            className="w-full px-3 py-2 bg-white border border-slate-200 text-slate-900 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
            required={required}
          />
          {description && (
            <p className="text-xs text-slate-500 mt-1">{description}</p>
          )}
        </div>
      );
  }
};

// Helper function to set nested value in object
function setNestedValue(obj: Record<string, unknown>, path: string, value: unknown) {
  const keys = path.split('.');
  let current: Record<string, unknown> = obj;
  
  for (let i = 0; i < keys.length - 1; i++) {
    const key = keys[i];
    if (!isRecord(current[key])) {
      current[key] = {};
    }
    current = current[key] as Record<string, unknown>;
  }
  
  current[keys[keys.length - 1]] = value;
}

// Helper function to format field name (camelCase -> Title Case)
function formatFieldName(name: string): string {
  return name
    .replace(/([A-Z])/g, ' $1')
    .replace(/^./, (str) => str.toUpperCase())
    .trim();
}

const isRecord = (value: unknown): value is Record<string, unknown> =>
  typeof value === 'object' && value !== null && !Array.isArray(value);

const toStringValue = (value: unknown, fallback = ''): string => {
  if (value === undefined || value === null) {
    return fallback;
  }
  return typeof value === 'string' ? value : String(value);
};

const toNumberInputValue = (value: unknown, fallback: unknown): number | '' => {
  if (typeof value === 'number') {
    return value;
  }
  if (value === '' || value === null || value === undefined) {
    if (typeof fallback === 'number') {
      return fallback;
    }
    return '';
  }
  if (typeof value === 'string' && value !== '') {
    const parsed = Number(value);
    return Number.isNaN(parsed) ? '' : parsed;
  }
  if (typeof fallback === 'number') {
    return fallback;
  }
  return '';
};

const toBooleanValue = (value: unknown, fallback: unknown): boolean => {
  if (typeof value === 'boolean') {
    return value;
  }
  if (typeof fallback === 'boolean') {
    return fallback;
  }
  return false;
};
