/* Generic save-on-blur edit helpers used by every screen that wires inline
   editing against a PATCH endpoint. Pattern: render a controlled input that
   tracks local state, calls onChange, and on blur (or debounced) fires save().
   Caller provides save(value) which returns a promise. */

(function () {
  const { useState, useEffect, useRef } = React;

  // <EditableField value="…" onSave={async v => api.patchX(id,{title:v})} />
  function EditableField({ value, onSave, multiline, className, placeholder, validator, mono, serif, autosize }) {
    const [local, setLocal] = useState(value ?? '');
    const [dirty, setDirty] = useState(false);
    const [saving, setSaving] = useState(false);
    const [error, setError] = useState(null);
    const initial = useRef(value ?? '');

    useEffect(() => {
      // Server-driven update wins ONLY when user isn't dirty
      if (!dirty) {
        setLocal(value ?? '');
        initial.current = value ?? '';
      }
    }, [value, dirty]);

    const commit = async () => {
      if (!dirty) return;
      if (validator) {
        const v = validator(local);
        if (v !== true) { setError(v || 'invalid'); return; }
      }
      setSaving(true); setError(null);
      try {
        await onSave(local);
        initial.current = local;
        setDirty(false);
      } catch (err) {
        setError(err.message || 'save failed');
      } finally { setSaving(false); }
    };

    const onChange = (e) => {
      setLocal(e.target.value);
      setDirty(e.target.value !== initial.current);
      if (error) setError(null);
    };

    const onKey = (e) => {
      if (!multiline && e.key === 'Enter') { e.preventDefault(); e.target.blur(); }
      if (e.key === 'Escape') { setLocal(initial.current); setDirty(false); setError(null); e.target.blur(); }
    };

    const baseStyle = autosize && multiline
      ? { minHeight: 60, resize: 'vertical', width: '100%' }
      : { width: '100%' };

    const cls = [
      'inline-edit',
      mono ? 'mono' : '',
      serif ? 'serif' : '',
      dirty ? 'dirty' : '',
      error ? 'has-error' : '',
      className || '',
    ].filter(Boolean).join(' ');

    if (multiline) {
      return (
        <div className="ef-wrap">
          <textarea
            className={cls}
            style={baseStyle}
            value={local}
            placeholder={placeholder}
            onChange={onChange}
            onBlur={commit}
            onKeyDown={onKey}
          />
          {(dirty || saving || error) && (
            <span className="ef-status">
              {saving && <span className="ef-spin"/>}
              {dirty && !saving && <span className="dirty-dot" title="Unsaved"/>}
              {error && <span className="ef-err">{error}</span>}
            </span>
          )}
        </div>
      );
    }
    return (
      <span className="ef-wrap">
        <input
          className={cls}
          style={baseStyle}
          value={local}
          placeholder={placeholder}
          onChange={onChange}
          onBlur={commit}
          onKeyDown={onKey}
        />
        {(dirty || saving || error) && (
          <span className="ef-status">
            {saving && <span className="ef-spin"/>}
            {dirty && !saving && <span className="dirty-dot" title="Unsaved"/>}
            {error && <span className="ef-err">{error}</span>}
          </span>
        )}
      </span>
    );
  }

  window.EditableField = EditableField;
})();
