import { compile } from "../compiler/compile.js";
import fs from "node:fs";
import { resolve, relative } from "node:path";
import { nodeNormalizePath } from "../utilities/node-normalize-path.js";
import { Logger } from "../services/logger/index.js";
const PLUGIN_NAME = "unplugin-paraglide-js";
const logger = new Logger();
/**
 * Default isServer which differs per bundler.
 */
let isServer;
let previousCompilation;
export const unpluginFactory = (args) => ({
    name: PLUGIN_NAME,
    enforce: "pre",
    async buildStart() {
        const isProduction = process.env.NODE_ENV === "production";
        // default to locale-modules for development to speed up the dev server
        // https://github.com/opral/inlang-paraglide-js/issues/486
        const outputStructure = args.outputStructure ??
            (isProduction ? "message-modules" : "locale-modules");
        try {
            previousCompilation = await compile({
                fs: wrappedFs,
                previousCompilation,
                outputStructure,
                // webpack invokes the `buildStart` api in watch mode,
                // to avoid cleaning the output directory in watch mode,
                // we only clean the output directory if there was no previous compilation
                cleanOutdir: previousCompilation === undefined,
                isServer,
                ...args,
            });
            logger.success(`Compilation complete (${outputStructure})`);
        }
        catch (error) {
            logger.error("Failed to compile project:", error.message);
            logger.info("Please check your translation files for syntax errors.");
            if (isProduction)
                throw error;
        }
        finally {
            // in any case add the files to watch
            for (const path of Array.from(readFiles)) {
                this.addWatchFile(path);
            }
        }
    },
    async watchChange(path) {
        const shouldCompile = readFiles.has(path) && !path.includes("cache");
        if (shouldCompile === false) {
            return;
        }
        const isProduction = process.env.NODE_ENV === "production";
        // default to locale-modules for development to speed up the dev server
        // https://github.com/opral/inlang-paraglide-js/issues/486
        const outputStructure = args.outputStructure ??
            (isProduction ? "message-modules" : "locale-modules");
        const previouslyReadFiles = new Set(readFiles);
        try {
            logger.info(`Re-compiling inlang project... File "${relative(process.cwd(), path)}" has changed.`);
            // Clear readFiles to track fresh file reads
            readFiles.clear();
            previousCompilation = await compile({
                fs: wrappedFs,
                previousCompilation,
                outputStructure,
                cleanOutdir: false,
                isServer,
                ...args,
            });
            logger.success(`Re-compilation complete (${outputStructure})`);
            // Add any new files to watch
            for (const filePath of Array.from(readFiles)) {
                this.addWatchFile(filePath);
            }
        }
        catch (e) {
            readFiles = previouslyReadFiles;
            // Reset compilation result on error
            previousCompilation = undefined;
            logger.warn("Failed to re-compile project:", e.message);
        }
    },
    vite: {
        config: {
            handler: () => {
                isServer = "import.meta.env?.SSR ?? typeof window === 'undefined'";
            },
        },
    },
    webpack(compiler) {
        compiler.options.resolve = {
            ...compiler.options.resolve,
            fallback: {
                ...compiler.options.resolve?.fallback,
                // https://stackoverflow.com/a/72989932
                async_hooks: false,
            },
        };
        compiler.hooks.beforeRun.tapPromise(PLUGIN_NAME, async () => {
            const isProduction = process.env.NODE_ENV === "production";
            // default to locale-modules for development to speed up the dev server
            // https://github.com/opral/inlang-paraglide-js/issues/486
            const outputStructure = args.outputStructure ??
                (isProduction ? "message-modules" : "locale-modules");
            try {
                previousCompilation = await compile({
                    fs: wrappedFs,
                    previousCompilation,
                    outputStructure,
                    // clean dir needs to be false. otherwise webpack get's into a race condition
                    // of deleting the output directory and writing files at the same time for
                    // multi environment builds
                    cleanOutdir: false,
                    ...args,
                });
                logger.success(`Compilation complete (${outputStructure})`);
            }
            catch (error) {
                logger.warn("Failed to compile project:", error.message);
                logger.warn("Please check your translation files for syntax errors.");
                if (isProduction)
                    throw error;
            }
        });
    },
});
let readFiles = new Set();
// Create a wrapper around the fs object to intercept and store read files
const wrappedFs = {
    ...fs,
    // @ts-expect-error - Node's fs has too many overloads
    readFile: (path, options, callback) => {
        readFiles.add(nodeNormalizePath(resolve(process.cwd(), path.toString())));
        return fs.readFile(path, options, callback);
    },
    // @ts-expect-error - Node's fs has too many overloads
    readFileSync: (path, options) => {
        readFiles.add(nodeNormalizePath(resolve(process.cwd(), path.toString())));
        return fs.readFileSync(path, options);
    },
    promises: {
        ...fs.promises,
        // @ts-expect-error - Node's fs.promises has too many overloads
        readFile: async (path, options) => {
            readFiles.add(nodeNormalizePath(resolve(process.cwd(), path.toString())));
            return fs.promises.readFile(path, options);
        },
    },
    // Add other fs methods as needed
};
