/**
 * HandParamsForm - Dynamic form component for Hand parameters
 *
 * Supports all parameter types:
 * - text: Text input
 * - number: Number input with min/max validation
 * - boolean: Toggle/checkbox
 * - select: Dropdown select
 * - textarea: Multi-line text
 * - array: Dynamic array with add/remove items
 * - object: JSON object editor
 * - file: File selector
 *
 * Features:
 * - Form validation (required, type, range, pattern)
 * - Parameter presets (save/load/delete)
 * - Error display below inputs
 */

import { useState, useCallback, useEffect, useMemo } from 'react';
import {
  AlertCircle,
  Plus,
  Trash2,
  Save,
  FolderOpen,
  Trash,
  ChevronDown,
  ChevronUp,
  FileText,
  Info,
} from 'lucide-react';
import type { HandParameter } from '../types/hands';

// === Types ===

export interface HandParamsFormProps {
  parameters: HandParameter[];
  values: Record<string, unknown>;
  onChange: (values: Record<string, unknown>) => void;
  errors?: Record<string, string>;
  disabled?: boolean;
  presetKey?: string; // Key for storing/loading presets
}

export interface ParameterPreset {
  id: string;
  name: string;
  createdAt: string;
  values: Record<string, unknown>;
}

// === Validation ===

interface ValidationResult {
  isValid: boolean;
  error?: string;
}

function validateParameter(param: HandParameter, value: unknown): ValidationResult {
  // Required check
  if (param.required) {
    if (value === undefined || value === null || value === '') {
      return { isValid: false, error: `${param.label} is required` };
    }
    if (Array.isArray(value) && value.length === 0) {
      return { isValid: false, error: `${param.label} is required` };
    }
    if (typeof value === 'object' && !Array.isArray(value) && Object.keys(value as Record<string, unknown>).length === 0) {
      return { isValid: false, error: `${param.label} is required` };
    }
  }

  // Skip further validation if value is empty and not required
  if (value === undefined || value === null || value === '') {
    return { isValid: true };
  }

  // Type-specific validation
  switch (param.type) {
    case 'number':
      if (typeof value !== 'number' || isNaN(value)) {
        return { isValid: false, error: `${param.label} must be a valid number` };
      }
      if (param.min !== undefined && value < param.min) {
        return { isValid: false, error: `${param.label} must be at least ${param.min}` };
      }
      if (param.max !== undefined && value > param.max) {
        return { isValid: false, error: `${param.label} must be at most ${param.max}` };
      }
      break;

    case 'text':
    case 'textarea':
      if (typeof value !== 'string') {
        return { isValid: false, error: `${param.label} must be text` };
      }
      if (param.pattern) {
        try {
          const regex = new RegExp(param.pattern);
          if (!regex.test(value)) {
            return { isValid: false, error: `${param.label} format is invalid` };
          }
        } catch {
          // Invalid regex pattern, skip validation
        }
      }
      break;

    case 'array':
      if (!Array.isArray(value)) {
        return { isValid: false, error: `${param.label} must be an array` };
      }
      break;

    case 'object':
      if (typeof value !== 'object' || Array.isArray(value)) {
        return { isValid: false, error: `${param.label} must be an object` };
      }
      try {
        // Try to stringify to validate JSON
        JSON.stringify(value);
      } catch {
        return { isValid: false, error: `${param.label} contains invalid JSON` };
      }
      break;

    case 'file':
      if (typeof value !== 'string') {
        return { isValid: false, error: `${param.label} must be a file path` };
      }
      break;
  }

  return { isValid: true };
}

// === Preset Storage ===

const PRESET_STORAGE_PREFIX = 'zclaw-hand-preset-';

function getPresetStorageKey(handId: string): string {
  return `${PRESET_STORAGE_PREFIX}${handId}`;
}

function loadPresets(handId: string): ParameterPreset[] {
  try {
    const stored = localStorage.getItem(getPresetStorageKey(handId));
    if (stored) {
      return JSON.parse(stored) as ParameterPreset[];
    }
  } catch {
    // Ignore parse errors
  }
  return [];
}

