import { fileURLToPath } from "node:url";
import { Pipeline } from "../core/base-pipeline.js";
import { ASTRO_VERSION } from "../core/constants.js";
import { enhanceViteSSRError } from "../core/errors/dev/index.js";
import { AggregateError, CSSError, MarkdownError } from "../core/errors/index.js";
import { RedirectComponentInstance } from "../core/redirects/index.js";
import { loadRenderer } from "../core/render/index.js";
import { routeIsRedirect } from "../core/routing/helpers.js";
import { findRouteToRewrite } from "../core/routing/rewrite.js";
import { isPage } from "../core/util.js";
import { getComponentMetadata } from "../vite-plugin-astro-server/metadata.js";
import { createResolve } from "../vite-plugin-astro-server/resolve.js";
import { PAGE_SCRIPT_ID } from "../vite-plugin-scripts/index.js";
import { newNodePool } from "../runtime/server/render/queue/pool.js";
import { HTMLStringCache } from "../runtime/server/html-string-cache.js";
import { queueRenderingEnabled } from "../core/app/manifest.js";
class RunnablePipeline extends Pipeline {
  getName() {
    return "RunnablePipeline";
  }
  // renderers are loaded on every request,
  // so it needs to be mutable here unlike in other environments
  renderers = new Array();
  routesList;
  loader;
  settings;
  getDebugInfo;
  constructor(loader, logger, manifest, settings, getDebugInfo, defaultRoutes) {
    const resolve = createResolve(loader, manifest.rootDir);
    const streaming = true;
    super(
      logger,
      manifest,
      "development",
      [],
      resolve,
      streaming,
      void 0,
      void 0,
      void 0,
      void 0,
      void 0,
      void 0,
      void 0,
      void 0,
      defaultRoutes
    );
    this.loader = loader;
    this.settings = settings;
    this.getDebugInfo = getDebugInfo;
  }
  static create(manifestData, {
    loader,
    logger,
    manifest,
    settings,
    getDebugInfo
  }) {
    const pipeline = new RunnablePipeline(loader, logger, manifest, settings, getDebugInfo);
    pipeline.routesList = manifestData;
    if (queueRenderingEnabled(manifest.experimentalQueuedRendering)) {
      pipeline.nodePool = newNodePool(manifest.experimentalQueuedRendering);
      if (manifest.experimentalQueuedRendering.contentCache) {
        pipeline.htmlStringCache = new HTMLStringCache(1e3);
      }
    }
    return pipeline;
  }
  async headElements(routeData) {
    const { manifest, runtimeMode, settings } = this;
    const filePath = new URL(`${routeData.component}`, manifest.rootDir);
    const scripts = /* @__PURE__ */ new Set();
    if (settings) {
      if (isPage(filePath, settings) && runtimeMode === "development") {
        scripts.add({
          props: { type: "module", src: "/@vite/client" },
          children: ""
        });
        if (this.manifest.devToolbar.enabled) {
          scripts.add({
            props: {
              type: "module",
              src: "/@id/astro/runtime/client/dev-toolbar/entrypoint.js"
            },
            children: ""
          });
          const additionalMetadata = {
            root: fileURLToPath(settings.config.root),
            version: ASTRO_VERSION,
            latestAstroVersion: settings.latestAstroVersion,
            // TODO: Currently the debug info is always fetched, which slows things down.
            // We should look into not loading it if the dev toolbar is disabled. And when
            // enabled, it would nice to request the debug info through import.meta.hot
            // when the button is click to defer execution as much as possible
            debugInfo: await this.getDebugInfo(),
            placement: settings.config.devToolbar.placement
          };
          const children = `window.__astro_dev_toolbar__ = ${JSON.stringify(additionalMetadata)}`;
          scripts.add({ props: {}, children });
        }
      }
      for (const script of settings.scripts) {
        if (script.stage === "head-inline") {
          scripts.add({
            props: {},
            children: script.content
          });
        } else if (script.stage === "page" && isPage(filePath, settings)) {
          scripts.add({
            props: { type: "module", src: `/@id/${PAGE_SCRIPT_ID}` },
            children: ""
          });
        }
      }
    }
    const { devCSSMap } = await import("virtual:astro:dev-css-all");
    const importer = devCSSMap.get(routeData.component);
    let css = /* @__PURE__ */ new Set();
    if (importer) {
      const cssModule = await importer();
      css = cssModule.css;
    } else {
      this.logger.warn(
        "assets",
        `Unable to find CSS for ${routeData.component}. This is likely a bug in Astro.`
      );
    }
    const links = /* @__PURE__ */ new Set();
    const styles = /* @__PURE__ */ new Set();
    for (const { id, url: src, content } of css) {
      scripts.add({ props: { type: "module", src }, children: "" });
      styles.add({ props: { "data-vite-dev-id": id }, children: content });
    }
    return { scripts, styles, links };
  }
  componentMetadata(routeData) {
    const filePath = new URL(`${routeData.component}`, this.manifest.rootDir);
    return getComponentMetadata(filePath, this.loader);
  }
  async preload(routeData, filePath) {
    if (routeIsRedirect(routeData)) {
      return RedirectComponentInstance;
    }
    const { loader } = this;
    for (const route of this.defaultRoutes) {
      if (route.matchesComponent(filePath)) {
        return route.instance;
      }
    }
    if (this.settings) {
      const renderers__ = this.settings.renderers.map((r) => loadRenderer(r, loader));
      const renderers_ = await Promise.all(renderers__);
      this.renderers = renderers_.filter((r) => Boolean(r));
    }
    try {
      return await loader.import(filePath.toString());
    } catch (error) {
      if (MarkdownError.is(error) || CSSError.is(error) || AggregateError.is(error)) {
        throw error;
      }
      throw enhanceViteSSRError({ error, filePath, loader });
    }
  }
  clearRouteCache() {
    this.routeCache.clearAll();
  }
  async getComponentByRoute(routeData) {
    const filePath = new URL(`${routeData.component}`, this.manifest.rootDir);
    return await this.preload(routeData, filePath);
  }
  async tryRewrite(payload, request) {
    if (!this.routesList) {
      throw new Error("Missing manifest data. This is an internal error, please file an issue.");
    }
    const { routeData, pathname, newUrl } = findRouteToRewrite({
      payload,
      request,
      routes: this.routesList?.routes,
      trailingSlash: this.manifest.trailingSlash,
      buildFormat: this.manifest.buildFormat,
      base: this.manifest.base,
      outDir: this.manifest.outDir
    });
    const componentInstance = await this.getComponentByRoute(routeData);
    return { newUrl, pathname, componentInstance, routeData };
  }
  setManifestData(manifestData) {
    this.routesList = manifestData;
  }
}
export {
  RunnablePipeline
};
