import {
  createFabricRenderEngineAdapter,
  createUnavailableRenderEngine,
  getCurrentRenderEngineKind,
  getRuntimeRenderEngine,
  normalizeRenderEngineKind,
  setRuntimeRenderEngine,
  type AsukaRenderEngine,
  type AsukaRenderEngineFactory,
  type AsukaRenderEngineKind,
  type AsukaRenderEngineOptions,
  type AsukaRenderEngineOptionsByKind,
} from './renderEngine';
import { assertAsukaRendererContract, inspectAsukaRendererContract } from './rendererContract';
import { mountFabricView, saveSnapshotForView, switchViewWithCanvas } from './viewSnapshots';
import { useAsukaStore } from '../store/asukaStore';

export interface AsukaRenderEngineResolveContext {
  viewId?: string | null;
  view?: unknown;
  kind?: AsukaRenderEngineKind;
}

export interface MountedBuiltInFabricEngine {
  instance: any;
  engine: AsukaRenderEngine;
  unmount: () => void;
}

export const isAsukaRenderEngine = (value: unknown): value is AsukaRenderEngine => {
  return inspectAsukaRendererContract(value).ok;
};

export const getRenderEngineOptionsForKind = (
  kind: AsukaRenderEngineKind,
  optionsByKind?: AsukaRenderEngineOptionsByKind | null,
): AsukaRenderEngineOptions | null => {
  const normalized = normalizeRenderEngineKind(kind, 'fabric');
  const map = optionsByKind && typeof optionsByKind === 'object' ? optionsByKind : {};
  return (map[normalized] ?? null) as AsukaRenderEngineOptions | null;
};

export const resolveLoadedRenderEngine = async (
  loaded: unknown,
  options: unknown,
  context: AsukaRenderEngineResolveContext,
): Promise<AsukaRenderEngine | null> => {
  const module = loaded as any;
  const kind = normalizeRenderEngineKind(context.kind, 'fabric');
  const candidates = [
    module,
    module?.default,
    module?.createAsukaRenderEngine,
    kind === 'fabric' ? module?.createAsukaFabricRenderEngine : null,
    kind === 'fabric' ? module?.createAsuka2DRenderEngine : null,
    kind === '3d' ? module?.createAsuka3DEngine : null,
    kind === '3d' ? module?.createAsukaBabylonRenderEngine : null,
  ].filter(Boolean);

  for (const candidate of candidates) {
    if (isAsukaRenderEngine(candidate)) return candidate;
    if (typeof candidate !== 'function') continue;
    try {
      const created = await (candidate as AsukaRenderEngineFactory)(options as any, context);
      if (isAsukaRenderEngine(created)) return created;
    } catch {}
  }

  return null;
};

/**
 * Stable module-facing router. It contains no compatibility methods: each
 * service getter resolves the currently mounted implementation for the target
 * view kind.
 */
export const createLiveModuleRenderEngine = (
  forcedKind?: AsukaRenderEngineKind | null,
): AsukaRenderEngine => {
  const resolveKind = () => normalizeRenderEngineKind(
    forcedKind ?? getCurrentRenderEngineKind(),
    'fabric',
  );
  const resolve = (): AsukaRenderEngine => (
    getRuntimeRenderEngine(resolveKind())
    ?? createUnavailableRenderEngine(resolveKind())
  );

  return {
    get kind() { return resolveKind(); },
    get capabilities() { return resolve().capabilities; },
    get lifecycle() { return resolve().lifecycle; },
    get events() { return resolve().events; },
    get history() { return resolve().history; },
    get scene() { return resolve().scene; },
    get objects() { return resolve().objects; },
    get selection() { return resolve().selection; },
    get placement() { return resolve().placement; },
    get viewport() { return resolve().viewport; },
    get exports() { return resolve().exports; },
    get areas() { return resolve().areas; },
    get products() { return resolve().products; },
    get assembly() { return resolve().assembly; },
    get models() { return resolve().models; },
    get parts() { return resolve().parts; },
    get materials() { return resolve().materials; },
    get surface() { return resolve().surface; },
    get canvas2d() { return resolve().canvas2d; },
  };
};

export const mountBuiltInFabricRenderEngine = async (
  container: HTMLElement,
  options?: { backgroundColor?: string },
): Promise<MountedBuiltInFabricEngine> => {
  const { initDesignerCanvas } = await import('./designer');
  const created = initDesignerCanvas(container, {
    backgroundColor: String(options?.backgroundColor ?? '#ffffff'),
  });
  const instance = created.instance;
  const engine = createFabricRenderEngineAdapter(instance, {
    activateView: (viewId, activationOptions) => {
      if (activationOptions?.mode === 'restore') {
        instance?.util?.state?.rebuildActiveViewFromDocument?.(viewId);
        return;
      }
      if (activationOptions?.mode === 'mount') return mountFabricView(instance, viewId);
      return switchViewWithCanvas(instance, viewId);
    },
    deactivateView: (viewId) => saveSnapshotForView(instance, viewId),
  });
  let resizeObserver: ResizeObserver | null = null;
  let resizeRaf = 0;

  resizeObserver = new ResizeObserver(() => {
    if (resizeRaf) cancelAnimationFrame(resizeRaf);
    resizeRaf = requestAnimationFrame(() => {
      try { instance?.resizeToHost?.(); } catch {}
    });
  });
  try { resizeObserver.observe(container); } catch {}
  try { instance?.resizeToHost?.(); } catch {}
  try { useAsukaStore.getState()?.setDesignerInstance?.(instance); } catch {}
  try { (globalThis as any).asukaDesigner = instance; } catch {}

  return {
    instance,
    engine,
    unmount: () => {
      try { resizeObserver?.disconnect?.(); } catch {}
      resizeObserver = null;
      if (resizeRaf) {
        try { cancelAnimationFrame(resizeRaf); } catch {}
        resizeRaf = 0;
      }
      try {
        const state = useAsukaStore.getState() as any;
        if (state?.designerInstance === instance) state?.setDesignerInstance?.(null);
      } catch {}
      try { instance?.dispose?.(); } catch {}
    },
  };
};

export const loadRenderEngineFromOptions = async (
  kind: AsukaRenderEngineKind,
  options: any,
  context: AsukaRenderEngineResolveContext,
): Promise<AsukaRenderEngine | null> => {
  const loader = typeof options?.loader === 'function' ? options.loader : null;
  if (!loader) return null;
  const loaded = await loader();
  return resolveLoadedRenderEngine(loaded, options, { ...context, kind });
};

export const registerMountedRuntimeEngine = (
  kind: AsukaRenderEngineKind,
  engine: AsukaRenderEngine | null | undefined,
): void => {
  if (engine) assertAsukaRendererContract(engine);
  setRuntimeRenderEngine(normalizeRenderEngineKind(kind, 'fabric'), engine ?? null);
};
