import esbuild from "esbuild";
import process from "process";
import builtins from "builtin-modules";
import { readFileSync } from 'fs';
import { join } from 'path';
import { polyfillNode } from 'esbuild-plugin-polyfill-node'
import { sassPlugin } from 'esbuild-sass-plugin'

const banner = `/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin
*/
`;

// Plugin to handle WASM files
const wasmPlugin = {
    name: 'wasm',
    setup(build) {
        build.onResolve({ filter: /\.wasm$/ }, args => {
            if (args.resolveDir === '') return;
            return {
                path: join(args.resolveDir, args.path),
                namespace: 'wasm-binary',
            };
        });

        build.onLoad({ filter: /\.wasm$/, namespace: 'wasm-binary' }, async (args) => {
            const contents = readFileSync(args.path);
            const wasmBase64 = contents.toString('base64');
            
            return {
                contents: `
                    const wasmBase64 = "${wasmBase64}";
                    const wasmBinary = Uint8Array.from(atob(wasmBase64), c => c.charCodeAt(0));
                    export default wasmBinary;
                `,
                loader: 'js',
            };
        });
    }
};

// Plugin to inject worker code
const workerPlugin = {
    name: 'worker',
    setup(build) {
        build.onResolve({ filter: /^virtual:worker-code$/ }, args => ({
            path: args.path,
            namespace: 'worker-code',
        }));

        build.onLoad({ filter: /.*/, namespace: 'worker-code' }, async () => {
            // Build worker code
            const result = await esbuild.build({
                entryPoints: ['src/modules/database/worker/database.ts'],
                bundle: true,
                write: false,
                format: 'iife',
                target: 'es2020',
                external: ['fs', 'path'],
                plugins: [wasmPlugin, polyfillNode({
                })],
                minify: process.argv[2] === 'production',
                define: {
                    'process.env.NODE_ENV': JSON.stringify(process.argv[2] === 'production' ? 'production' : 'development'),
                    '__dirname': '"/"'
                }
            });

            return {
                contents: `
                    const workerCode = ${JSON.stringify(result.outputFiles[0].text)};
                    export default workerCode;
                `,
                loader: 'js',
            };
        });
    },
};

const prod = (process.argv[2] === "production");

const context = await esbuild.context({
    banner: {
        js: banner,
    },
    entryPoints: ["src/main.ts"],
    bundle: true,
    external: [
        "obsidian",
        "electron",
        "@codemirror/autocomplete",
        "@codemirror/collab",
        "@codemirror/commands",
        "@codemirror/language",
        "@codemirror/lint",
        "@codemirror/search",
        "@codemirror/state",
        "@codemirror/view",
        "@lezer/common",
        "@lezer/highlight",
        "@lezer/lr",
        ...builtins
    ],
    format: "cjs",
    target: "ES2020",
    logLevel: "info",
    sourcemap: prod ? false : "inline",
    treeShaking: true,
    outfile: "main.js",
    loader: {
        '.svg': 'text'
    },
    plugins: [
        wasmPlugin,
        workerPlugin
    ],
    define: {
        'process.env.NODE_ENV': JSON.stringify(prod ? 'production' : 'development'),
        '__dirname': '"/"'
    }
});

const stylesContext = await esbuild.context({
    entryPoints: ['src/styles/main.scss'],
    outfile: './styles.css',
    bundle: true,
    minify: true,
    sourcemap: true,
    plugins: [sassPlugin({
        sourceMap: true
    })],
    logLevel: 'info'
})


if (prod) {
    await context.rebuild();
    await stylesContext.rebuild();
    process.exit(0);
} else {
    await Promise.all([
        context.watch(),
        stylesContext.watch()
    ])
}