import type { ComponentChildren, JSX } from 'preact';
import { cloneElement, createContext } from 'preact';
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'preact/hooks';
import { useT } from '../../hooks/useT';
import { AsukaIcon } from './AsukaIcon';
import { cn } from './cn';
import { Field } from './Field';

type AnyObject = Record<string, any>;
type Path = string;

function splitPath(path: Path): string[] {
  if (!path) return [];
  return path.split('.').flatMap((p) => (p.includes('[') ? p.replace(/\]/g, '').split('[') : [p]));
}

function getIn(obj: any, path: Path): any {
  const parts = splitPath(path);
  let cur = obj;
  for (const key of parts) {
    if (cur == null) return undefined;
    cur = cur[key as any];
  }
  return cur;
}

function setIn(obj: any, path: Path, value: any): any {
  const parts = splitPath(path);
  const root = Array.isArray(obj) ? [...obj] : { ...(obj ?? {}) };
  let cur: any = root;
  for (let i = 0; i < parts.length; i++) {
    const k = parts[i];
    const last = i === parts.length - 1;
    const nextKey = parts[i + 1];
    const nextIsIndex = nextKey !== undefined && /^\d+$/.test(nextKey);
    if (last) {
      cur[k] = value;
    } else {
      const existing = cur[k];
      const next = existing != null ? (Array.isArray(existing) ? [...existing] : { ...existing }) : nextIsIndex ? [] : {};
      cur[k] = next;
      cur = next;
    }
  }
  return root;
}

function joinPath(parent: string, child: string) {
  if (!parent) return child;
  if (!child) return parent;
  // Support absolute paths if child starts with '.' or '[' (rare, but useful)
  if (child.startsWith('[')) return parent + child;
  return parent + '.' + child;
}

function normalizeValidationErrors(errors: FormValidationErrors | null | undefined): Record<string, string> {
  const out: Record<string, string> = {};
  if (!errors || typeof errors !== 'object') return out;
  for (const [path, message] of Object.entries(errors)) {
    const normalized = typeof message === 'string' ? message.trim() : '';
    if (path && normalized) out[path] = normalized;
  }
  return out;
}

// ---------------------------------------------------------------------------
// Contexts
// ---------------------------------------------------------------------------

export type FormState<TValues extends AnyObject> = {
  values: TValues;
  errors: Record<string, string>;
  touched: Record<string, boolean>;
  setValue: (path: string, value: any) => void;
  setTouched: (path: string, touched: boolean) => void;
  validate: () => Promise<boolean>;
  /** Internal registry for per-field validators/schemas */
  __registerField: (path: string, cfg: FieldValidationConfig) => () => void;
};

type FieldValidationConfig = {
  validate?: (value: any, values: AnyObject) => string | null | undefined;
};

const FormContext = createContext<FormState<any> | null>(null);
const FormPathContext = createContext<string>('');

export function useForm<TValues extends AnyObject>() {
  const ctx = useContext(FormContext);
  if (!ctx) throw new Error('useForm must be used within <Form>');
  return ctx as FormState<TValues>;
}

export function useFormField<T = any>(name: string) {
  const form = useForm<any>();
  const base = useContext(FormPathContext);
  const fullName = joinPath(base, name);
  return {
    name: fullName,
    value: getIn(form.values, fullName) as T,
    error: form.errors[fullName],
    touched: !!form.touched[fullName],
    setValue: (v: any) => form.setValue(fullName, v),
    setTouched: (t: boolean) => form.setTouched(fullName, t),
  };
}

// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------


export type FormRequiredOption = boolean | string;

function isRequiredValueMissing(value: any): boolean {
  if (value == null) return true;
  if (typeof value === 'string') return value.trim().length === 0;
  if (typeof value === 'boolean') return value === false;
  if (Array.isArray(value)) return value.length === 0;
  return false;
}

