

export const attachTextEditingRuntime = (config: {
  canvas: any;
  isCurvedPathTextObject: (target: any) => boolean;
  updateCurvedPathObject: (target: any, patch: any) => void;
  scheduleHistorySnapshot: (options?: any) => void;
  autoSizeTextboxToContent?: (target: any) => void;
}) => {
  const { canvas, isCurvedPathTextObject, updateCurvedPathObject, scheduleHistorySnapshot, autoSizeTextboxToContent } = config;

  let rafCurvedTextAuto = 0 as any;
  let pendingCurvedTextAutoTarget: any = null;

  const scheduleCurvedTextAutoFit = (target: any) => {
    if (!target) return;
    if (!isCurvedPathTextObject(target)) return;
    const d: any = (target as any)._asukaCurvedTextData;
    if (!d || d.type !== 'circle') return;
    if (d.circleAutoFit === false) return;
    pendingCurvedTextAutoTarget = target;
    if (rafCurvedTextAuto) return;
    rafCurvedTextAuto = requestAnimationFrame(() => {
      rafCurvedTextAuto = 0;
      const t = pendingCurvedTextAutoTarget;
      pendingCurvedTextAutoTarget = null;
      if (!t || !t.canvas) return;
      updateCurvedPathObject(t, {});
      try { t.setCoords?.(); } catch {}
      try { t.canvas?.requestRenderAll?.(); } catch {}
    });
  };

  const onTextChanged = (e: any) => {
    const t: any = e?.target;
    try {
      const c: any = t?._asukaFixedConstraints;
      if (t && c && typeof t.text === 'string') {
        let text = String(t.text ?? '');
        if (typeof c.maxChars === 'number' && c.maxChars >= 0) text = text.slice(0, c.maxChars);
        if (typeof c.maxLines === 'number' && c.maxLines >= 1) text = text.split(/\r?\n/).slice(0, c.maxLines).join('\n');
        if (text !== t.text) {
          t.text = text;
          t.dirty = true;
        }
        (t as any).__asukaFixedLastEditingText = String(t.text ?? '');
        if (typeof c.minChars !== 'number' || String(t.text ?? '').length >= c.minChars) {
          (t as any).__asukaFixedLastValidText = String(t.text ?? '');
        }
      }
    } catch {}
    try { autoSizeTextboxToContent?.(t); } catch {}
    try { t?.setCoords?.(); } catch {}
    try { t?.canvas?.requestRenderAll?.(); } catch {}
    try { scheduleCurvedTextAutoFit(e?.target); } catch {}
  };

  const onTextEditingExited = (e: any) => {
    const t = e?.target;
    if (!t) return;
    try {
      const c: any = (t as any)?._asukaFixedConstraints;
      if (c && typeof t.text === 'string' && typeof c.minChars === 'number' && c.minChars >= 0) {
        const current = String(t.text ?? '');
        if (current.length < c.minChars) {
          const fallback = String((t as any).__asukaFixedLastValidText ?? (t as any).__asukaFixedLastEditingText ?? current);
          t.text = fallback.length >= c.minChars ? fallback : current;
          t.dirty = true;
          t.setCoords?.();
        } else {
          (t as any).__asukaFixedLastValidText = current;
        }
      }

      if (isCurvedPathTextObject(t)) {
        const d: any = (t as any)._asukaCurvedTextData;
        if (d && d.type === 'circle') updateCurvedPathObject(t, {});
      }
      try { autoSizeTextboxToContent?.(t); } catch {}
      scheduleHistorySnapshot();
      t.canvas?.requestRenderAll?.();
    } catch {}
  };

  canvas.on('text:changed', onTextChanged);
  canvas.on('text:editing:exited', onTextEditingExited);

  return {
    scheduleCurvedTextAutoFit,
    onTextChanged,
    onTextEditingExited,
  };
};
