import { extname } from "node:path";
import { getAssetsPrefix } from "../assets/utils/getAssetsPrefix.js";
import { fileExtension } from "../core/path.js";
import { fileURLToPath } from "node:url";
import { isRunnableDevEnvironment } from "vite";
import { AstroError, AstroErrorData } from "../core/errors/index.js";
import { wrapId } from "../core/util.js";
import { isBuildableCSSRequest } from "../vite-plugin-astro-server/util.js";
import { crawlGraph } from "../vite-plugin-astro-server/vite.js";
import {
  CONTENT_IMAGE_FLAG,
  CONTENT_RENDER_FLAG,
  LINKS_PLACEHOLDER,
  PROPAGATED_ASSET_FLAG,
  STYLES_PLACEHOLDER
} from "./consts.js";
import { hasContentFlag } from "./utils.js";
import { joinPaths, prependForwardSlash, slash } from "@astrojs/internal-helpers/path";
import { ASTRO_VITE_ENVIRONMENT_NAMES } from "../core/constants.js";
import { isAstroServerEnvironment } from "../environments.js";
function astroContentAssetPropagationPlugin({
  settings
}) {
  let environment = void 0;
  return {
    name: "astro:content-asset-propagation",
    enforce: "pre",
    resolveId: {
      filter: {
        id: new RegExp(`(?:\\?|&)(?:${CONTENT_IMAGE_FLAG}|${CONTENT_RENDER_FLAG})(?:&|=|$)`)
      },
      async handler(id, importer, opts) {
        if (hasContentFlag(id, CONTENT_IMAGE_FLAG)) {
          const [base, query] = id.split("?");
          const params = new URLSearchParams(query);
          const importerParam = params.get("importer");
          const importerPath = importerParam ? fileURLToPath(new URL(importerParam, settings.config.root)) : importer;
          const resolved = await this.resolve(base, importerPath, { skipSelf: true, ...opts });
          if (!resolved) {
            throw new AstroError({
              ...AstroErrorData.ImageNotFound,
              message: AstroErrorData.ImageNotFound.message(base)
            });
          }
          resolved.id = `${resolved.id}?${CONTENT_IMAGE_FLAG}`;
          return resolved;
        }
        if (hasContentFlag(id, CONTENT_RENDER_FLAG)) {
          const base = id.split("?")[0];
          for (const { extensions, handlePropagation = true } of settings.contentEntryTypes) {
            if (handlePropagation && extensions.includes(extname(base))) {
              return this.resolve(`${base}?${PROPAGATED_ASSET_FLAG}`, importer, {
                skipSelf: true,
                ...opts
              });
            }
          }
          return this.resolve(base, importer, { skipSelf: true, ...opts });
        }
      }
    },
    configureServer(server) {
      const ssrEnv = server.environments[ASTRO_VITE_ENVIRONMENT_NAMES.ssr];
      if (isRunnableDevEnvironment(ssrEnv)) {
        environment = ssrEnv;
      } else if (isRunnableDevEnvironment(server.environments[ASTRO_VITE_ENVIRONMENT_NAMES.astro])) {
        environment = server.environments[ASTRO_VITE_ENVIRONMENT_NAMES.astro];
      }
    },
    transform: {
      filter: {
        id: new RegExp(`(?:\\?|&)${PROPAGATED_ASSET_FLAG}(?:&|=|$)`)
      },
      async handler(_, id) {
        if (hasContentFlag(id, PROPAGATED_ASSET_FLAG)) {
          const basePath = id.split("?")[0];
          let stringifiedLinks, stringifiedStyles;
          if (isAstroServerEnvironment(this.environment) && environment) {
            if (!environment.moduleGraph.getModuleById(basePath)?.ssrModule) {
              await environment.runner.import(basePath).catch(() => {
              });
            }
            const {
              styles,
              urls,
              crawledFiles: styleCrawledFiles
            } = await getStylesForURL(basePath, environment);
            for (const file of styleCrawledFiles) {
              if (!file.includes("node_modules")) {
                this.addWatchFile(file);
              }
            }
            stringifiedLinks = JSON.stringify([...urls]);
            stringifiedStyles = JSON.stringify(styles.map((s) => s.content));
          } else {
            stringifiedLinks = JSON.stringify(LINKS_PLACEHOLDER);
            stringifiedStyles = JSON.stringify(STYLES_PLACEHOLDER);
          }
          const code = `
					async function getMod() {
						return import(${JSON.stringify(basePath)});
					}
					const collectedLinks = ${stringifiedLinks};
					const collectedStyles = ${stringifiedStyles};
					const defaultMod = { __astroPropagation: true, getMod, collectedLinks, collectedStyles, collectedScripts: [] };
					export default defaultMod;
				`;
          return { code, map: { mappings: "" } };
        }
      }
    }
  };
}
const INLINE_QUERY_REGEX = /(?:\?|&)inline(?:$|&)/;
async function getStylesForURL(filePath, environment) {
  const importedCssUrls = /* @__PURE__ */ new Set();
  const importedStylesMap = /* @__PURE__ */ new Map();
  const crawledFiles = /* @__PURE__ */ new Set();
  for await (const importedModule of crawlGraph(environment, filePath, false)) {
    if (importedModule.file) {
      crawledFiles.add(importedModule.file);
    }
    if (isBuildableCSSRequest(importedModule.url)) {
      let css = "";
      if (typeof importedModule.ssrModule?.default === "string") {
        css = importedModule.ssrModule.default;
      } else {
        let modId = importedModule.url;
        if (!INLINE_QUERY_REGEX.test(importedModule.url)) {
          if (importedModule.url.includes("?")) {
            modId = importedModule.url.replace("?", "?inline&");
          } else {
            modId += "?inline";
          }
        }
        try {
          const ssrModule = await environment.runner.import(modId);
          css = ssrModule.default;
        } catch {
          continue;
        }
      }
      importedStylesMap.set(importedModule.url, {
        id: wrapId(importedModule.id ?? importedModule.url),
        url: wrapId(importedModule.url),
        content: css
      });
    }
  }
  return {
    urls: importedCssUrls,
    styles: [...importedStylesMap.values()],
    crawledFiles
  };
}
async function contentAssetsBuildPostHook(base, assetsPrefix, internals, {
  chunks,
  mutate
}) {
  for (const chunk of chunks) {
    if (!chunk.code.includes(LINKS_PLACEHOLDER)) continue;
    const entryStyles = /* @__PURE__ */ new Set();
    const entryLinks = /* @__PURE__ */ new Set();
    for (const id of chunk.moduleIds) {
      const entryCss = internals.propagatedStylesMap.get(id);
      if (entryCss) {
        for (const value of entryCss) {
          if (value.type === "inline") entryStyles.add(value.content);
          if (value.type === "external") {
            let href;
            if (assetsPrefix) {
              const pf = getAssetsPrefix(fileExtension(value.src), assetsPrefix);
              href = joinPaths(pf, slash(value.src));
            } else {
              href = prependForwardSlash(joinPaths(base, slash(value.src)));
            }
            entryLinks.add(href);
          }
        }
      }
    }
    let newCode = chunk.code;
    if (entryStyles.size) {
      newCode = newCode.replace(
        JSON.stringify(STYLES_PLACEHOLDER),
        JSON.stringify(Array.from(entryStyles))
      );
    } else {
      newCode = newCode.replace(JSON.stringify(STYLES_PLACEHOLDER), "[]");
    }
    if (entryLinks.size) {
      newCode = newCode.replace(
        JSON.stringify(LINKS_PLACEHOLDER),
        JSON.stringify(Array.from(entryLinks))
      );
    } else {
      newCode = newCode.replace(JSON.stringify(LINKS_PLACEHOLDER), "[]");
    }
    mutate(chunk.fileName, newCode, chunk.prerender);
  }
}
export {
  astroContentAssetPropagationPlugin,
  contentAssetsBuildPostHook
};
