import { createHash } from "node:crypto";
import { parse, resolve } from "node:path";
import { mkdir, readFile, writeFile } from "node:fs/promises";
import loadLocalCollection from "./loaders/loadLocalCollection.js";
import loadIconifyCollections from "./loaders/loadIconifyCollections.js";
export function createPlugin({ include = {}, iconDir = "src/icons", svgoOptions }, ctx) {
    let collections;
    const { root } = ctx;
    const virtualModuleId = "virtual:astro-icon";
    const resolvedVirtualModuleId = "\0" + virtualModuleId;
    return {
        name: "astro-icon",
        resolveId(id) {
            if (id === virtualModuleId) {
                return resolvedVirtualModuleId;
            }
        },
        async load(id) {
            if (id === resolvedVirtualModuleId) {
                try {
                    if (!collections) {
                        collections = await loadIconifyCollections({ root, include });
                    }
                    const local = await loadLocalCollection(iconDir, svgoOptions);
                    collections["local"] = local;
                    logCollections(collections, { ...ctx, iconDir });
                    await generateIconTypeDefinitions(Object.values(collections), root);
                }
                catch (ex) {
                    // Failed to load the local collection
                }
                return `export default ${JSON.stringify(collections)};\nexport const config = ${JSON.stringify({ include })}`;
            }
        },
        configureServer({ watcher, moduleGraph }) {
            watcher.add(`${iconDir}/**/*.svg`);
            watcher.on("all", async (_, filepath) => {
                const parsedPath = parse(filepath);
                const resolvedIconDir = resolve(root.pathname, iconDir);
                const isSvgFileInIconDir = parsedPath.dir.startsWith(resolvedIconDir) &&
                    parsedPath.ext === ".svg";
                const isAstroConfig = parsedPath.name === "astro.config";
                if (!isSvgFileInIconDir && !isAstroConfig)
                    return;
                console.log(`Local icons changed, reloading`);
                try {
                    if (!collections) {
                        collections = await loadIconifyCollections({ root, include });
                    }
                    const local = await loadLocalCollection(iconDir, svgoOptions);
                    collections["local"] = local;
                    logCollections(collections, { ...ctx, iconDir });
                    await generateIconTypeDefinitions(Object.values(collections), root);
                    moduleGraph.invalidateAll();
                }
                catch (ex) {
                    // Failed to load the local collection
                }
                return `export default ${JSON.stringify(collections)};\nexport const config = ${JSON.stringify({ include })}`;
            });
        },
    };
}
function logCollections(collections, { logger, iconDir }) {
    if (Object.keys(collections).length === 0) {
        logger.warn("No icons detected!");
        return;
    }
    const names = Object.keys(collections).filter((v) => v !== "local");
    if (collections["local"]) {
        names.unshift(iconDir);
    }
    logger.info(`Loaded icons from ${names.join(", ")}`);
}
async function generateIconTypeDefinitions(collections, rootDir, defaultPack = "local") {
    const typeFile = new URL("./.astro/icon.d.ts", rootDir);
    await ensureDir(new URL("./", typeFile));
    const oldHash = await tryGetHash(typeFile);
    const currentHash = collectionsHash(collections);
    if (currentHash === oldHash) {
        return;
    }
    await writeFile(typeFile, `// Automatically generated by astro-icon
// ${currentHash}

declare module 'virtual:astro-icon' {
\texport type Icon = ${collections.length > 0
        ? collections
            .map((collection) => Object.keys(collection.icons)
            .concat(Object.keys(collection.aliases ?? {}))
            .map((icon) => `\n\t\t| "${collection.prefix === defaultPack
            ? ""
            : `${collection.prefix}:`}${icon}"`))
            .flat(1)
            .join("")
        : "never"};
}`);
}
function collectionsHash(collections) {
    const hash = createHash("sha256");
    for (const collection of collections) {
        hash.update(collection.prefix);
        hash.update(Object.keys(collection.icons)
            .concat(Object.keys(collection.aliases ?? {}))
            .sort()
            .join(","));
    }
    return hash.digest("hex");
}
async function tryGetHash(path) {
    try {
        const text = await readFile(path, { encoding: "utf-8" });
        return text.split("\n", 3)[1].replace("// ", "");
    }
    catch { }
}
async function ensureDir(path) {
    try {
        await mkdir(path, { recursive: true });
    }
    catch { }
}