function savePresets(handId: string, presets: ParameterPreset[]): void {
  try {
    localStorage.setItem(getPresetStorageKey(handId), JSON.stringify(presets));
  } catch {
    // Ignore storage errors
  }
}

// === Sub-Components ===

interface FormFieldWrapperProps {
  param: HandParameter;
  error?: string;
  children: React.ReactNode;
}

function FormFieldWrapper({ param, error, children }: FormFieldWrapperProps) {
  return (
    <div className="space-y-1.5">
      <label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
        {param.label}
        {param.required && <span className="text-red-500 ml-1">*</span>}
      </label>
      {children}
      {param.description && !error && (
        <p className="text-xs text-gray-500 dark:text-gray-400 flex items-center gap-1">
          <Info className="w-3 h-3" />
          {param.description}
        </p>
      )}
      {error && (
        <p className="text-xs text-red-500 flex items-center gap-1">
          <AlertCircle className="w-3 h-3" />
          {error}
        </p>
      )}
    </div>
  );
}

// === Parameter Input Components ===

interface ParamInputProps {
  param: HandParameter;
  value: unknown;
  onChange: (value: unknown) => void;
  disabled?: boolean;
  error?: string;
}

function TextParamInput({ param, value, onChange, disabled, error }: ParamInputProps) {
  return (
    <input
      type="text"
      value={(value as string) ?? ''}
      onChange={(e) => onChange(e.target.value)}
      placeholder={param.placeholder}
      disabled={disabled}
      className={`w-full px-3 py-2 text-sm border rounded-lg bg-white dark:bg-gray-900 text-gray-900 dark:text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed ${
        error ? 'border-red-500' : 'border-gray-300 dark:border-gray-600'
      }`}
    />
  );
}

function NumberParamInput({ param, value, onChange, disabled, error }: ParamInputProps) {
  return (
    <input
      type="number"
      value={(value as number) ?? ''}
      onChange={(e) => {
        const val = e.target.value;
        onChange(val === '' ? undefined : parseFloat(val));
      }}
      placeholder={param.placeholder}
      min={param.min}
      max={param.max}
      disabled={disabled}
      className={`w-full px-3 py-2 text-sm border rounded-lg bg-white dark:bg-gray-900 text-gray-900 dark:text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed ${
        error ? 'border-red-500' : 'border-gray-300 dark:border-gray-600'
      }`}
    />
  );
}

function BooleanParamInput({ param, value, onChange, disabled }: ParamInputProps) {
  return (
    <label className="flex items-center gap-2 cursor-pointer">
      <input
        type="checkbox"
        checked={(value as boolean) ?? false}
        onChange={(e) => onChange(e.target.checked)}
        disabled={disabled}
        className="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
      />
      <span className="text-sm text-gray-600 dark:text-gray-400">
        {param.placeholder || 'Enabled'}
      </span>
    </label>
  );
}

function SelectParamInput({ param, value, onChange, disabled, error }: ParamInputProps) {
  return (
    <select
      value={(value as string) ?? ''}
      onChange={(e) => onChange(e.target.value)}
      disabled={disabled}
      className={`w-full px-3 py-2 text-sm border rounded-lg bg-white dark:bg-gray-900 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed ${
        error ? 'border-red-500' : 'border-gray-300 dark:border-gray-600'
      }`}
    >
      <option value="">{param.placeholder || '-- Select --'}</option>
      {param.options?.map((opt) => (
        <option key={opt.value} value={opt.value}>
          {opt.label}
        </option>
      ))}
    </select>
  );
}

function TextareaParamInput({ param, value, onChange, disabled, error }: ParamInputProps) {
  return (
    <textarea
      value={(value as string) ?? ''}
      onChange={(e) => onChange(e.target.value)}
      placeholder={param.placeholder}
      disabled={disabled}
      rows={3}
      className={`w-full px-3 py-2 text-sm border rounded-lg bg-white dark:bg-gray-900 text-gray-900 dark:text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed resize-y ${
        error ? 'border-red-500' : 'border-gray-300 dark:border-gray-600'
      }`}
    />
  );
}

