

export const attachObjectCollectionRuntime = (config: {
  canvas: any;
  isUserLayer: (obj: any) => boolean;
  drawingZones: () => any[];
  getZoneForObject: (obj: any) => any;
  getZoneInnerBounds: (zone: any) => any;
  autoFitObjectToBounds: (obj: any, bounds: any, padding?: number) => void;
  shouldAutoFitObjectToBounds?: (obj: any, zone: any) => boolean;
  emitBefore: (name: string, payload?: any, meta?: any) => void;
  emitAfter: (name: string, payload?: any, meta?: any) => void;
  ensureOrderingSeq: (obj: any, index?: number) => any;
  scheduleNormalizeLayers: (options?: any) => void;
  isHistoryRelevantObject: (obj: any) => boolean;
  scheduleHistorySnapshot: (options?: any) => void;
  syncDocumentObjectsForView: () => void;
  fixedSlotsRuntime?: { restoreIndicatorForRemovedContent?: (obj: any) => boolean };
  useAsukaStore?: any;
}) => {

  const {
    canvas,
    isUserLayer,
    drawingZones,
    getZoneForObject,
    getZoneInnerBounds,
    autoFitObjectToBounds,
    shouldAutoFitObjectToBounds,
    emitBefore,
    emitAfter,
    ensureOrderingSeq,
    scheduleNormalizeLayers,
    isHistoryRelevantObject,
    scheduleHistorySnapshot,
    syncDocumentObjectsForView,
    fixedSlotsRuntime,
    useAsukaStore,
  } = config;

  const isCanvasSnapshotLoading = () => {
    try {
      return Boolean(
        (canvas as any).__asukaSnapshotLoading ||
        (canvas as any).__asukaViewRebuilding ||
        (canvas as any).__asukaStateImporting ||
        (canvas as any).__asukaRestoringFromState,
      );
    } catch {
      return false;
    }
  };

  const isRestoredObject = (obj: any) => {
    try {
      return Boolean(
        (obj as any).__asukaRestoredFromState ||
        (obj as any)._asukaRestoredFromState ||
        (obj as any).__asukaSkipAutoFit,
      );
    } catch {
      return false;
    }
  };

  const onObjectAdded = (e: any) => {
    const obj = e?.target;
    if (!obj) return;

    // loadFromJSON()/view rebuilds fire object:added for transient runtime objects.
    // The logical document state is already the source of truth during that phase,
    // so syncing/history here can overwrite it with half-loaded canvas contents.
    if (isCanvasSnapshotLoading()) return;

    try {
      if (!(obj as any)._asukaAutoFitDone && !isRestoredObject(obj) && isUserLayer(obj) && !(obj as any)._asukaIgnoreDrawingZones) {
        const zones = drawingZones?.() ?? [];
        const zone = (zones.length > 0) ? getZoneForObject(obj) : null;
        const shouldFit = zone
          ? shouldAutoFitObjectToBounds?.(obj, zone) !== false
          : true;

        if (shouldFit) {
          const bounds = zone && zone.width > 2 && zone.height > 2
            ? getZoneInnerBounds(zone)
            : { left: 0, top: 0, width: Math.max(1, Number(canvas.getWidth?.() ?? canvas.width ?? 1) || 1), height: Math.max(1, Number(canvas.getHeight?.() ?? canvas.height ?? 1) || 1) };
          emitBefore('designer.autoFitLayerToBounds', { object: obj, bounds });
          autoFitObjectToBounds(obj, bounds, 12);
          emitAfter('designer.autoFitLayerToBounds', { object: obj, bounds });
          try { canvas.requestRenderAll?.(); } catch {}
        }
      }
    } catch {}

    try {
      ensureOrderingSeq(obj, (canvas.getObjects?.() ?? []).length);
      if (typeof (obj as any).layerIndex !== 'number') {
        (obj as any).layerIndex = ensureOrderingSeq(obj);
      }
    } catch {}

    try { scheduleNormalizeLayers({ assignMissingOnly: false, canvasRef: canvas }); } catch {}
    try {
      if (isHistoryRelevantObject(obj)) {
        scheduleHistorySnapshot({ reason: 'object:added' });
      }
    } catch {}
    try { syncDocumentObjectsForView(); } catch {}
  };

  const onObjectRemoved = (event: any) => {
    const obj: any = event?.target;
    if (!obj) return;

    // A snapshot/view rebuild clears the previous canvas before loading the next
    // logical document. Do not restore markers, sync document objects or commit
    // history from those transient remove events; the post-load lifecycle will
    // rebuild marker indicators from fixedSlots once the canvas is stable.
    if (isCanvasSnapshotLoading()) return;

    try { scheduleNormalizeLayers({ assignMissingOnly: false, canvasRef: canvas }); } catch {}
    try { syncDocumentObjectsForView(); } catch {}

    let didRestoreSlotIndicator = false;
    if (!obj?._asukaFixedSlotIsIndicator) {
      try { didRestoreSlotIndicator = !!fixedSlotsRuntime?.restoreIndicatorForRemovedContent?.(obj); } catch {}
    }
    if (didRestoreSlotIndicator) {
      try { useAsukaStore?.getState?.().setFixedSlotActive?.(null); } catch {}
      try { canvas.requestRenderAll?.(); } catch {}
    }

    try {
      if (isHistoryRelevantObject(obj) || didRestoreSlotIndicator) {
        scheduleHistorySnapshot({ force: true, reason: 'object:removed' });
      }
    } catch {}
  };

  canvas.on('object:added', onObjectAdded);
  canvas.on('object:removed', onObjectRemoved);

  return {
    onObjectAdded,
    onObjectRemoved,
  };
};