function resolveRequiredMessage(required: FormRequiredOption | undefined, requiredMessage: string | undefined, t: ReturnType<typeof useT>): string {
  if (typeof required === 'string' && required.trim()) return required;
  if (typeof requiredMessage === 'string' && requiredMessage.trim()) return requiredMessage;
  return t('form.validation.required', 'This field is required');
}

export type FormActions<TValues extends AnyObject> = {
  resetForm: (nextInitialValues?: TValues) => void;
};

export type FormValidationErrors = Record<string, string | null | undefined>;
export type FormValuesValidator<TValues extends AnyObject> = (
  values: TValues,
) => FormValidationErrors | null | undefined | Promise<FormValidationErrors | null | undefined>;

export type FormProps<TValues extends AnyObject> = {
  /** Optional form-level validator. Return an error message keyed by field path. */
  validate?: FormValuesValidator<TValues>;
  initialValues: TValues;
  onSubmit: (values: TValues, actions: FormActions<TValues>) => void | Promise<void>;
  className?: string;
  children?: ComponentChildren | ((form: FormState<TValues>) => JSX.Element);
};

function Form<TValues extends AnyObject>({ validate: validateValues, initialValues, onSubmit, className, children }: FormProps<TValues>) {
  const [values, setValues] = useState<TValues>(initialValues);
  const [touched, setTouchedState] = useState<Record<string, boolean>>({});
  const [errors, setErrors] = useState<Record<string, string>>({});

  // Registry for per-field validation.
  const fieldRegistry = useRef<Record<string, FieldValidationConfig>>({});

  const resetForm = useCallback(
    (nextInitial?: TValues) => {
      const next = (nextInitial ?? initialValues) as TValues;
      setValues(next);
      setTouchedState({});
      setErrors({});
    },
    [initialValues]
  );

  const __registerField = useCallback((path: string, cfg: FieldValidationConfig) => {
    fieldRegistry.current[path] = cfg;
    return () => {
      delete fieldRegistry.current[path];
    };
  }, []);

  const validateForm = useCallback(async () => {
    const nextErrors: Record<string, string> = {};

    // 1) Form-level validation (optional)
    if (validateValues) {
      Object.assign(nextErrors, normalizeValidationErrors(await validateValues(values)));
    }

    // 2) Field-level validators
    const entries = Object.entries(fieldRegistry.current);
    for (const [path, cfg] of entries) {
      const value = getIn(values, path);
      if (cfg.validate) {
        const msg = cfg.validate(value, values);
        if (typeof msg === 'string' && msg.trim() && !nextErrors[path]) nextErrors[path] = msg;
      }
    }

    setErrors(nextErrors);
    return Object.keys(nextErrors).length === 0;
  }, [validateValues, values]);

  const api = useMemo<FormState<TValues>>(
    () => ({
      values,
      errors,
      touched,
      setValue: (path, value) => setValues((prev) => (Object.is(getIn(prev, path), value) ? prev : setIn(prev, path, value))),
      setTouched: (path, t) => setTouchedState((prev) => ({ ...prev, [path]: t })),
      validate: validateForm,
      __registerField,
    }),
    [values, errors, touched, validateForm, __registerField]
  );

  const onSubmitInternal = async (e: Event) => {
    e.preventDefault();
    const ok = await validateForm();
    if (!ok) return;
    await onSubmit(values, { resetForm });
  };

  return (
    <FormContext.Provider value={api}>
      <FormPathContext.Provider value="">
        <form className={cn('ask-flex ask-flex-col ask-gap-4', className)} onSubmit={onSubmitInternal as any}>
          {typeof children === 'function' ? (children(api) as any) : children}
        </form>
      </FormPathContext.Provider>
    </FormContext.Provider>
  );
}

// ---------------------------------------------------------------------------
// <Form.Item />
// ---------------------------------------------------------------------------

