// route-loader.jsx — prototype-only route code splitting boundary.
//
// This keeps heavy game screens out of the initial mobile payload. During the
// Next.js 16 migration, keep PROTOTYPE_ROUTE_MODULES as the route manifest and
// replace loadPrototypeRouteModule with next/dynamic imports. Capacitor can use
// the same screen boundaries without coupling game code to the delivery shell.

const PROTOTYPE_ROUTE_MODULES = Object.freeze({
  tracking: Object.freeze({
    source: null,
    compiledSource: null,
    dependencies: ['vendor/leaflet.js'],
    exportName: 'TrackingScreen',
    label: 'Live tracking',
  }),
  'lucky-strike': Object.freeze({
    source: 'screens-luckystrike.jsx',
    compiledSource: 'app/chunks/lucky-strike.js',
    dependencies: ['vendor/gsap.min.js'],
    exportName: 'LuckyStrikePage',
    label: 'Lucky Strike',
  }),
  showdowns: Object.freeze({
    source: 'screens-showdowns.jsx',
    compiledSource: 'app/chunks/shootout.js',
    dependencies: [],
    exportName: 'ShowdownsScreen',
    label: 'Shootout',
  }),
});

const prototypeRouteModulePromises = new Map();
const prototypeExternalScriptPromises = new Map();
const prototypeRouteLoaderUrl = (() => {
  const scripts = Array.from(document.scripts);
  const loaderScript = scripts.find((script) => /(?:^|\/)route-loader\.jsx(?:[?#].*)?$/.test(script.src));
  return loaderScript?.src || new URL('app/route-loader.jsx', document.baseURI).href;
})();

function loadPrototypeRouteModule(routeName) {
  const route = PROTOTYPE_ROUTE_MODULES[routeName];
  if (!route) {
    return Promise.reject(new Error(`Unknown prototype route: ${routeName}`));
  }

  if (prototypeRouteModulePromises.has(routeName)) {
    return prototypeRouteModulePromises.get(routeName);
  }

  const isProductionBuild = window.__LASSO_PRODUCTION_BUILD__ === true;
  const productionChunks = window.__LASSO_PRODUCTION_CHUNKS__ || {};
  const routeSource = isProductionBuild
    ? productionChunks[routeName] || route.compiledSource
    : route.source;
  const sourceUrl = routeSource
    ? new URL(routeSource, isProductionBuild ? document.baseURI : prototypeRouteLoaderUrl).href
    : null;
  const dependencies = route.dependencies || [];

  const modulePromise = Promise.all(dependencies.map(loadPrototypeExternalScript))
    .then(() => {
      if (typeof window[route.exportName] === 'function') {
        return window[route.exportName];
      }
      if (!sourceUrl) {
        throw new Error(`${route.label} has no loadable source`);
      }
      if (isProductionBuild) {
        return loadPrototypeClassicScript(sourceUrl, routeName, route);
      }
      return loadPrototypeJsxSource(sourceUrl, routeName, route);
    })
    .catch((error) => {
      prototypeRouteModulePromises.delete(routeName);
      throw error;
    });

  prototypeRouteModulePromises.set(routeName, modulePromise);
  return modulePromise;
}

function loadPrototypeExternalScript(source) {
  const sourceUrl = new URL(source, document.baseURI).href;
  if (prototypeExternalScriptPromises.has(sourceUrl)) {
    return prototypeExternalScriptPromises.get(sourceUrl);
  }

  const dependencyPromise = new Promise((resolve, reject) => {
    const script = document.createElement('script');
    script.src = sourceUrl;
    script.async = true;
    script.dataset.prototypeDependency = source;
    script.onload = () => resolve(sourceUrl);
    script.onerror = () => reject(new Error(`Dependency failed to load: ${source}`));
    document.head.appendChild(script);
  }).catch((error) => {
    prototypeExternalScriptPromises.delete(sourceUrl);
    throw error;
  });

  prototypeExternalScriptPromises.set(sourceUrl, dependencyPromise);
  return dependencyPromise;
}

function loadPrototypeClassicScript(sourceUrl, routeName, route) {
  return new Promise((resolve, reject) => {
    const script = document.createElement('script');
    script.src = sourceUrl;
    script.async = true;
    script.dataset.prototypeRoute = routeName;
    script.onload = () => {
      const Screen = window[route.exportName];
      if (typeof Screen !== 'function') {
        reject(new Error(`${route.label} loaded without exporting ${route.exportName}`));
        return;
      }
      resolve(Screen);
    };
    script.onerror = () => reject(new Error(`${route.label} could not be loaded`));
    document.head.appendChild(script);
  });
}

function loadPrototypeJsxSource(sourceUrl, routeName, route) {
  return fetch(sourceUrl, { credentials: 'same-origin' })
    .then((response) => {
      if (!response.ok) {
        throw new Error(`${route.label} failed to load (${response.status})`);
      }
      return response.text();
    })
    .then((source) => {
      const transformed = Babel.transform(source, {
        presets: ['react'],
        filename: route.source,
        sourceMaps: 'inline',
      }).code;

      return new Promise((resolve, reject) => {
        const blobUrl = URL.createObjectURL(new Blob(
          [`${transformed}\n//# sourceURL=${sourceUrl}`],
          { type: 'text/javascript' },
        ));
        const script = document.createElement('script');
        script.src = blobUrl;
        script.async = true;
        script.dataset.prototypeRoute = routeName;
        script.onload = () => {
          URL.revokeObjectURL(blobUrl);
          script.remove();
          const Screen = window[route.exportName];
          if (typeof Screen !== 'function') {
            reject(new Error(`${route.label} loaded without exporting ${route.exportName}`));
            return;
          }
          resolve(Screen);
        };
        script.onerror = () => {
          URL.revokeObjectURL(blobUrl);
          script.remove();
          reject(new Error(`${route.label} could not be evaluated`));
        };
        document.head.appendChild(script);
      });
    });
}

function preloadPrototypeRoute(routeName) {
  return loadPrototypeRouteModule(routeName).catch(() => null);
}

function PrototypeRouteBoundary({ routeName, app }) {
  const route = PROTOTYPE_ROUTE_MODULES[routeName];
  const [loadState, setLoadState] = React.useState(() => (
    typeof window[route?.exportName] === 'function'
      ? { status: 'ready', Screen: window[route.exportName], error: null }
      : { status: 'loading', Screen: null, error: null }
  ));
  const [attempt, setAttempt] = React.useState(0);

  React.useEffect(() => {
    let active = true;
    setLoadState({ status: 'loading', Screen: null, error: null });

    loadPrototypeRouteModule(routeName)
      .then((Screen) => {
        if (active) setLoadState({ status: 'ready', Screen, error: null });
      })
      .catch((error) => {
        if (active) setLoadState({ status: 'error', Screen: null, error });
      });

    return () => {
      active = false;
    };
  }, [routeName, attempt]);

  if (!route) {
    return <PrototypeRouteError label="This screen" onBack={app.back}/>;
  }

  if (loadState.status === 'error') {
    return (
      <PrototypeRouteError
        label={route.label}
        message={loadState.error?.message}
        onBack={app.back}
        onRetry={() => setAttempt((value) => value + 1)}
      />
    );
  }

  if (loadState.status !== 'ready') {
    return <PrototypeRouteLoading label={route.label}/>;
  }

  const Screen = loadState.Screen;
  return <Screen app={app}/>;
}

function PrototypeRouteLoading({ label }) {
  return (
    <main
      aria-busy="true"
      aria-label={`Loading ${label}`}
      style={{
        flex: 1,
        display: 'grid',
        placeItems: 'center',
        minHeight: 0,
        background: 'var(--bg-app)',
        color: 'var(--fg-primary)',
      }}
    >
      <div style={{ textAlign: 'center', padding: 24 }}>
        <div style={{ fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 20 }}>{label}</div>
        <div style={{ marginTop: 8, fontFamily: 'var(--font-body)', fontSize: 12, color: 'var(--fg-secondary)' }}>Loading experience…</div>
      </div>
    </main>
  );
}

function PrototypeRouteError({ label, message, onBack, onRetry }) {
  return (
    <main
      role="alert"
      style={{
        flex: 1,
        display: 'grid',
        placeItems: 'center',
        minHeight: 0,
        background: 'var(--bg-app)',
        color: 'var(--fg-primary)',
      }}
    >
      <div style={{ width: 'min(320px, calc(100% - 40px))', textAlign: 'center' }}>
        <div style={{ fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 20 }}>{label} is unavailable</div>
        <div style={{ marginTop: 8, fontFamily: 'var(--font-body)', fontSize: 12, lineHeight: 1.5, color: 'var(--fg-secondary)' }}>
          {message || 'The experience could not be loaded.'}
        </div>
        <div style={{ display: 'flex', justifyContent: 'center', gap: 10, marginTop: 18 }}>
          {onRetry && <Button onClick={onRetry}>Try again</Button>}
          <Button secondary onClick={onBack}>Back</Button>
        </div>
      </div>
    </main>
  );
}

Object.assign(window, {
  PROTOTYPE_ROUTE_MODULES,
  PrototypeRouteBoundary,
  loadPrototypeRouteModule,
  preloadPrototypeRoute,
});