function ArrayParamInput({ param, value, onChange, disabled, error }: ParamInputProps) {
  const [newItem, setNewItem] = useState('');
  const items = (Array.isArray(value) ? value : []) as string[];

  const handleAddItem = () => {
    if (newItem.trim()) {
      onChange([...items, newItem.trim()]);
      setNewItem('');
    }
  };

  const handleRemoveItem = (index: number) => {
    const newItems = [...items];
    newItems.splice(index, 1);
    onChange(newItems);
  };

  const handleUpdateItem = (index: number, newValue: string) => {
    const newItems = [...items];
    newItems[index] = newValue;
    onChange(newItems);
  };

  const handleKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === 'Enter') {
      e.preventDefault();
      handleAddItem();
    }
  };

  return (
    <div className={`space-y-2 p-3 border rounded-lg ${error ? 'border-red-500' : 'border-gray-300 dark:border-gray-600'}`}>
      {/* Existing Items */}
      {items.length > 0 && (
        <div className="space-y-1.5">
          {items.map((item, index) => (
            <div key={index} className="flex items-center gap-2">
              <input
                type="text"
                value={item}
                onChange={(e) => handleUpdateItem(index, e.target.value)}
                disabled={disabled}
                className="flex-1 px-2 py-1 text-sm border border-gray-200 dark:border-gray-700 rounded bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:outline-none focus:ring-1 focus:ring-blue-500 disabled:opacity-50"
              />
              <button
                type="button"
                onClick={() => handleRemoveItem(index)}
                disabled={disabled}
                className="p-1 text-red-500 hover:text-red-700 hover:bg-red-50 dark:hover:bg-red-900/20 rounded disabled:opacity-50 disabled:cursor-not-allowed"
              >
                <Trash2 className="w-4 h-4" />
              </button>
            </div>
          ))}
        </div>
      )}

      {/* Add New Item */}
      <div className="flex items-center gap-2">
        <input
          type="text"
          value={newItem}
          onChange={(e) => setNewItem(e.target.value)}
          onKeyDown={handleKeyDown}
          placeholder={param.placeholder || 'Add item...'}
          disabled={disabled}
          className="flex-1 px-2 py-1 text-sm border border-gray-200 dark:border-gray-700 rounded bg-white dark:bg-gray-800 text-gray-900 dark:text-white placeholder-gray-400 focus:outline-none focus:ring-1 focus:ring-blue-500 disabled:opacity-50"
        />
        <button
          type="button"
          onClick={handleAddItem}
          disabled={disabled || !newItem.trim()}
          className="p-1 text-blue-500 hover:text-blue-700 hover:bg-blue-50 dark:hover:bg-blue-900/20 rounded disabled:opacity-50 disabled:cursor-not-allowed"
        >
          <Plus className="w-4 h-4" />
        </button>
      </div>

      {items.length === 0 && !newItem && (
        <p className="text-xs text-gray-400 text-center">No items added yet</p>
      )}
    </div>
  );
}