export type FormItemProps = {
  name: string;
  label?: string;
  helper?: string;
  className?: string;
  children: JSX.Element;
  /** Mark the field as required. Pass a string to use it as the validation message. */
  required?: FormRequiredOption;
  /** Optional validation message used when required={true}. */
  requiredMessage?: string;
  /** Custom validator for this field. Return null if ok, otherwise message. */
  validate?: (value: any, values: AnyObject) => string | null | undefined;
  /**
   * Optional sibling-selection validation.
   *
   * - true            => enforce a single `true` among siblings (radio-like)
   * - 'single'        => same as true
   * - 'atLeastOne'    => validates that at least one sibling is true
   * - 'singleRequired'=> enforce single true AND require at least one (can't end up with none)
   */
  unique?: boolean | 'single' | 'atLeastOne' | 'singleRequired' | 'treeSingle' | 'treeSingleRequired';

  /**
   * Render without the <Field> wrapper (inline mode).
   * Useful for building custom layouts while keeping automatic value binding.
   */
  inline?: boolean;
};

function inferChildBindingProps(child: any) {
  // Heuristic binding based on common component props / vnode type.
  // - Checkbox-like: native checkbox (type='checkbox'), explicit 'checked' prop, or our <Checkbox /> component.
  const p = child?.props ?? {};
  const t = child?.type;

  if (p?.type === 'checkbox') return { kind: 'checked' as const };
  if ('checked' in p) return { kind: 'checked' as const };

  const typeName = typeof t === 'function' ? (t.displayName ?? t.name) : '';
  if (typeName === 'Checkbox') return { kind: 'checked' as const };

  return { kind: 'value' as const };
}


function getRootKeyFromPath(fullPath: string): string {
  const m = /^([A-Za-z0-9_]+)/.exec(fullPath);
  return m?.[1] ?? '';
}

function getFieldKeyFromPath(fullPath: string): string {
  const segs = fullPath.split('.');
  return segs[segs.length - 1] ?? '';
}

function collectTreeFieldPaths(values: any, rootKey: string, fieldKey: string): string[] {
  const out: string[] = [];
  const root = values?.[rootKey];
  if (!Array.isArray(root)) return out;

  const walkAny = (node: any, basePath: string) => {
    if (!node || typeof node !== 'object') return;

        // In tree-unique mode we must include *all* nodes in arrays, even if the key doesn't exist yet.
    // Otherwise only items that already have the property become togglable (e.g. only the first checkbox works).
    out.push(`${basePath}.${fieldKey}`);

    for (const k of Object.keys(node)) {
      const v = (node as any)[k];
      if (Array.isArray(v)) {
        for (let i = 0; i < v.length; i++) {
          walkAny(v[i], `${basePath}.${k}[${i}]`);
        }
      }
    }
  };

  for (let i = 0; i < root.length; i++) {
    walkAny(root[i], `${rootKey}[${i}]`);
  }
  return Array.from(new Set(out));
}

function countTreeTrues(values: any, rootKey: string, fieldKey: string): number {
  const paths = collectTreeFieldPaths(values, rootKey, fieldKey);
  let n = 0;
  for (const p of paths) if (getIn(values, p)) n++;
  return n;
}


function getListSiblingInfo(fullPath: string): null | { listBase: string; index: number; fieldKey: string } {
  // Matches: something[3].active
  const m = fullPath.match(/^(.*)\[(\d+)\]\.([^.[\]]+)$/);
  if (!m) return null;
  return { listBase: m[1], index: Number(m[2]), fieldKey: m[3] };
}

