

export const createDrawingAreasInteractionRuntime = (config: {
  canvas: any;
  fabric: any;
  getDrawingZones: () => any[];
  setDrawingZones: (zones: any[]) => void;
  getZone: () => any;
  getActiveDrawingZoneId: () => string | null;
  setActiveDrawingZoneId: (id: string | null) => void;
  getZonePickActive: () => boolean;
  setZonePickActive: (value: boolean) => void;
  getZonePickResolve: () => ((id: string) => void) | null;
  setZonePickResolve: (fn: ((id: string) => void) | null) => void;
  getZonePickReject: () => ((err: any) => void) | null;
  setZonePickReject: (fn: ((err: any) => void) | null) => void;
  getZonePickRects: () => any[];
  setZonePickRects: (rects: any[]) => void;
  getPendingAddZoneId: () => string | null;
  setPendingAddZoneId: (id: string | null) => void;
  getActiveDrawingAreaDraftObjectId: () => string | null;
  setActiveDrawingAreaDraftObjectId: (id: string | null) => void;
  isDrawingAreaDraft: (obj: any) => boolean;
  getObjectWorldBounds: (obj: any) => { left: number; top: number; width: number; height: number } | null;
  getRuntimeObjectByIdFast: (objectId: string | null | undefined) => any;
  emitAsukaEvent: (name: string, payload?: any) => void;
  syncDrawingZoneVisuals: () => void;
  scheduleHistorySnapshot: (options?: any) => void;
  createZoneClipObjectSync: (zone: any, bounds: any) => any;
  normalizeSvgObjectToRect: (obj: any, bounds: any) => void;
}) => {
  const {
    canvas,
    fabric,
    getDrawingZones,
    setDrawingZones,
    getZone,
    setActiveDrawingZoneId,
    getZonePickActive,
    setZonePickActive,
    getZonePickResolve,
    setZonePickResolve,
    getZonePickReject,
    setZonePickReject,
    getZonePickRects,
    setZonePickRects,
    getPendingAddZoneId,
    setPendingAddZoneId,
    getActiveDrawingAreaDraftObjectId,
    setActiveDrawingAreaDraftObjectId,
    isDrawingAreaDraft,
    getObjectWorldBounds,
    getRuntimeObjectByIdFast,
    emitAsukaEvent,
    syncDrawingZoneVisuals,
    scheduleHistorySnapshot,
    createZoneClipObjectSync,
    normalizeSvgObjectToRect,
  } = config;

  const requestRender = () => {
    try { canvas.requestRenderAll?.(); } catch { try { canvas.renderAll?.(); } catch {} }
  };

  const clearZonePickRects = () => {
    try {
      for (const r of getZonePickRects()) {
        try { canvas.remove(r); } catch {}
      }
    } catch {}
    setZonePickRects([]);
    requestRender();
  };

  const createDrawingAreaFromDraftObject = (obj: any): string | null => {
    if (!isDrawingAreaDraft(obj)) return null;
    const bounds = getObjectWorldBounds(obj);
    if (!bounds) return null;
    const nextId = String((obj as any)._asukaDraftDrawingAreaId || `zone-${Date.now()}`);
    const nextZone: any = {
      id: nextId,
      left: bounds.left,
      top: bounds.top,
      width: bounds.width,
      height: bounds.height,
      shape: (((obj as any)._asukaDraftDrawingAreaShape as any) ?? 'rect'),
      sourceSvg: typeof (obj as any)._asukaDraftDrawingAreaSvg === 'string' ? String((obj as any)._asukaDraftDrawingAreaSvg) : undefined,
      sourcePath: typeof (obj as any)._asukaDraftDrawingAreaPath === 'string' ? String((obj as any)._asukaDraftDrawingAreaPath) : undefined,
    };
    const list = Array.isArray(getDrawingZones()) ? getDrawingZones().slice() : [];
    const existingIdx = list.findIndex((z: any) => z.id === nextId);
    if (existingIdx >= 0) list[existingIdx] = nextZone;
    else list.push(nextZone);
    try { canvas.remove(obj); } catch {}
    setDrawingZones(list);
    setActiveDrawingZoneId(nextId);
    try {
      if (getZonePickActive() || (getZonePickRects() && getZonePickRects().length)) {
        setZonePickActive(false);
        setZonePickResolve(null);
        setZonePickReject(null);
        setPendingAddZoneId(null);
        clearZonePickRects();
      }
    } catch {}
    emitAsukaEvent('drawingZones.updated', { zones: list.map((z: any) => ({ id: z.id, x: z.left, y: z.top, width: z.width, height: z.height, shape: z.shape, sourceSvg: z.sourceSvg, sourcePath: z.sourcePath })) });
    emitAsukaEvent('drawingZones.activeChanged', { id: nextId });
    try { syncDrawingZoneVisuals(); } catch {}
    try { scheduleHistorySnapshot(); } catch {}
    requestRender();
    setActiveDrawingAreaDraftObjectId(null);
    return nextId;
  };

  const finalizeDrawingAreaDraftById = (objectId: string | null | undefined) => {
    if (!objectId) return null;
    const obj = getRuntimeObjectByIdFast(String(objectId));
    if (!obj || !isDrawingAreaDraft(obj)) return null;
    return createDrawingAreaFromDraftObject(obj);
  };

  const createZonePickerVisual = (z: any) => {
    const shape = String((z as any)?.shape ?? 'rect');
    const common: any = {
      fill: 'rgba(59, 130, 246, 0.08)',
      stroke: '#3b82f6',
      strokeWidth: 1.5,
      strokeUniform: true,
      strokeDashArray: [6, 4],
      selectable: false,
      evented: true,
      hasControls: false,
      hasBorders: false,
      objectCaching: true,
      excludeFromExport: true,
      hoverCursor: 'pointer',
      absolutePositioned: true,
    };
    try {
      if (shape === 'svg' && typeof (z as any)?.sourceSvg === 'string' && (z as any).sourceSvg) return null;
      const overlay = createZoneClipObjectSync(z as any, { left: z.left, top: z.top, width: z.width, height: z.height });
      if (!overlay) return null;
      overlay.set?.(common);
      (overlay as any)._asukaRole = 'drawingZonePicker';
      (overlay as any)._asukaZonePickId = z.id;
      (overlay as any).itemGroup = 'Tools';
      return overlay;
    } catch {
      return null;
    }
  };

  const finalizeSvgZonePickerVisual = (z: any, refId: string, res: any) => {
    try {
      const rawObjects = Array.isArray(res) ? (res[0] ?? []) : (res?.objects ?? []);
      const objects = Array.isArray(rawObjects) ? rawObjects.filter(Boolean) : (rawObjects ? [rawObjects] : []);
      const options = Array.isArray(res) ? (res[1] ?? {}) : (res?.options ?? {});
      let overlay: any = null;
      const groupFn = (fabric as any).util?.groupSVGElements;
      if (objects.length === 1) overlay = objects[0];
      if (!overlay && typeof groupFn === 'function') {
        try { overlay = groupFn(objects, options || {}); } catch {}
      }
      if (!overlay && typeof (fabric as any).Group === 'function') {
        try { overlay = new (fabric as any).Group(objects, options || {}); } catch {}
      }
      if (!overlay) return;
      try { overlay.set({ originX: 'left', originY: 'top', left: Number(z.left ?? 0), top: Number(z.top ?? 0), absolutePositioned: true }); } catch {}
      normalizeSvgObjectToRect(overlay, { left: z.left, top: z.top, width: z.width, height: z.height });
      overlay.set?.({
        fill: 'rgba(59, 130, 246, 0.08)',
        stroke: '#3b82f6',
        strokeWidth: 1.5,
        strokeUniform: true,
        strokeDashArray: [6, 4],
        selectable: false,
        evented: true,
        hasControls: false,
        hasBorders: false,
        excludeFromExport: true,
        hoverCursor: 'pointer',
        objectCaching: true,
        absolutePositioned: true,
      });
      const walk = (node: any) => {
        if (!node) return;
        try { node.set?.({
          fill: 'rgba(59, 130, 246, 0.08)',
          stroke: '#3b82f6',
          strokeWidth: 1.5,
          strokeUniform: true,
          strokeDashArray: [6, 4],
          selectable: false,
          evented: false,
          hasControls: false,
          hasBorders: false,
          excludeFromExport: true,
          hoverCursor: 'pointer',
          objectCaching: true,
          absolutePositioned: true,
        }); } catch {}
        try {
          const children = typeof node.getObjects === 'function' ? node.getObjects() : [];
          for (const child of children || []) walk(child);
        } catch {}
      };
      walk(overlay);
      (overlay as any)._asukaRole = 'drawingZonePicker';
      (overlay as any)._asukaZonePickId = z.id;
      (overlay as any).itemGroup = 'Tools';
      if (!getZonePickActive()) return;
      const current = getZonePickRects();
      const idx = current.findIndex((r: any) => String((r as any)?._asukaZonePickId ?? '') === refId);
      if (idx >= 0) {
        try { canvas.remove(current[idx]); } catch {}
        current.splice(idx, 1);
      }
      current.push(overlay);
      setZonePickRects(current);
      canvas.add(overlay);
      try { canvas.bringToFront?.(overlay); } catch {}
      requestRender();
    } catch {}
  };

  const startZonePick = (): Promise<string> => {
    const drawingZones = getDrawingZones();
    if (drawingZones.length <= 1) {
      const only = drawingZones[0] ?? getZone();
      setActiveDrawingZoneId(only.id);
      setPendingAddZoneId(only.id);
      return Promise.resolve(only.id);
    }

    if (getZonePickActive()) {
      return new Promise((resolve, reject) => {
        const prevResolve = getZonePickResolve();
        const prevReject = getZonePickReject();
        setZonePickResolve((id) => { try { prevResolve?.(id); } finally { resolve(id); } });
        setZonePickReject((e) => { try { prevReject?.(e); } finally { reject(e); } });
      });
    }

    setZonePickActive(true);
    try { canvas.discardActiveObject?.(); } catch {}
    try { (canvas as any).fire?.('selection:cleared'); } catch {}

    clearZonePickRects();
    const nextRects: any[] = [];
    for (const z of drawingZones) {
      let r = createZonePickerVisual(z);
      if (!r) {
        r = new fabric.Rect({
          left: z.left,
          top: z.top,
          width: z.width,
          height: z.height,
          fill: 'rgba(59, 130, 246, 0.04)',
          stroke: 'rgba(59, 130, 246, 0.35)',
          strokeWidth: 1,
          strokeUniform: true,
          strokeDashArray: [3, 3],
          selectable: false,
          evented: false,
          hasControls: false,
          hasBorders: false,
          objectCaching: true,
          excludeFromExport: true,
          hoverCursor: 'pointer',
        });
        (r as any)._asukaRole = 'drawingZonePickerFallback';
        (r as any)._asukaZonePickId = z.id;
        (r as any).itemGroup = 'Tools';
        if (String((z as any)?.shape ?? 'rect') === 'svg' && typeof (z as any)?.sourceSvg === 'string' && (z as any).sourceSvg) {
          try {
            const load = (fabric as any).loadSVGFromString?.((z as any).sourceSvg);
            const finish = (res: any) => finalizeSvgZonePickerVisual(z, z.id, res);
            if (load && typeof load.then === 'function') load.then((res: any) => finish(res)).catch(() => {});
            else (fabric as any).loadSVGFromString((z as any).sourceSvg, (objects: any[], options: any) => finish({ objects, options }));
          } catch {}
        }
      }
      nextRects.push(r);
      canvas.add(r);
      try { canvas.bringToFront?.(r); } catch {}
    }
    setZonePickRects(nextRects);
    requestRender();

    return new Promise((resolve, reject) => {
      setZonePickResolve(resolve);
      setZonePickReject(reject);
    });
  };

  const finishZonePick = (pickedId: string) => {
    setActiveDrawingZoneId(pickedId);
    setPendingAddZoneId(pickedId);
    setZonePickActive(false);
    const resolve = getZonePickResolve();
    setZonePickResolve(null);
    setZonePickReject(null);
    clearZonePickRects();
    try { resolve?.(pickedId); } catch {}
    try { syncDrawingZoneVisuals(); } catch {}
  };

  const cancelZonePick = (err?: any) => {
    setZonePickActive(false);
    const reject = getZonePickReject();
    setZonePickResolve(null);
    setZonePickReject(null);
    clearZonePickRects();
    try { reject?.(err ?? new Error('zone pick cancelled')); } catch {}
  };

  const ensureZoneForAdd = async (): Promise<string> => {
    const drawingZones = getDrawingZones();
    if (!drawingZones.length) {
      setActiveDrawingZoneId(null);
      setPendingAddZoneId(null);
      return 'canvas';
    }
    if (drawingZones.length <= 1) {
      const only = drawingZones[0] ?? getZone();
      setActiveDrawingZoneId(only.id);
      setPendingAddZoneId(only.id);
      return only.id;
    }
    return await startZonePick();
  };

  return {
    clearZonePickRects,
    createDrawingAreaFromDraftObject,
    finalizeDrawingAreaDraftById,
    startZonePick,
    finishZonePick,
    cancelZonePick,
    ensureZoneForAdd,
    getPendingAddZoneId,
    getActiveDrawingAreaDraftObjectId,
  };
};
