/** Local sandbox host. Ready is a mount observation, never a verification result. */
import React, { Component, useEffect } from 'react';
import { createRoot } from 'react-dom/client';

declare global {
  interface Window {
    __STYLEGALLERY_PREVIEW__: { id: string; status: string; errors: string[] };
  }
}

export function mount(Demo: React.ComponentType, id: string) {
  const state = window.__STYLEGALLERY_PREVIEW__ = { id, status: 'loading', errors: [] as string[] };
  const send = (message: object) => parent.postMessage({ ...message, id }, '*');
  const report = (error: unknown) => {
    const message = error instanceof Error ? error.message : String(error);
    if (!state.errors.includes(message)) state.errors.push(message);
    state.status = 'error';
    document.body.dataset.previewStatus = 'error';
    send({ type: 'sg-preview-error', message });
  };
  window.addEventListener('error', (event) => report(event.error ?? event.message));
  window.addEventListener('unhandledrejection', (event) => report(event.reason));
  window.addEventListener('securitypolicyviolation', (event) => report(`Blocked by preview policy: ${event.violatedDirective}`));
  const theme = new URLSearchParams(location.search).get('theme') === 'dark' ? 'dark' : 'light';
  document.documentElement.classList.toggle('dark', theme === 'dark');
  document.documentElement.dataset.theme = theme;
  // Keep original source bytes while ensuring imported attribution/navigation remains local text.
  const removeDestinations = () => document.querySelectorAll('a[href]').forEach((link) => {
    link.removeAttribute('href');
    link.removeAttribute('target');
  });
  document.addEventListener('click', (event) => {
    if ((event.target as Element)?.closest?.('a')) event.preventDefault();
  }, true);
  document.addEventListener('submit', (event) => event.preventDefault());
  new MutationObserver(removeDestinations).observe(document.getElementById('root')!, { childList: true, subtree: true });
  const observe = () => {
    const root = document.getElementById('root')!;
    const elements = Array.from(root.querySelectorAll('*'));
    const rect = root.getBoundingClientRect();
    const diagnostics = {
      textLength: (root.textContent ?? '').trim().length,
      elementCount: elements.length,
      visibleElementCount: elements.filter((element) => {
        const bounds = element.getBoundingClientRect();
        const style = getComputedStyle(element);
        return bounds.width > 0 && bounds.height > 0 && style.display !== 'none' && style.visibility !== 'hidden' && Number(style.opacity) !== 0;
      }).length,
      images: [],
      canvases: [],
      rootRect: { width: rect.width, height: rect.height },
      documentWidth: document.documentElement.scrollWidth,
      viewportWidth: innerWidth,
    };
    send({ type: 'sg-preview-observation', diagnostics });
  };
  class Boundary extends Component<{ children: React.ReactNode }, { error: string | null }> {
    state = { error: null as string | null };
    static getDerivedStateFromError(error: Error) { return { error: error.message }; }
    componentDidCatch(error: Error) { report(error); }
    render() { return this.state.error ? <p role="alert">This preview could not render: {this.state.error}</p> : this.props.children; }
  }
  function Ready() {
    useEffect(() => {
      const preference = matchMedia('(prefers-reduced-motion: reduce)');
      const syncSVG = () => document.querySelectorAll('svg').forEach((svg) => {
        if (preference.matches) svg.pauseAnimations?.();
        else svg.unpauseAnimations?.();
      });
      syncSVG();
      preference.addEventListener('change', syncSVG);
      let second = 0;
      let delayed = 0;
      const first = requestAnimationFrame(() => { second = requestAnimationFrame(() => {
        if (state.status !== 'error') {
          state.status = 'mounted';
          document.body.dataset.previewStatus = 'mounted';
          send({ type: 'sg-preview-ready' });
          observe();
          delayed = window.setTimeout(observe, 700);
        }
      }); });
      return () => {
        cancelAnimationFrame(first);
        cancelAnimationFrame(second);
        clearTimeout(delayed);
        preference.removeEventListener('change', syncSVG);
      };
    }, []);
    return <Demo />;
  }
  createRoot(document.getElementById('root')!).render(<Boundary><Ready /></Boundary>);
}
