import React, { useCallback, useMemo, useState } from 'react'
import { View } from '../view'
import { Alert } from '../alert'
import { applyFieldChange } from '../fields/applyFieldChange'
import { FormContext } from './FormContext.jsx'

function fieldNameFromTarget(target) {
  if (!target) return null
  if (typeof target.name === 'string' && target.name.length > 0) return target.name
  if (typeof target.id === 'string' && target.id.length > 0 && !target.id.includes('.')) {
    return target.id
  }
  return null
}

/**
 * @typedef {object} FormChangeDetail
 * @property {import('react').ChangeEvent} event Native change event (or synthetic field commit).
 * @property {{ name: string, type?: string } | undefined} field Matching field def from `fields`.
 * @property {unknown} value New value for that field after the change.
 * @property {Record<string, unknown>} data Full form data after the change.
 */

/**
 * @typedef {object} FormContextValue
 * @property {string} id Stable form id for flow-runner (`form[id="…"]`).
 * @property {string} [schemaId] Resource schema id for field copy keys (`{schemaId}.{fieldName}.label`).
 * @property {{ name: string, type?: string, label?: string, required?: boolean, options?: string[], accept?: string, min?: number, max?: number }[]} fields
 * @property {Record<string, unknown>} data Current field values.
 * @property {(detail: FormChangeDetail) => void} onChange
 * @property {readonly string[]} status
 */

/**
 * Form provider + semantic `<form>`. Supports controlled (`data` + `onChange`) and
 * uncontrolled (`defaultData`) modes. Uncontrolled updates use {@link applyFieldChange}.
 *
 * Pair with {@link FieldFactory} for template-driven fields. Place action buttons
 * outside {@link FieldFactory} so they are not disabled by its fieldset.
 *
 * Submit handlers run only after native validation passes (`checkValidity()` on the form).
 * When `validate` is set, it runs after native checks and can block submit.
 *
 * @param {{
 *   id: string,
 *   schemaId?: string,
 *   fields: { name: string, type?: string, label?: string, required?: boolean, options?: string[], accept?: string, min?: number, max?: number }[],
 *   data?: Record<string, unknown>,
 *   defaultData?: Record<string, unknown>,
 *   onChange?: (detail: FormChangeDetail) => void,
 *   onSubmit?: (data: Record<string, unknown>, event: import('react').FormEvent<HTMLFormElement>) => void | Promise<void>,
 *   validate?: (data: Record<string, unknown>) => { ok: true, data?: Record<string, unknown> } | { ok: false, message?: string },
 *   status?: readonly string[],
 *   children?: import('react').ReactNode,
 * }} props
 */
export function Form ({
  id,
  schemaId,
  fields,
  data: dataProp,
  defaultData,
  onChange,
  onSubmit,
  validate,
  status = [],
  children,
  ...props
}) {
  const [internalData, setInternalData] = useState(defaultData ?? {})
  const [validationError, setValidationError] = useState()
  const isControlled = dataProp !== undefined
  const data = isControlled ? dataProp : internalData

  const handleChange = useCallback((event) => {
    setValidationError(undefined)
    const nextData = applyFieldChange(data, event)
    if (!isControlled) {
      setInternalData(nextData)
    }
    const fieldName = fieldNameFromTarget(event?.target)
    const field = fieldName ? fields.find(f => f.name === fieldName) : undefined
    onChange?.({
      event,
      field,
      value: fieldName ? nextData[fieldName] : undefined,
      data: nextData,
    })
  }, [data, fields, isControlled, onChange])

  const contextValue = useMemo(() => ({
    id,
    schemaId,
    fields,
    data,
    onChange: handleChange,
    status,
  }), [id, schemaId, fields, data, handleChange, status])

  return (
    <FormContext.Provider value={contextValue}>
      <View
        as="form"
        id={id}
        onSubmit={e => {
          e.preventDefault()
          if (!onSubmit) return
          if (!e.currentTarget.checkValidity()) return
          let submitData = data
          if (validate) {
            const result = validate(data)
            if (!result.ok) {
              setValidationError(result.message ?? 'Validation failed')
              return
            }
            submitData = result.data ?? data
          }
          setValidationError(undefined)
          onSubmit(submitData, e)
        }}
        {...props}
      >
        {validationError ? (
          <Alert variant="danger" style={{ marginBottom: 'var(--space-m)' }}>
            {validationError}
          </Alert>
        ) : null}
        {children}
      </View>
    </FormContext.Provider>
  )
}
