

export const attachSelectionEventsRuntime = (config: {
  canvas: any;
  fabric: any;
  activeDrawingZoneId: any;
  useAsukaStore: any;
  syncDocumentSelectionState: (canvas: any, activeDrawingZoneId: any) => void;
  emitSelectionChange: (reason: string) => void;
  applyClipForObject: (object: any) => void;
  isCurvedPathTextObject: (object: any) => boolean;
  getCurvedPathInnerText: (object: any) => any;
  ensureCurveHandlesForObject: (object: any) => void;
  ensureCurvedHandlesHidden: (object: any) => void;
  asukaEnsureCenterTransformOrigin: (object: any) => void;
  showCurvedGuides: (object: any) => void;
  hideCurvedGuides: () => void;
  syncSelectedZoneOutlineFromSelection: (event?: any) => void;
  removeSelectedZoneOutline: () => void;
  finalizeDrawingAreaDraftById: (id: string) => void;
  isDrawingAreaDraft: (object: any) => boolean;
  ensureObjectId: (object: any) => string;
  getActiveDrawingAreaDraftObjectId: () => string | null;
  setActiveDrawingAreaDraftObjectId: (id: string | null) => void;
  isConstraintsEnabled?: () => boolean;
  getZoneForObject?: (object: any) => any;
  classifyObjectPosition?: (object: any, zone: any) => any;
  updateObjectStrokeForZone?: (object: any, status: any) => void;
  rememberLastGoodPosition?: (object: any, status: any) => void;
}) => {
  const {
    canvas,
    fabric,
    activeDrawingZoneId,
    useAsukaStore,
    syncDocumentSelectionState,
    emitSelectionChange,
    applyClipForObject,
    isCurvedPathTextObject,
    getCurvedPathInnerText,
    ensureCurveHandlesForObject,
    ensureCurvedHandlesHidden,
    asukaEnsureCenterTransformOrigin,
    showCurvedGuides,
    hideCurvedGuides,
    syncSelectedZoneOutlineFromSelection,
    removeSelectedZoneOutline,
    finalizeDrawingAreaDraftById,
    isDrawingAreaDraft,
    ensureObjectId,
    getActiveDrawingAreaDraftObjectId,
    setActiveDrawingAreaDraftObjectId,
    isConstraintsEnabled,
    getZoneForObject,
    classifyObjectPosition,
    updateObjectStrokeForZone,
    rememberLastGoodPosition,
  } = config;

  const getPrimarySelectionTarget = (event?: any) => event?.selected?.[0] || event?.target || canvas.getActiveObject?.() || null;

  const commitPreviousDraftIfNeeded = (nextSelected: any | null) => {
    const nextId = nextSelected ? String(nextSelected?._asukaObjectId ?? '') : null;
    const currentId = getActiveDrawingAreaDraftObjectId();
    if (currentId && currentId !== nextId) {
      finalizeDrawingAreaDraftById(currentId);
    }
    setActiveDrawingAreaDraftObjectId(
      nextSelected && isDrawingAreaDraft(nextSelected)
        ? String(nextSelected?._asukaObjectId ?? ensureObjectId(nextSelected))
        : null,
    );
  };

  const discardIfElementInstance = (evt?: any) => {
    try {
      const t = getPrimarySelectionTarget(evt);
      if (!t) return;
      if ((t._asukaElementIsInstance || t._asukaElementName) && t.selectable !== true) {
        canvas.discardActiveObject?.();
        canvas.requestRenderAll?.();
      }
    } catch {}
  };

  const syncElementSelectionOverlay = () => {
    try {
      const a: any = canvas.getActiveObject?.();
      if (a && a._asukaElementIsInstance && a._asukaElementName && a.selectable !== true) {
        const viewId = useAsukaStore.getState()?.activeViewId ?? null;
        try { useAsukaStore.getState()?.setElementSelection?.({ name: String(a._asukaElementName), viewId }); } catch {}

        try {
          const br = a.getBoundingRect?.(true) ?? null;
          const vpt = canvas.viewportTransform ?? [1, 0, 0, 1, 0, 0];
          const canvasRect = canvas.getElement?.().getBoundingClientRect?.() ?? null;
          if (br && canvasRect && fabric?.util?.transformPoint) {
            const p1 = fabric.util.transformPoint(new fabric.Point(br.left, br.top), vpt);
            const p2 = fabric.util.transformPoint(new fabric.Point(br.left + br.width, br.top + br.height), vpt);
            const left = canvasRect.left + p1.x;
            const top = canvasRect.top + p1.y;
            const width = Math.max(1, p2.x - p1.x);
            const height = Math.max(1, p2.y - p1.y);
            try { useAsukaStore.getState()?.setModuleOverlayAnchorBox?.({ left, top, width, height }); } catch {}
          }
        } catch {}

        try {
          const st: any = useAsukaStore.getState();
          st?.setModuleDisplayModeOverride?.('elements', 'popover');
          if (st?.activeModuleId !== 'elements') st?.openModule?.('elements');
        } catch {}
      } else {
        try { useAsukaStore.getState()?.setElementSelection?.(null); } catch {}
      }
    } catch {}
  };

  const syncSelectionVisuals = () => {
    try {
      const a: any = canvas.getActiveObject?.();
      if (a) {
        if (a.type === 'activeSelection' && typeof a.getObjects === 'function') {
          a.getObjects().forEach((o: any) => applyClipForObject(o));
        } else {
          applyClipForObject(a);
        }
      }
    } catch {}

    try {
      const a: any = canvas.getActiveObject?.();
      if (a && isConstraintsEnabled?.() && getZoneForObject && classifyObjectPosition && updateObjectStrokeForZone) {
        if (a.type === 'activeSelection' && typeof a.getObjects === 'function') {
          a.getObjects().forEach((o: any) => {
            try {
              const status = classifyObjectPosition(o, getZoneForObject(o));
              updateObjectStrokeForZone(o, status);
              rememberLastGoodPosition?.(o, status);
              o.setCoords?.();
            } catch {}
          });
        } else {
          const status = classifyObjectPosition(a, getZoneForObject(a));
          updateObjectStrokeForZone(a, status);
          rememberLastGoodPosition?.(a, status);
          a.setCoords?.();
        }
        canvas.requestRenderAll?.();
      }
    } catch {}

    try {
      const a: any = canvas.getActiveObject?.();
      if (a && isCurvedPathTextObject(a)) {
        const inner = getCurvedPathInnerText(a);
        if (inner) ensureCurveHandlesForObject(inner);
      }
    } catch {}
  };

  const onSelectionCreatedOrUpdated = (reason: 'selection:created' | 'selection:updated', event?: any) => {
    const target = getPrimarySelectionTarget(event);
    try { syncSelectedZoneOutlineFromSelection(event); } catch {}
    try { commitPreviousDraftIfNeeded(target); } catch {}
    try { discardIfElementInstance(event); } catch {}
    try {
      const t = getPrimarySelectionTarget(event);
      if (t) {
        asukaEnsureCenterTransformOrigin(t);
        showCurvedGuides(t);
      }
    } catch {}
    try { syncSelectionVisuals(); } catch {}
    try { syncElementSelectionOverlay(); } catch {}
    try { syncDocumentSelectionState(canvas, activeDrawingZoneId); } catch {}
    emitSelectionChange(reason);
  };

  const onSelectionCleared = () => {
    try { removeSelectedZoneOutline(); } catch {}
    try {
      const currentId = getActiveDrawingAreaDraftObjectId();
      if (currentId) finalizeDrawingAreaDraftById(currentId);
      setActiveDrawingAreaDraftObjectId(null);
    } catch {}
    try { hideCurvedGuides(); } catch {}
    try {
      const objs: any[] = canvas?._objects || [];
      objs.forEach((o: any) => {
        if (o && o._asukaCurvedText && o._asukaCurveHandles) ensureCurvedHandlesHidden(o);
      });
    } catch {}
    try { useAsukaStore.getState()?.setElementSelection?.(null); } catch {}
    try { syncDocumentSelectionState(canvas, activeDrawingZoneId); } catch {}
    emitSelectionChange('selection:cleared');
  };

  canvas.on('selection:created', (event: any) => onSelectionCreatedOrUpdated('selection:created', event));
  canvas.on('selection:updated', (event: any) => onSelectionCreatedOrUpdated('selection:updated', event));
  canvas.on('selection:cleared', onSelectionCleared);

  return {
    commitPreviousDraftIfNeeded,
    discardIfElementInstance,
    syncSelectionVisuals,
    syncElementSelectionOverlay,
    onSelectionCreatedOrUpdated,
    onSelectionCleared,
  };
};
