import * as cp from 'node:child_process';
import * as path from 'node:path';
import * as fs from 'node:fs/promises';
import { readFileSync } from 'node:fs';

import babel from '@rollup/plugin-babel';
import nodeResolve from '@rollup/plugin-node-resolve';
import commonJs from '@rollup/plugin-commonjs';
import terser from '@rollup/plugin-terser';
import replace from '@rollup/plugin-replace';
import alias from '@rollup/plugin-alias';
import virtual from '@rollup/plugin-virtual';

export const packageFolder = path.join(__dirname, '..');
export const pkg = JSON.parse(readFileSync(path.resolve(packageFolder, 'package.json')).toString());

const sha1 = String(cp.execSync('git rev-parse HEAD')).split('\n')[0];
const headerVersion = pkg.version + '(' + sha1 + ')';
export const banner = [
    '/*!-----------------------------------------------------------------------------',
    ' * Copyright (c) Microsoft Corporation. All rights reserved.',
    ' * monaco-kusto version: ' + headerVersion,
    ' * Released under the MIT license',
    ' * https://https://github.com/Azure/monaco-kusto/blob/master/README.md',
    ' *-----------------------------------------------------------------------------*/',
    '',
].join('\n');

const languageServiceFiles = [
    ['@kusto/language-service/bridge.min', 'bridge.min'],
    ['@kusto/language-service/Kusto.JavaScript.Client.min', 'kusto.javascript.client.min'],
    ['@kusto/language-service/newtonsoft.json.min', 'newtonsoft.json.min'],
    ['@kusto/language-service-next/Kusto.Language.Bridge.min', 'Kusto.Language.Bridge.min'],
];

/**
 * Currently AMD builds require these files to be in a specific spot at
 * runtime. See {@link AMD_WORKER_LANGUAGE_SERVER_IMPORT}
 *
 * @param {string} target
 */
export async function copyLanguageServerFiles(target) {
    for (const [from, to] of languageServiceFiles) {
        await fs.cp(require.resolve(from), path.join(packageFolder, target, to + '.js'));
    }
}

export const extensions = ['.js', '.ts'];

const amdLanguageServerAlias = Object.fromEntries(
    languageServiceFiles.map(([from, to]) => [from, 'vs/language/kusto/' + to])
);

// Language server files need to be evaluated in a specific order. If it wasn't
// for that, we could add them to the AMD module dependencies and things would
// be faster and simpler.
//
// AMD modules have a way to define relationships outside of the module deps,
// but they only affect when the `define` callback is run, not when the .js file is evaluated. The language server files would need to be proper amd modules to take advantage of this, and if they were, we might as well define their deps inside of them.
const AMD_WORKER_LANGUAGE_SERVER_IMPORT = `// If we're running in a web worker - which doesn't share global context with the main thread -
// we need to manually load dependencies that are not explicit- meaning our non-module dependencies
// generated by Bridge.Net
if (typeof document == 'undefined') {
    // Match how monaco defines the base url when inside a web worker: https://github.com/microsoft/vscode/blob/dbeea39df4ac933fe6d5395ad457dad4b494838b/src/vs/base/worker/workerMain.ts#L16
    //
    // MonacoEnvironment.baseUrl is conditional because monaco only sets it when the url is of a different origin.
    // Related monaco code: https://github.com/microsoft/vscode/blob/dbeea39df4ac933fe6d5395ad457dad4b494838b/src/vs/base/browser/defaultWorkerFactory.ts#L47
    //
    // It's not clear if MonacoEnvironment in a worker is meant to be a public api or not, so this may break
    const baseUrl = (self.MonacoEnvironment?.baseUrl || '../../../') + 'vs/language/kusto/';
    importScripts(
         baseUrl + 'bridge.min.js',
         baseUrl + 'kusto.javascript.client.min.js',
         baseUrl + 'newtonsoft.json.min.js',
         baseUrl + 'Kusto.Language.Bridge.min.js'
    );
}`;

/**
 * @type { import('rollup').RollupOptions }
 */
export const rollupAMDConfig = {
    input: {
        kustoMode: path.join(__dirname, '../src/kustoMode.ts'),
        kustoWorker: path.join(__dirname, '../src/kustoWorker.ts'),
        'monaco.contribution': path.join(__dirname, '../src/monaco.contribution.ts'),
    },
    preserveEntrySignatures: 'strict',
    makeAbsoluteExternalsRelative: true,
    external: ['vs/editor/editor.main', ...Object.values(amdLanguageServerAlias)],
    plugins: [
        virtual({
            'language-service': AMD_WORKER_LANGUAGE_SERVER_IMPORT,
        }),
        replace({
            preventAssignment: true,
            'Bridge.isNode': false,
        }),
        alias({ entries: { ['monaco-editor/esm/vs/editor/editor.api']: 'vs/editor/editor.main' } }),
        nodeResolve({ extensions }),
        commonJs(), // Required to bundle xregexp
        babel({
            extensions,
            babelHelpers: 'inline',
            presets: [['@babel/preset-env', { targets: { ie: 11 } }], '@babel/preset-typescript'],
        }),
    ],
};

/**
 *
 * Non-dev builds are minified, bundled, and transpiled so they can be consumed
 * directly, without the consumers running their own bundler or transpiler.
 *
 * @param { 'dev' | 'min' } type
 * @returns { import('rollup').OutputOptions }
 */
export function rollupAMDOutput(type) {
    return {
        name: 'test',
        banner,
        format: 'amd',
        amd: { autoId: true, basePath: 'vs/language/kusto' },
        dir: path.join(packageFolder, 'release', type),
        sourcemap: !process.env.CI,
        plugins: [type === 'min' && terser()],
        globals: {
            'monaco-editor': 'monaco',
        },
    };
}