export function FormItem({ name, label, helper, className, children, required, requiredMessage, validate: validateProp, unique, inline }: FormItemProps) {
  const form = useForm<any>();
  const base = useContext(FormPathContext);
  const fullName = joinPath(base, name);
  const t = useT();

  const uniqueMode: FormItemProps['unique'] = unique === true ? 'single' : unique;
  const uniqueInfo = useMemo(() => (uniqueMode ? getListSiblingInfo(fullName) : null), [fullName, uniqueMode]);
  const isRequired = Boolean(required);

  // Register per-field validation.
  useEffect(() => {
    const unregister = form.__registerField(fullName, {
      validate: (value, values) => {
        if (isRequired && isRequiredValueMissing(value)) {
          return resolveRequiredMessage(required, requiredMessage, t);
        }

        // Built-in unique validation (optional)
        if (uniqueMode) {
          if (uniqueMode === 'treeSingle' || uniqueMode === 'treeSingleRequired') {
            const rootKey = getRootKeyFromPath(fullName);
            const fieldKey = getFieldKeyFromPath(fullName);
            const trues = countTreeTrues(values, rootKey, fieldKey);
            if (uniqueMode === 'treeSingleRequired' && trues === 0) {
              return t('form.validation.atLeastOne', 'You must set at least one active item');
            }
            if (trues > 1) {
              return t('form.validation.unique', 'Only one item can be active');
            }
          } else if (uniqueInfo) {
            const arr: any[] = Array.isArray(getIn(values, uniqueInfo.listBase)) ? getIn(values, uniqueInfo.listBase) : [];
            const trues = arr.filter((it) => !!it?.[uniqueInfo.fieldKey]).length;
            if (uniqueMode === 'atLeastOne' || uniqueMode === 'singleRequired') {
              if (trues === 0) return t('form.validation.atLeastOne', 'You must set at least one active item');
            }
            if (uniqueMode === 'single' || uniqueMode === 'singleRequired') {
              if (trues > 1) return t('form.validation.unique', 'Only one item can be active');
            }
          }
        }

        const msg = validateProp?.(value, values);
        return msg;
      },
    });
    return unregister;
  }, [form, fullName, required, requiredMessage, validateProp, uniqueMode, uniqueInfo, isRequired, t]);

  const value = getIn(form.values, fullName);
  const error = form.errors[fullName];
  const hint = error ?? helper;

  const childProps = useMemo(() => (children as any)?.props ?? {}, [children]);
  const bind = inferChildBindingProps(children);

  const childValidationProps = useMemo(
    () => ({
      'aria-required': isRequired || childProps['aria-required'] || undefined,
      'aria-invalid': error ? true : childProps['aria-invalid'],
      required: isRequired || childProps.required || undefined,
    }),
    [childProps, error, isRequired]
  );

  const nextChild = useMemo(() => {
    // Preserve user handlers.
    const userOnInput = childProps.onInput;
    const userOnChange = childProps.onChange;
    const userOnBlur = childProps.onBlur;

    if (bind.kind === 'checked') {
      return cloneElement(children as any, {
        ...childValidationProps,
        checked: Boolean(value),
        onChange: (e: any) => {
          const nextChecked = typeof e === 'boolean'
            ? e
            : Boolean(e?.currentTarget?.checked ?? e?.target?.checked);
          // Unique behavior: radio-like among siblings in a list
          if (uniqueMode && (uniqueMode === 'treeSingle' || uniqueMode === 'treeSingleRequired') && nextChecked) {
            const rootKey = getRootKeyFromPath(fullName);
            const fieldKey = getFieldKeyFromPath(fullName);
            const paths = collectTreeFieldPaths(form.values, rootKey, fieldKey);
            for (const p of paths) form.setValue(p, p === fullName);
          } else if (uniqueMode && uniqueInfo && (uniqueMode === 'single' || uniqueMode === 'singleRequired') && nextChecked) {
            const current = form.values;
            const arr: any[] = Array.isArray(getIn(current, uniqueInfo.listBase)) ? getIn(current, uniqueInfo.listBase) : [];
            for (let i = 0; i < arr.length; i++) {
              const p = `${uniqueInfo.listBase}[${i}].${uniqueInfo.fieldKey}`;
              form.setValue(p, i === uniqueInfo.index);
            }
          } else {
            form.setValue(fullName, nextChecked);
          }
          userOnChange?.(e);
        },
        onBlur: (e: any) => {
          form.setTouched(fullName, true);
          userOnBlur?.(e);
        },
      });
    }

    return cloneElement(children as any, {
      ...childValidationProps,
      value: value ?? '',
      onInput: (e: any) => {
        const next = e?.currentTarget?.value;
        form.setValue(fullName, next);
        userOnInput?.(e);
      },
      onChange: (e: any) => {
        // For Select components that only use onChange.
        const next = e?.currentTarget?.value;
        if (next !== undefined) form.setValue(fullName, next);
        userOnChange?.(e);
      },
      onBlur: (e: any) => {
        form.setTouched(fullName, true);
        userOnBlur?.(e);
      },
    });
  }, [bind.kind, children, childProps, childValidationProps, form, fullName, value, uniqueMode, uniqueInfo]);

  if (inline) {
    return <div className={className}>{nextChild}</div>;
  }

  return (
    <div className={className}>
      <Field label={label} required={isRequired} hint={hint ?? (error ? t('common.invalid', 'Invalid value') : undefined)}>
        {nextChild}
      </Field>
    </div>
  );
}