function ObjectParamInput({ param, value, onChange, disabled, error }: ParamInputProps) {
  const [jsonText, setJsonText] = useState('');
  const [parseError, setParseError] = useState<string | null>(null);
  const [isExpanded, setIsExpanded] = useState(true);

  // Sync jsonText with value
  useEffect(() => {
    try {
      if (value && typeof value === 'object' && !Array.isArray(value)) {
        setJsonText(JSON.stringify(value, null, 2));
        setParseError(null);
      } else {
        setJsonText('');
      }
    } catch {
      setJsonText('');
    }
  }, [value]);

  const handleTextChange = (text: string) => {
    setJsonText(text);

    if (!text.trim()) {
      onChange({});
      setParseError(null);
      return;
    }

    try {
      const parsed = JSON.parse(text);
      if (typeof parsed === 'object' && !Array.isArray(parsed)) {
        onChange(parsed);
        setParseError(null);
      } else {
        setParseError('Value must be a JSON object');
      }
    } catch {
      setParseError('Invalid JSON format');
    }
  };

  return (
    <div className={`space-y-2 ${error || parseError ? 'border-red-500' : ''}`}>
      <button
        type="button"
        onClick={() => setIsExpanded(!isExpanded)}
        className="flex items-center gap-2 text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white"
      >
        {isExpanded ? <ChevronUp className="w-4 h-4" /> : <ChevronDown className="w-4 h-4" />}
        {isExpanded ? 'Collapse' : 'Expand'} JSON Editor
      </button>

      {isExpanded && (
        <textarea
          value={jsonText}
          onChange={(e) => handleTextChange(e.target.value)}
          placeholder={param.placeholder || '{\n  "key": "value"\n}'}
          disabled={disabled}
          rows={6}
          className={`w-full px-3 py-2 text-sm border rounded-lg bg-white dark:bg-gray-900 text-gray-900 dark:text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono disabled:opacity-50 disabled:cursor-not-allowed resize-y ${
            error || parseError ? 'border-red-500' : 'border-gray-300 dark:border-gray-600'
          }`}
        />
      )}

      {parseError && (
        <p className="text-xs text-red-500 flex items-center gap-1">
          <AlertCircle className="w-3 h-3" />
          {parseError}
        </p>
      )}
    </div>
  );
}

function FileParamInput({ param, value, onChange, disabled, error }: ParamInputProps) {
  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (file) {
      // For now, just store the file name. In a real implementation,
      // you might want to read the file contents or handle upload
      onChange(file.name);
    }
  };

  return (
    <div className={`space-y-2 ${error ? 'border-red-500' : ''}`}>
      <div className="flex items-center gap-2">
        <label
          className={`flex-1 flex items-center gap-2 px-3 py-2 text-sm border rounded-lg cursor-pointer transition-colors ${
            disabled
              ? 'bg-gray-100 dark:bg-gray-800 cursor-not-allowed opacity-50'
              : 'bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-gray-800'
          } ${error ? 'border-red-500' : 'border-gray-300 dark:border-gray-600'}`}
        >
          <FileText className="w-4 h-4 text-gray-400" />
          <span className="flex-1 truncate text-gray-900 dark:text-white">
            {(value as string) || param.placeholder || 'Choose file...'}
          </span>
          <input
            type="file"
            accept={param.accept}
            onChange={handleFileChange}
            disabled={disabled}
            className="hidden"
          />
        </label>
        {(value as string) && (
          <button
            type="button"
            onClick={() => onChange('')}
            disabled={disabled}
            className="p-2 text-red-500 hover:text-red-700 hover:bg-red-50 dark:hover:bg-red-900/20 rounded disabled:opacity-50 disabled:cursor-not-allowed"
          >
            <Trash2 className="w-4 h-4" />
          </button>
        )}
      </div>
    </div>
  );
}

// === Parameter Field Component ===

function ParameterField({ param, value, onChange, disabled, externalError }: ParamInputProps & { externalError?: string }) {
  const [internalError, setInternalError] = useState<string | undefined>(undefined);

  const handleChange = useCallback((newValue: unknown) => {
    // Validate on change
    const result = validateParameter(param, newValue);
    setInternalError(result.error ?? undefined);
    onChange(newValue);
  }, [param, onChange]);

  const error = externalError || internalError;

  const inputProps: ParamInputProps = { param, value, onChange: handleChange, disabled, error };

  const renderInput = () => {
    switch (param.type) {
      case 'text':
        return <TextParamInput {...inputProps} />;
      case 'number':
        return <NumberParamInput {...inputProps} />;
      case 'boolean':
        return <BooleanParamInput {...inputProps} />;
      case 'select':
        return <SelectParamInput {...inputProps} />;
      case 'textarea':
        return <TextareaParamInput {...inputProps} />;
      case 'array':
        return <ArrayParamInput {...inputProps} />;
      case 'object':
        return <ObjectParamInput {...inputProps} />;
      case 'file':
        return <FileParamInput {...inputProps} />;
      default:
        return <TextParamInput {...inputProps} />;
    }
  };

  return (
    <FormFieldWrapper param={param} error={error}>
      {renderInput()}
    </FormFieldWrapper>
  );
}

