

export const createRendererLifecycleRuntime = (config: {
  canvas: any;
  instance: any;
  useAsukaStore: any;
  sanitizeFabricControls: () => void;
  selectionOutlineRuntime: { reset?: () => void };
  curvedGuideRuntime: { reset?: () => void };
  snapLinesRuntime: { reset?: () => void };
  clipPathsRuntime: { reset?: () => void };
  resetDrawingZoneRect: () => void;
  rebuildRuntimeObjectIndex: () => void;
  syncDrawingZoneVisuals: () => void;
  applyAsukaControlsDeep: (obj: any) => void;
  debugDescribeControls?: (obj: any) => any;
  getControlsDebugEnabled?: () => boolean;
  isControlEligible?: (obj: any) => boolean;
  importViewSnapshots: (snapshots: any, options?: any) => void;
  getSnapshotForView: (viewId: string) => any;
  getDocumentObjectsState: () => Record<string, any[]>;
  switchViewWithCanvas: (instance: any, viewId: string) => void;
  syncDocumentViewportState: (viewId: string | null, canvas: any) => void;
  syncDocumentSelectionState: (canvas: any, drawingAreaId: string | null) => void;
  rebindSpecialObjectsImpl: () => void;
  fixedSlotsRuntime?: { ensureIndicatorsForSlots?: () => boolean };
}) => {
  const {
    canvas,
    instance,
    useAsukaStore,
    sanitizeFabricControls,
    selectionOutlineRuntime,
    curvedGuideRuntime,
    snapLinesRuntime,
    clipPathsRuntime,
    resetDrawingZoneRect,
    rebuildRuntimeObjectIndex,
    syncDrawingZoneVisuals,
    applyAsukaControlsDeep,
    getControlsDebugEnabled,
    isControlEligible,
    importViewSnapshots,
    getSnapshotForView,
    getDocumentObjectsState,
    switchViewWithCanvas,
    syncDocumentViewportState,
    syncDocumentSelectionState,
    rebindSpecialObjectsImpl,
    fixedSlotsRuntime,
  } = config;

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

  const normalizeDrawingAreas = (zones: any[] | null | undefined): any[] => {
    if (!Array.isArray(zones)) return [];
    const byId = new Map<string, any>();
    const order: string[] = [];
    for (const raw of zones) {
      if (!raw || typeof raw !== 'object') continue;
      const id = String(raw.id ?? '').trim();
      if (!id) continue;
      const next = {
        id,
        x: Number(raw.x ?? raw.left ?? 0) || 0,
        y: Number(raw.y ?? raw.top ?? 0) || 0,
        width: Math.max(1, Number(raw.width ?? 0) || 1),
        height: Math.max(1, Number(raw.height ?? 0) || 1),
        shape: raw.shape,
        sourceSvg: typeof raw.sourceSvg === 'string' ? raw.sourceSvg : undefined,
        sourcePath: typeof raw.sourcePath === 'string' ? raw.sourcePath : undefined,
      };
      if (!byId.has(id)) order.push(id);
      byId.set(id, next);
    }
    return order.map((id) => byId.get(id)).filter(Boolean);
  };

  const sanitizeDocumentObjects = (objects: any[] | null | undefined): any[] => {
    if (!Array.isArray(objects)) return [];
    return objects.filter((obj: any) => {
      const role = String(obj?._asukaRole ?? '').trim();
      if (role === 'drawingZone' || role.startsWith('drawingZone')) return false;
      if (role === 'tool' || role === 'drawingAreaDraft' || role === 'clipPath' || role === 'snapVertical' || role === 'snapHorizontal' || role === 'shapePreview') return false;
      if (obj?._asukaFixedSlotIsIndicator) return false;
      return true;
    });
  };


  const readDrawingAreasForSnapshot = (viewId: string, base: any, state: any): any[] => {
    const activeViewId = String(state?.activeViewId ?? '').trim();
    if (String(viewId) === activeViewId) {
      try {
        const runtimeZones = instance?.getDrawingZones?.();
        if (Array.isArray(runtimeZones)) return normalizeDrawingAreas(runtimeZones);
      } catch {}
      return normalizeDrawingAreas(state?.documentDrawingAreas);
    }
    return normalizeDrawingAreas((base as any)?.drawingAreas);
  };

  const readActiveDrawingAreaForSnapshot = (viewId: string, base: any, state: any, drawingAreas: any[]): string | null => {
    const activeViewId = String(state?.activeViewId ?? '').trim();
    const raw = String(
      String(viewId) === activeViewId
        ? (state?.documentSelection?.activeDrawingAreaId ?? instance?.getActiveDrawingZone?.() ?? '')
        : ((base as any)?.activeDrawingAreaId ?? ''),
    ).trim();
    return raw && drawingAreas.some((z: any) => String(z.id) === raw) ? raw : null;
  };

  const ensureFixedSlotsNowAndSoon = () => {
    const run = () => {
      try { fixedSlotsRuntime?.ensureIndicatorsForSlots?.(); } catch {}
    };

    run();
    try {
      const scheduler = (globalThis as any)?.queueMicrotask;
      if (typeof scheduler === 'function') scheduler(run);
      else setTimeout(run, 0);
    } catch {}
    try { requestAnimationFrame(() => run()); } catch { try { setTimeout(run, 16); } catch {} }
    try { setTimeout(run, 50); } catch {}
  };

  const refreshAfterViewLoad = () => {
    try { sanitizeFabricControls(); } catch {}
    try { canvas.discardActiveObject?.(); } catch {}
    try { selectionOutlineRuntime.reset?.(); } catch {}
    try { curvedGuideRuntime.reset?.(); } catch {}
    try { resetDrawingZoneRect(); } catch {}
    try { snapLinesRuntime.reset?.(); } catch {}
    try { clipPathsRuntime.reset?.(); } catch {}
    try { rebuildRuntimeObjectIndex(); } catch {}
    try { rebindSpecialObjectsImpl(); } catch {}
    try { syncDrawingZoneVisuals(); } catch {}
    ensureFixedSlotsNowAndSoon();

    try {
      const objs = canvas.getObjects?.() ?? [];
      if (getControlsDebugEnabled?.()) void 0;
      if (Array.isArray(objs)) {
        objs.forEach((o: any) => {
          try {
            const c = (o as any).controls;
            if (c && typeof c === 'object') {
              Object.keys(c).forEach((k: string) => {
                const v = c[k];
                if (!v || typeof v !== 'object' || typeof v.x !== 'number' || typeof v.y !== 'number') {
                  try { delete c[k]; } catch {}
                }
              });
            }
          } catch {}
          if (isControlEligible && !isControlEligible(o)) return;
          applyAsukaControlsDeep(o);
          if (getControlsDebugEnabled?.()) void 0;
        });
      }
      const active = canvas.getActiveObject?.();
      if (active) applyAsukaControlsDeep(active);
    } catch {}

    try {
      if (!(canvas as any).__asukaControlsListenerBound) {
        (canvas as any).__asukaControlsListenerBound = true;
        canvas.on?.('selection:created', () => {
          try {
            const a = canvas.getActiveObject?.();
            if (a) applyAsukaControlsDeep(a);
            if (getControlsDebugEnabled?.()) void 0;
          } catch {}
        });
        canvas.on?.('selection:updated', () => {
          try {
            const a = canvas.getActiveObject?.();
            if (a) applyAsukaControlsDeep(a);
            if (getControlsDebugEnabled?.()) void 0;
          } catch {}
        });
      }
    } catch {}

    requestRender();
  };

  const rebuildViewFromDocumentState = (targetViewId?: string | null) => {
    const state = useAsukaStore.getState() as any;
    const viewId = String(targetViewId ?? state?.activeViewId ?? '').trim();
    if (!viewId) return;
    const byView = getDocumentObjectsState();
    const rebuilt = Object.fromEntries(
      Object.entries(byView).map(([id, objects]) => {
        const viewId = String(id);
        const base = getSnapshotForView(viewId) ?? {};
        const drawingAreas = readDrawingAreasForSnapshot(viewId, base, state);
        const activeDrawingAreaId = readActiveDrawingAreaForSnapshot(viewId, base, state, drawingAreas);
        return [viewId, {
          ...(base && typeof base === 'object' ? base : {}),
          objects: JSON.parse(JSON.stringify(sanitizeDocumentObjects(Array.isArray(objects) ? objects : []))),
          drawingAreas,
          activeDrawingAreaId,
        }];
      }),
    );
    try { importViewSnapshots(rebuilt as any, { replace: true }); } catch {}
    try { switchViewWithCanvas(instance as any, viewId); } catch {}
    try { syncDocumentViewportState(viewId || null, canvas); } catch {}
    try { syncDocumentSelectionState(canvas, (useAsukaStore.getState() as any)?.documentSelection?.activeDrawingAreaId ?? null); } catch {}
    try { rebindSpecialObjectsImpl(); } catch {}
    ensureFixedSlotsNowAndSoon();
    try { canvas.calcOffset?.(); } catch {}
    requestRender();
  };

  const loadDocumentSnapshot = (snapshot: any) => {
    if (!snapshot || typeof snapshot !== 'object') return;
    const nextObjectsByView = (snapshot.objectsByView && typeof snapshot.objectsByView === 'object') ? snapshot.objectsByView : {};
    const nextViewportByView = (snapshot.viewportByView && typeof snapshot.viewportByView === 'object') ? snapshot.viewportByView : {};
    const nextScenes3dByView = ((snapshot as any).scenes3dByView && typeof (snapshot as any).scenes3dByView === 'object') ? (snapshot as any).scenes3dByView : null;
    const nextSelection = (snapshot.selection && typeof snapshot.selection === 'object')
      ? snapshot.selection
      : { activeObjectIds: [], activeDrawingAreaId: null };
    const targetViewId = String(snapshot.activeViewId ?? (useAsukaStore.getState() as any)?.activeViewId ?? '').trim();
    const targetEngineKind = String((snapshot as any)?.activeRenderEngineKind ?? '').trim().toLowerCase();
    if (targetViewId && targetEngineKind !== '3d') {
      try {
        (canvas as any).__asukaSnapshotLoading = true;
        (canvas as any).__asukaRestoringFromState = true;
      } catch {}
    }

    try {
      const markerPatch: any = {};
      const catalogPatch: any = {};
      if (Array.isArray((snapshot as any).elements)) {
        catalogPatch.elements = JSON.parse(JSON.stringify((snapshot as any).elements));
      }
      if (Array.isArray((snapshot as any).fixedSlots)) {
        markerPatch.fixedSlots = JSON.parse(JSON.stringify((snapshot as any).fixedSlots));
        markerPatch.fixedSlotActiveId = (snapshot as any).fixedSlotActiveId ?? null;
        markerPatch.fixedSlotsPlacing = null;
      }
      const engineKind = targetEngineKind;
      useAsukaStore.setState({
        documentObjectsByView: JSON.parse(JSON.stringify(nextObjectsByView)),
        documentViewportByView: JSON.parse(JSON.stringify(nextViewportByView)),
        ...(nextScenes3dByView ? { document3dScenesByView: JSON.parse(JSON.stringify(nextScenes3dByView)) } : null),
        ...(engineKind === '3d' || engineKind === 'fabric' ? { activeRenderEngineKind: engineKind } : null),
        documentSelection: JSON.parse(JSON.stringify(nextSelection)),
        ...catalogPatch,
        ...markerPatch,
      } as any);
    } catch {}

    const drawingAreasByView = ((snapshot as any)?.drawingAreasByView && typeof (snapshot as any).drawingAreasByView === 'object') ? (snapshot as any).drawingAreasByView : {};
    const rebuilt = Object.fromEntries(
      Object.entries(nextObjectsByView).map(([viewId, objects]) => {
        const base = getSnapshotForView(String(viewId)) ?? {};
        const drawingAreas = Array.isArray((drawingAreasByView as any)?.[String(viewId)])
          ? normalizeDrawingAreas((drawingAreasByView as any)[String(viewId)])
          : normalizeDrawingAreas((base as any)?.drawingAreas ?? []);
        const activeDrawingAreaId = readActiveDrawingAreaForSnapshot(String(viewId), base, useAsukaStore.getState?.() ?? {}, drawingAreas);
        return [String(viewId), {
          ...(base && typeof base === 'object' ? base : {}),
          objects: JSON.parse(JSON.stringify(sanitizeDocumentObjects(Array.isArray(objects) ? objects : []))),
          drawingAreas,
          activeDrawingAreaId,
        }];
      }),
    );
    try { importViewSnapshots(rebuilt as any, { replace: true }); } catch {}
    if (targetViewId) {
      try { switchViewWithCanvas(instance as any, targetViewId); } catch {}
    }
    try { syncDocumentViewportState(targetViewId || null, canvas); } catch {}
    try { syncDocumentSelectionState(canvas, nextSelection?.activeDrawingAreaId ?? null); } catch {}
    try { canvas.discardActiveObject?.(); } catch {}
    try { rebindSpecialObjectsImpl(); } catch {}
    ensureFixedSlotsNowAndSoon();
    try { canvas.calcOffset?.(); } catch {}
    requestRender();
  };

  return {
    refreshAfterViewLoad,
    loadDocumentSnapshot,
    rebuildViewFromDocumentState,
    rebindSpecialObjects: rebindSpecialObjectsImpl,
  };
};