// ---------------------------------------------------------------------------
// <Form.List /> + <Form.Recursive />
// ---------------------------------------------------------------------------

export type FormListProps = {
  name: string;
  label?: string;
  helper?: string;
  className?: string;
  minItems?: number;
  maxItems?: number;
  /** Default item created when clicking "+ Add" */
  defaultItem?: AnyObject;
  /** Optional custom label for the add button. */
  addLabel?: string;

  /** Show the add button at the bottom (Figma-like). Default: true */
  addAtBottom?: boolean;

  /** Allow duplicating items. Default: true */
  allowDuplicate?: boolean;

  /** Hide the built-in item chrome (actions row). */
  inlineItems?: boolean;

  /** Collapse long items behind a compact, readable header. */
  collapsibleItems?: boolean;

  /** Header content used when items are collapsible. */
  itemTitle?: ComponentChildren | ((opts: FormListItemContext & { isOpen: boolean }) => ComponentChildren);

  children:
    | ComponentChildren
    | ((opts: FormListItemContext) => JSX.Element);
};

export type FormListItemContext = {
  index: number;
  item: AnyObject;
  remove?: () => void;
  path: string;
  level?: number;
};

export function FormList({
  name,
  label,
  helper,
  className,
  minItems = 0,
  maxItems = Infinity,
  defaultItem = {},
  addLabel,
  addAtBottom = true,
  allowDuplicate = true,
  inlineItems = false,
  collapsibleItems = false,
  itemTitle,
  children,
  level = 0,
}: FormListProps & { level?: number }) {
  const form = useForm<any>();
  const base = useContext(FormPathContext);
  const fullName = joinPath(base, name);
  const t = useT();

  const items: any[] = useMemo(() => {
    const value = getIn(form.values, fullName);
    return Array.isArray(value) ? value : [];
  }, [form.values, fullName]);
  const getItemKey = useCallback((item: any, index: number) => {
    const stableId = String(item?.id ?? '').trim();
    return stableId || `${fullName}[${index}]`;
  }, [fullName]);
  const itemKeys = useMemo(() => items.map(getItemKey), [getItemKey, items]);
  const previousItemCount = useRef(items.length);
  const [openItemKey, setOpenItemKey] = useState<string | null>(
    collapsibleItems && level === 0 ? (itemKeys[0] ?? null) : null,
  );

  useEffect(() => {
    if (!collapsibleItems) return;
    const addedItem = items.length > previousItemCount.current;
    previousItemCount.current = items.length;
    setOpenItemKey((current) => {
      if (addedItem) return itemKeys[itemKeys.length - 1] ?? null;
      if (current && itemKeys.includes(current)) return current;
      return level === 0 ? (itemKeys[0] ?? null) : null;
    });
  }, [collapsibleItems, itemKeys, items.length, level]);

  const add = () => {
    if (items.length >= maxItems) return;
    const item = { ...(defaultItem ?? {}) };
    const shouldActivateNewVariation =
      item &&
      typeof item === 'object' &&
      (item as any).active === true &&
      /(?:^|\.)variations$/.test(fullName);
    const nextItems = shouldActivateNewVariation
      ? items.map((existing) => (existing && typeof existing === 'object' ? { ...existing, active: false } : existing))
      : items;
    form.setValue(fullName, [...nextItems, item]);
    if (collapsibleItems) setOpenItemKey(getItemKey(item, items.length));
  };

  const duplicateAt = (idx: number) => {
    if (!allowDuplicate) return;
    if (items.length >= maxItems) return;
    const baseItem = items[idx];
    const cloneRecursive = (value: any): any => {
      if (Array.isArray(value)) return value.map(cloneRecursive);
      if (!value || typeof value !== 'object') return value;
      const next: any = {};
      for (const [k, v] of Object.entries(value)) next[k] = cloneRecursive(v);
      if (typeof next.id === 'string' && next.id) {
        next.id = `${next.id}-copy-${Math.random().toString(36).slice(2, 8)}`;
      }
      return next;
    };
    const copy = cloneRecursive(baseItem);
    const next = [...items];
    next.splice(idx + 1, 0, copy);
    form.setValue(fullName, next);
    if (collapsibleItems) setOpenItemKey(getItemKey(copy, idx + 1));
  };

  const move = (from: number, to: number) => {
    if (to < 0 || to >= items.length) return;
    const next = [...items];
    const [it] = next.splice(from, 1);
    next.splice(to, 0, it);
    form.setValue(fullName, next);
  };

  const removeAt = (idx: number) => {
    if (items.length <= minItems) return;
    form.setValue(
      fullName,
      items.filter((_, i) => i !== idx)
    );
  };

  const canAdd = items.length < maxItems;

  const AddButton = (
    <button
      type="button"
      className={cn(
        'ask-text-[12px] ask-text-primary hover:ask-underline',
        !canAdd ? 'ask-pointer-events-none ask-opacity-50' : ''
      )}
      onClick={add}
    >
      {`+ ${addLabel ?? t('common.add')}`}
    </button>
  );

  return (
    <div className={cn('ask-flex ask-flex-col ask-gap-2', className)}>
      <div className="ask-flex ask-items-center ask-justify-between">
        <div className="ask-text-[12px] ask-font-medium ask-text-textMain">{label ?? name}</div>
        {!addAtBottom ? AddButton : null}
      </div>
      {helper ? <div className="ask-text-[11px] ask-text-textMuted">{helper}</div> : null}

      <div className="ask-flex ask-flex-col ask-gap-4">
        {items.map((item, idx) => {
          const itemPath = `${fullName}[${idx}]`;
          const remove = () => removeAt(idx);
          const childCtx = { index: idx, item, remove, level, path: itemPath };
          const renderedChildren = typeof children === 'function' ? children(childCtx) : children;
          const disableRemove = items.length <= minItems;
          const itemKey = getItemKey(item, idx);
          const isOpen = !collapsibleItems || openItemKey === itemKey;
          const renderedTitle = typeof itemTitle === 'function'
            ? itemTitle({ ...childCtx, isOpen })
            : (itemTitle ?? `${t('common.item', 'Item')} ${idx + 1}`);

          const itemChrome = inlineItems ? null : (
            <div className={cn('ask-flex ask-items-center ask-justify-end ask-gap-2', !collapsibleItems && 'ask-mb-2')}>
              <button
                type="button"
                className={cn('ask-text-[12px] hover:ask-underline', idx <= 0 ? 'ask-text-textMuted ask-pointer-events-none' : 'ask-text-textMain')}
                onClick={() => move(idx, idx - 1)}
              >
                ↑
              </button>
              <button
                type="button"
                className={cn(
                  'ask-text-[12px] hover:ask-underline',
                  idx >= items.length - 1 ? 'ask-text-textMuted ask-pointer-events-none' : 'ask-text-textMain'
                )}
                onClick={() => move(idx, idx + 1)}
              >
                ↓
              </button>
              {allowDuplicate ? (
                <button
                  type="button"
                  className={cn('ask-text-[12px] hover:ask-underline', !canAdd ? 'ask-text-textMuted ask-pointer-events-none' : 'ask-text-textMain')}
                  onClick={() => duplicateAt(idx)}
                >
                  {t('common.duplicate', 'Duplicate')}
                </button>
              ) : null}
              <button
                type="button"
                className={cn(
                  'ask-text-[12px] hover:ask-underline',
                  disableRemove ? 'ask-text-textMuted ask-pointer-events-none' : 'ask-text-red-600'
                )}
                onClick={remove}
              >
                {t('common.remove')}
              </button>
            </div>
          );

          return (
            <div key={itemKey} className={cn('ask-rounded-[14px] ask-border ask-border-borderSubtle ask-bg-surfaceAlt', !collapsibleItems && 'ask-p-3')}>
              {collapsibleItems ? (
                <div className="ask-flex ask-min-h-12 ask-items-center ask-gap-2 ask-px-3 ask-py-2">
                  <button
                    type="button"
                    className="ask-flex ask-min-w-0 ask-flex-1 ask-items-center ask-gap-2 ask-text-left"
                    aria-expanded={isOpen}
                    onClick={() => setOpenItemKey((current) => current === itemKey ? null : itemKey)}
                  >
                    <AsukaIcon
                      name="chevron-right"
                      size={13}
                      className={cn('ask-shrink-0 ask-text-textMuted ask-transition-transform', isOpen && 'ask-rotate-90')}
                    />
                    <span className="ask-min-w-0 ask-flex-1">{renderedTitle}</span>
                  </button>
                  {itemChrome}
                </div>
              ) : itemChrome}

              <div className={cn(collapsibleItems && 'ask-border-t ask-border-borderSubtle ask-p-3', collapsibleItems && !isOpen && 'ask-hidden')}>
                <FormPathContext.Provider value={itemPath}>
                  {renderedChildren as any}
                </FormPathContext.Provider>
              </div>
            </div>
          );
        })}
      </div>

      {addAtBottom ? (
        <div className="ask-flex ask-items-center ask-justify-end">{AddButton}</div>
      ) : null}
    </div>
  );
}

