import '../core/fabricCrashKiller';
import { initDesignerCanvas, type DesignerInstance } from '../core/designer';
import {
  createFabricRenderEngineAdapter,
  createRenderEngineEvents,
  emitRenderEngineEvent,
  getDefaultRenderEngineCapabilities,
  type AsukaRenderEngine,
  type AsukaRenderEngineFactory,
} from '../core/renderEngine';
import { onAsukaEvent } from '../core/events';
import { useAsukaStore } from '../store/asukaStore';
import { mountFabricView, saveSnapshotForView, switchViewWithCanvas } from '../core/viewSnapshots';

export interface AsukaFabricEngineAddonOptions {
  backgroundColor?: string;
  [key: string]: unknown;
}

export type AsukaFabricRenderEngine = AsukaRenderEngine;

export const createAsukaFabricRenderEngine: AsukaRenderEngineFactory = (rawOptions): AsukaFabricRenderEngine => {
  const options = (rawOptions ?? {}) as AsukaFabricEngineAddonOptions;
  let designerInstance: DesignerInstance | null = null;
  let adapter: AsukaRenderEngine | null = null;
  let resizeObserver: ResizeObserver | null = null;
  let resizeRaf = 0;
  let eventDisposers: Array<() => void> = [];

  const requireAdapter = (): AsukaRenderEngine => {
    if (!adapter) {
      throw new Error('[AsukaDesigner] Fabric render engine is not mounted yet.');
    }
    return adapter;
  };

  const cleanup = () => {
    eventDisposers.forEach((dispose) => { try { dispose(); } catch {} });
    eventDisposers = [];
    try { resizeObserver?.disconnect?.(); } catch {}
    resizeObserver = null;
    if (resizeRaf) {
      try { cancelAnimationFrame(resizeRaf); } catch {}
      resizeRaf = 0;
    }
    try { designerInstance?.dispose?.(); } catch {}
    try {
      const state = useAsukaStore.getState() as any;
      if (state?.designerInstance === designerInstance) state?.setDesignerInstance?.(null);
    } catch {}
    designerInstance = null;
    adapter = null;
  };

  const engine: AsukaFabricRenderEngine = {
    kind: 'fabric',
    get capabilities() { return adapter?.capabilities ?? getDefaultRenderEngineCapabilities('fabric'); },
    get events() { return adapter?.events ?? createRenderEngineEvents('fabric'); },
    get history() { return requireAdapter().history; },
    get scene() { return requireAdapter().scene; },
    get objects() { return requireAdapter().objects; },
    get selection() { return requireAdapter().selection; },
    get placement() { return requireAdapter().placement; },
    get viewport() { return requireAdapter().viewport; },
    get exports() { return requireAdapter().exports; },
    get areas() { return requireAdapter().areas; },
    get canvas2d() { return requireAdapter().canvas2d; },
    lifecycle: {
      mount: (container: HTMLElement) => {
        cleanup();
        const created = initDesignerCanvas(container, {
          backgroundColor: String(options?.backgroundColor ?? '#ffffff'),
        });
        designerInstance = created.instance;
        adapter = createFabricRenderEngineAdapter(created.instance, {
          activateView: (viewId, activationOptions) => {
            if (!designerInstance) return;
            if (activationOptions?.mode === 'restore') {
              designerInstance.util?.state?.rebuildActiveViewFromDocument?.(viewId);
              return;
            }
            if (activationOptions?.mode === 'mount') return mountFabricView(designerInstance, viewId);
            return switchViewWithCanvas(designerInstance, viewId);
          },
          deactivateView: (viewId) => {
            if (designerInstance) saveSnapshotForView(designerInstance, viewId);
          },
        });

        const relay = (eventName: string, type: string, action: string) => onAsukaEvent(eventName, (payload) => {
          emitRenderEngineEvent({
            type,
            action,
            id: String(payload?.objectId ?? payload?.id ?? '').trim() || null,
            engine: 'fabric',
            payload,
          });
        });
        eventDisposers = [
          relay('canvas.selection.change', 'selection', 'selection.change'),
          relay('after:canvas.object.update', 'changed', 'object.update'),
          relay('after:canvas.object.remove', 'changed', 'object.remove'),
          relay('after:canvas.object.duplicate', 'changed', 'object.duplicate'),
          relay('drawingZones.updated', 'changed', 'area.update'),
          relay('drawingZones.activeChanged', 'selection', 'area.select'),
          relay('after:canvas.history.undo', 'history', 'history.undo'),
          relay('after:canvas.history.redo', 'history', 'history.redo'),
          relay('after:canvas.history.reset', 'history', 'history.reset'),
          relay('after:state.restore', 'changed', 'state.restore'),
        ];

        resizeObserver = new ResizeObserver(() => {
          if (!designerInstance) return;
          if (resizeRaf) cancelAnimationFrame(resizeRaf);
          resizeRaf = requestAnimationFrame(() => {
            try { designerInstance?.resizeToHost?.(); } catch {}
          });
        });
        try { resizeObserver.observe(container); } catch {}
        try { designerInstance.resizeToHost?.(); } catch {}
        try { (useAsukaStore.getState() as any)?.setDesignerInstance?.(designerInstance); } catch {}
        try { (globalThis as any).asukaDesigner = designerInstance; } catch {}
        emitRenderEngineEvent({ type: 'ready', action: 'mount', engine: 'fabric' });
      },
      unmount: cleanup,
      activateView: (viewId, activationOptions) => requireAdapter().lifecycle.activateView(viewId, activationOptions),
      deactivateView: (viewId) => requireAdapter().lifecycle.deactivateView(viewId),
      loadView: (viewId: string, state: unknown) => requireAdapter().lifecycle.loadView(viewId, state),
      exportView: (viewId: string) => requireAdapter().lifecycle.exportView(viewId),
    },
  };

  return engine;
};

export default createAsukaFabricRenderEngine;