// === Preset Manager Component ===

interface PresetManagerProps {
  presetKey?: string;
  currentValues: Record<string, unknown>;
  onLoadPreset: (values: Record<string, unknown>) => void;
}

function PresetManager({ presetKey, currentValues, onLoadPreset }: PresetManagerProps) {
  const [presets, setPresets] = useState<ParameterPreset[]>([]);
  const [showSaveDialog, setShowSaveDialog] = useState(false);
  const [presetName, setPresetName] = useState('');
  const [showPresetList, setShowPresetList] = useState(false);

  // Load presets on mount
  useEffect(() => {
    if (presetKey) {
      setPresets(loadPresets(presetKey));
    }
  }, [presetKey]);

  const handleSavePreset = () => {
    if (!presetKey || !presetName.trim()) return;

    const newPreset: ParameterPreset = {
      id: `preset-${Date.now()}`,
      name: presetName.trim(),
      createdAt: new Date().toISOString(),
      values: { ...currentValues },
    };

    const newPresets = [...presets, newPreset];
    setPresets(newPresets);
    savePresets(presetKey, newPresets);
    setPresetName('');
    setShowSaveDialog(false);
  };

  const handleLoadPreset = (preset: ParameterPreset) => {
    onLoadPreset(preset.values);
    setShowPresetList(false);
  };

  const handleDeletePreset = (presetId: string) => {
    if (!presetKey) return;
    const newPresets = presets.filter((p) => p.id !== presetId);
    setPresets(newPresets);
    savePresets(presetKey, newPresets);
  };

  if (!presetKey) return null;

  return (
    <div className="border-b border-gray-200 dark:border-gray-700 pb-4 mb-4">
      <div className="flex items-center gap-2">
        <button
          type="button"
          onClick={() => setShowSaveDialog(true)}
          className="flex items-center gap-1.5 px-3 py-1.5 text-sm border border-gray-200 dark:border-gray-600 rounded-md hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300"
        >
          <Save className="w-3.5 h-3.5" />
          Save Preset
        </button>
        <button
          type="button"
          onClick={() => setShowPresetList(!showPresetList)}
          disabled={presets.length === 0}
          className="flex items-center gap-1.5 px-3 py-1.5 text-sm border border-gray-200 dark:border-gray-600 rounded-md hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300 disabled:opacity-50 disabled:cursor-not-allowed"
        >
          <FolderOpen className="w-3.5 h-3.5" />
          Load Preset ({presets.length})
        </button>
      </div>

      {/* Save Dialog */}
      {showSaveDialog && (
        <div className="mt-3 p-3 bg-gray-50 dark:bg-gray-900 rounded-lg space-y-2">
          <label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
            Preset Name
          </label>
          <div className="flex items-center gap-2">
            <input
              type="text"
              value={presetName}
              onChange={(e) => setPresetName(e.target.value)}
              placeholder="My preset..."
              className="flex-1 px-3 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500"
              onKeyDown={(e) => {
                if (e.key === 'Enter') handleSavePreset();
                if (e.key === 'Escape') setShowSaveDialog(false);
              }}
              autoFocus
            />
            <button
              type="button"
              onClick={handleSavePreset}
              disabled={!presetName.trim()}
              className="px-3 py-1.5 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed"
            >
              Save
            </button>
            <button
              type="button"
              onClick={() => setShowSaveDialog(false)}
              className="px-3 py-1.5 text-sm border border-gray-200 dark:border-gray-600 rounded-md hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300"
            >
              Cancel
            </button>
          </div>
        </div>
      )}

      {/* Preset List */}
      {showPresetList && presets.length > 0 && (
        <div className="mt-3 p-3 bg-gray-50 dark:bg-gray-900 rounded-lg space-y-2">
          <label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
            Available Presets
          </label>
          <div className="space-y-1.5 max-h-48 overflow-y-auto">
            {presets.map((preset) => (
              <div
                key={preset.id}
                className="flex items-center justify-between p-2 bg-white dark:bg-gray-800 rounded border border-gray-200 dark:border-gray-700"
              >
                <div className="flex-1 min-w-0">
                  <p className="text-sm font-medium text-gray-900 dark:text-white truncate">
                    {preset.name}
                  </p>
                  <p className="text-xs text-gray-500 dark:text-gray-400">
                    {new Date(preset.createdAt).toLocaleDateString()}
                  </p>
                </div>
                <div className="flex items-center gap-1 ml-2">
                  <button
                    type="button"
                    onClick={() => handleLoadPreset(preset)}
                    className="px-2 py-1 text-xs text-blue-600 dark:text-blue-400 hover:bg-blue-50 dark:hover:bg-blue-900/20 rounded"
                  >
                    Load
                  </button>
                  <button
                    type="button"
                    onClick={() => handleDeletePreset(preset.id)}
                    className="p-1 text-red-500 hover:text-red-700 hover:bg-red-50 dark:hover:bg-red-900/20 rounded"
                  >
                    <Trash className="w-3.5 h-3.5" />
                  </button>
                </div>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

// === Main Component ===

export function HandParamsForm({
  parameters,
  values,
  onChange,
  errors,
  disabled,
  presetKey,
}: HandParamsFormProps) {
  // Initialize values with defaults
  const initialValues = useMemo(() => {
    const result: Record<string, unknown> = { ...values };
    parameters.forEach((param) => {
      if (result[param.name] === undefined && param.defaultValue !== undefined) {
        result[param.name] = param.defaultValue;
      }
    });
    return result;
  }, [parameters, values]);

  // Update parent when initialValues changes
  useEffect(() => {
    const hasMissingDefaults = parameters.some(
      (p) => values[p.name] === undefined && p.defaultValue !== undefined
    );
    if (hasMissingDefaults) {
      onChange(initialValues);
    }
  }, [initialValues, parameters, values, onChange]);

  const handleFieldChange = useCallback(
    (paramName: string, value: unknown) => {
      onChange({
        ...values,
        [paramName]: value,
      });
    },
    [values, onChange]
  );

  const handleLoadPreset = useCallback(
    (presetValues: Record<string, unknown>) => {
      onChange({
        ...values,
        ...presetValues,
      });
    },
    [values, onChange]
  );

  if (parameters.length === 0) {
    return (
      <div className="text-center py-4 text-gray-500 dark:text-gray-400 text-sm">
        No parameters required for this Hand.
      </div>
    );
  }

  return (
    <div className="space-y-4">
      {/* Preset Manager */}
      <PresetManager
        presetKey={presetKey}
        currentValues={values}
        onLoadPreset={handleLoadPreset}
      />

      {/* Parameter Fields - Grid Layout */}
      <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
        {parameters.map((param) => (
          <div
            key={param.name}
            className={param.type === 'textarea' || param.type === 'object' || param.type === 'array' ? 'md:col-span-2' : ''}
          >
            <ParameterField
              param={param}
              value={values[param.name]}
              onChange={(value) => handleFieldChange(param.name, value)}
              disabled={disabled}
              externalError={errors?.[param.name]}
            />
          </div>
        ))}
      </div>
    </div>
  );
}

// === Validation Export ===

export function validateAllParameters(
  parameters: HandParameter[],
  values: Record<string, unknown>
): Record<string, string> {
  const errors: Record<string, string> = {};

  parameters.forEach((param) => {
    const result = validateParameter(param, values[param.name]);
    if (!result.isValid && result.error) {
      errors[param.name] = result.error;
    }
  });

  return errors;
}

export default HandParamsForm;