export type FormRecursiveProps = Omit<FormListProps, 'children'> & {
  children: ComponentChildren | ((opts: FormListItemContext & { remove: () => void }) => JSX.Element);
  childName?: string;
  indent?: boolean;
  level?: number;
};

export function FormRecursive({ childName = 'variations', indent = true, ...props }: FormRecursiveProps) {
  const level = (props as any).level ?? 0;
  const children = (props as any).children;

  const renderItem = (ctx: any) => {
    const body = typeof children === 'function' ? children(ctx) : children;
    return (
      <div className={cn(indent && ctx.level > 0 ? 'ask-ml-4 ask-pl-3 ask-border-l ask-border-borderSubtle' : '')}>
        {body}
        <div className="ask-mt-3">
          <FormRecursive
            {...(props as any)}
            name={childName}
            label={undefined as any}
            helper={undefined as any}
            minItems={0}
            level={(ctx.level ?? 0) + 1}
            childName={childName}
            indent={indent}
          />
        </div>
      </div>
    );
  };

  return <FormList {...(props as any)} level={level}>{renderItem}</FormList>;
}

// Attach as static properties: Form.Item, Form.List, Form.Recursive
(Form as any).Item = FormItem;
(Form as any).List = FormList;
(Form as any).Recursive = FormRecursive;

export type FormWithStatics = typeof Form & {
  Item: typeof FormItem;
  List: typeof FormList;
  Recursive: typeof FormRecursive;
};


// TypeScript: allow <Form.Item />, <Form.List />, <Form.Recursive />




export const FormWithHelpers = Form as FormWithStatics;
export { FormWithHelpers as Form };

export default FormWithHelpers;
