import { useAsukaStore } from '../store/asukaStore';
import { getPriceUiBreakdown } from './pricingEngine';
import { getRuntimeRenderEngine, subscribeRenderEngineEvents } from './renderEngine';

/**
 * PricingController
 *
 * Goal: keep a UI-friendly pricing breakdown in Zustand and refresh it whenever
 * the canvas or pricing-related settings change.
 *
 * This is intentionally small and framework-agnostic: it only relies on the
 * DesignerInstance `onChange()` hook and Zustand subscriptions.
 */

export type PricingControllerStop = () => void;

export function startPricingController(): PricingControllerStop {
  const st = useAsukaStore;

  let detachDesignerListener: (() => void) | null = null;
  let rafId: number | null = null;
  let stopped = false;

  const scheduleRecalc = () => {
    if (stopped) return;
    if (rafId != null) return;
    rafId = window.requestAnimationFrame(() => {
      rafId = null;
      if (stopped) return;
      try {
        const s = st.getState() as any;
        const designer = s.designerInstance;
        const kind = String(s.activeRenderEngineKind ?? '').toLowerCase() === '3d' ? '3d' : 'fabric';
        const breakdown = getPriceUiBreakdown({ designer, engine: getRuntimeRenderEngine(kind) });
        s.setPricingTotals?.(breakdown);
      } catch {
        // ignore (ex: designer not ready yet)
      }
    });
  };

  const bindDesigner = (designer: any | null) => {
    // cleanup previous
    if (detachDesignerListener) {
      try {
        detachDesignerListener();
      } catch {
        // ignore
      }
      detachDesignerListener = null;
    }

    if (!designer || typeof designer.onChange !== 'function') {
      return;
    }

    detachDesignerListener = designer.onChange(() => {
      scheduleRecalc();
    });
  };

  // NOTE: this store isn't using `subscribeWithSelector` middleware.
  // We therefore implement tiny selector subscriptions ourselves to keep
  // production builds type-safe and stable.

  let prevDesigner = (st.getState() as any).designerInstance;
  bindDesigner(prevDesigner);

  const unsubDesigner = st.subscribe((s) => {
    const next = (s as any).designerInstance;
    if (next === prevDesigner) return;
    prevDesigner = next;
    bindDesigner(next as any);
    scheduleRecalc();
  });

  let prevInputsJson = JSON.stringify({
    price: (st.getState() as any).price,
    rules: (st.getState() as any).pricingRules,
    colors: (st.getState() as any).colors,
    printingMethods: (st.getState() as any).printingMethods,
  });

  const unsubInputs = st.subscribe((s) => {
    const ss: any = s;
    const nextJson = JSON.stringify({
      price: ss.price,
      rules: ss.pricingRules,
      colors: ss.colors,
      printingMethods: ss.printingMethods,
    });
    if (nextJson === prevInputsJson) return;
    prevInputsJson = nextJson;
    scheduleRecalc();
  });

  let previousDocumentState = {
    activeViewId: (st.getState() as any).activeViewId,
    activeRenderEngineKind: (st.getState() as any).activeRenderEngineKind,
    views: (st.getState() as any).views,
    objectsByView: (st.getState() as any).documentObjectsByView,
    scenesByView: (st.getState() as any).document3dScenesByView,
  };
  const unsubDocument = st.subscribe((s) => {
    const next = {
      activeViewId: (s as any).activeViewId,
      activeRenderEngineKind: (s as any).activeRenderEngineKind,
      views: (s as any).views,
      objectsByView: (s as any).documentObjectsByView,
      scenesByView: (s as any).document3dScenesByView,
    };
    if (next.activeViewId === previousDocumentState.activeViewId
      && next.activeRenderEngineKind === previousDocumentState.activeRenderEngineKind
      && next.views === previousDocumentState.views
      && next.objectsByView === previousDocumentState.objectsByView
      && next.scenesByView === previousDocumentState.scenesByView) return;
    previousDocumentState = next;
    scheduleRecalc();
  });

  const unsubRendererEvents = subscribeRenderEngineEvents((event) => {
    if (event.type === 'changed' || event.type === 'history' || event.type === 'ready') scheduleRecalc();
  });

  // initial computation
  scheduleRecalc();

  return () => {
    stopped = true;
    try {
      unsubDesigner();
    } catch {
      // ignore
    }
    try {
      unsubInputs();
    } catch {
      // ignore
    }
    try { unsubDocument(); } catch {}
    try { unsubRendererEvents(); } catch {}
    try {
      detachDesignerListener?.();
    } catch {
      // ignore
    }
    if (rafId != null) {
      try {
        window.cancelAnimationFrame(rafId);
      } catch {
        // ignore
      }
      rafId = null;
    }
  };
}
