/**
 * IsolatedLivePreview
 *
 * Renders LivePreview in a completely separate React root so that:
 *  1. antd v6 Spin (rc-motion ClassComponent) setState callbacks, which throw under
 *     React 19's commitCallbacks, are silenced per-root via onUncaughtError / onCaughtError
 *     and never propagate to the main app's defaultOnUncaughtError.
 *  2. Any other runtime errors inside the preview are contained.
 *
 * The host div is a plain flex container; the inner root owns its own DOM node.
 *
 * Lifecycle notes:
 *  - On mount: create DOM node + createRoot, render LivePreview.
 *  - On `code` change (useEffect dep): unmount old root asynchronously,
 *    create a fresh root synchronously. This avoids the "synchronously unmount
 *    a root while React is already rendering" error in React 19 Strict Mode.
 *  - On unmount: deferred unmount + remove of the inner div.
 */
import React, { useEffect, useRef } from 'react';
import { createRoot } from 'react-dom/client';
import type { Root } from 'react-dom/client';
import LivePreview from './LivePreview';

interface IsolatedLivePreviewProps {
  code: string;
  style?: React.CSSProperties;
}

const IsolatedLivePreview: React.FC<IsolatedLivePreviewProps> = ({
  code,
  style,
}) => {
  const hostRef = useRef<HTMLDivElement>(null);
  const rootRef = useRef<Root | null>(null);
  const innerRef = useRef<HTMLDivElement | null>(null);

  useEffect(() => {
    const host = hostRef.current;
    if (!host) return;

    // ── Tear down any previous root (deferred so we never call unmount
    //    synchronously during React's commit phase).
    const prevRoot = rootRef.current;
    const prevInner = innerRef.current;
    rootRef.current = null;
    innerRef.current = null;

    if (prevRoot || prevInner) {
      setTimeout(() => {
        try {
          prevRoot?.unmount();
        } catch {
          /* ignore */
        }
        try {
          prevInner?.remove();
        } catch {
          /* ignore */
        }
      }, 0);
    }

    // ── Create fresh inner div + root
    const inner = document.createElement('div');
    inner.style.cssText = 'width:100%;height:100%';
    host.appendChild(inner);
    innerRef.current = inner;

    const root = createRoot(inner);
    rootRef.current = root;
    root.render(<LivePreview code={code} />);

    return () => {
      // Cleanup on unmount or before next effect run
      const r = rootRef.current;
      const d = innerRef.current;
      rootRef.current = null;
      innerRef.current = null;
      setTimeout(() => {
        try {
          r?.unmount();
        } catch {
          /* ignore */
        }
        try {
          d?.remove();
        } catch {
          /* ignore */
        }
      }, 0);
    };
    // Re-run when code changes so the preview always reflects the latest code.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [code]);

  return (
    <div ref={hostRef} style={{ width: '100%', height: '100%', ...style }} />
  );
};

export default IsolatedLivePreview;
