import type { FormDef, FormError, Result } from './types.js'

const OPTION_TYPES = new Set(['select', 'multiselect', 'radio'])

/**
 * Structural validation of a form DEFINITION (not a submission). Pure.
 * Catches dup field names, option-types without options, bad pattern source, empty id/fields.
 */
export function defineForm(def: FormDef): Result<FormDef, FormError[]> {
  const errors: FormError[] = []

  if (!def.id || def.id.trim() === '') {
    errors.push({ code: 'empty-id', message: 'form id must be a non-empty string' })
  }
  if (!Array.isArray(def.fields) || def.fields.length === 0) {
    errors.push({ code: 'no-fields', message: 'form must declare at least one field' })
  }

  const seen = new Set<string>()
  for (const f of def.fields ?? []) {
    if (!f.name || f.name.trim() === '') {
      errors.push({ code: 'missing-name', message: 'field name must be a non-empty string' })
    } else if (seen.has(f.name)) {
      errors.push({ code: 'duplicate-field', field: f.name, message: `duplicate field name: ${f.name}` })
    } else {
      seen.add(f.name)
    }

    if (!f.label || f.label.trim() === '') {
      errors.push({ code: 'missing-label', field: f.name, message: `field ${f.name} must have a label` })
    }

    if (OPTION_TYPES.has(f.type) && (!f.options || f.options.length === 0)) {
      errors.push({ code: 'no-options', field: f.name, message: `field ${f.name} (${f.type}) requires options` })
    }

    if (f.pattern !== undefined) {
      try {
        new RegExp(f.pattern)
      } catch {
        errors.push({ code: 'bad-pattern', field: f.name, message: `field ${f.name} has an invalid pattern source` })
      }
    }
  }

  if (errors.length > 0) return { ok: false, error: errors }
  return { ok: true, value: def }
}
