import { buildSync } from 'esbuild';
import { beforeAll, describe, expect, it, vi } from 'vitest';
import { exportedForTesting as sm, sourceMapExceptionHandler } from './source-maps.js';
// wasm file can't cleanly be imported within vitest, but we can mock it out.
vi.mock('@gunsch/source-map/lib/mappings.wasm', () => ({
    default: {
        buffer: [],
    },
}));
// Example code for minification that throws an error.
//
// Stack looks like this:
//
//     Error: no soap, radio!
//         at Camp.print (../testcase.js:3:14)
//         at makeError (../testcase.js:7:15)
//         at eval (../testcase.js:9:0)
const exampleSourceCode = `var Camp = class {
    print() {
        throw new Error('no soap, radio!');
    }
};
function makeError() {
    new Camp().print();
}
makeError();
`;
let sourceCompiled;
let sourceMap;
beforeAll(() => {
    // Compile some minified source with sourcemaps with esbuild
    // Command-line equivalent:
    // npx esbuild testcase.js --bundle --outfile=./dist/testcase.js --platform=node --sourcemap --sources-content=false --minify
    const result = buildSync({
        stdin: {
            contents: exampleSourceCode,
            resolveDir: process.cwd(),
            sourcefile: 'testcase.js',
        },
        write: false,
        minify: true,
        bundle: true,
        outfile: 'dist/testcase.js',
        sourcemap: 'external',
        platform: 'browser',
    });
    const outputFiles = {};
    for (const file of result.outputFiles) {
        outputFiles[file.path.substring(file.path.lastIndexOf('/') + 1)] = file.text;
    }
    sourceCompiled = outputFiles['testcase.js'];
    sourceMap = outputFiles['testcase.js.map'];
});
describe('sourceMapExceptionHandler', () => {
    it('has a different stack when eval-ing directly', () => {
        // First, try eval'ing the generated file directly, without sourcemaps.
        let testError;
        try {
            // eslint-disable-next-line security/detect-eval-with-expression
            eval(sourceCompiled);
        }
        catch (error) {
            testError = error;
        }
        expect(testError).toBeDefined();
        const stackExpectations = [
            'Error: no soap, radio!',
            /at r\.print \(eval at <anonymous> \(.*?\/source-maps.test\..s:\d+:\d+\), <anonymous>:1:33\)/,
            /at n \(eval at <anonymous> \(.*?\/source-maps.test\..s:\d+:\d+\), <anonymous>:1:85\)/,
            /at eval \(eval at <anonymous> \(.*?\/source-maps.test\..s:\d+:\d+\), <anonymous>:1:93\)/,
            /at eval \(eval at <anonymous> \(.*?\/source-maps.test\..s:\d+:\d+\), <anonymous>:1:99\)/,
        ];
        for (const [i, expectation] of stackExpectations.entries()) {
            expect(testError?.stack?.split('\n')[i]).toMatch(expectation);
        }
        // Now, run the stack through sourceMapExceptionHandler
        let sourceMappedError;
        try {
            sourceMapExceptionHandler(sourceMap, testError);
        }
        catch (error) {
            sourceMappedError = error;
        }
        expect(sourceMappedError).toBeDefined();
        expect(sourceMappedError?.stack?.split('\n').slice(0, 4)).toEqual([
            'Error: no soap, radio!',
            '    at r.print (../testcase.js:3:14)',
            '    at n (../testcase.js:7:15)',
            '    at eval (../testcase.js:9:0)',
        ]);
    });
});
describe('parseStack', () => {
    // Optional: Test with a mock SourceMapConsumer if the source map is set and an error is thrown
    it('parses stack lines correctly', async () => {
        // Mock out SourceMapConsumer
        const mockConsumer = {
            originalPositionFor: (stackLine) => ({
                source: 'source.ts',
                line: stackLine.line % 1000,
                column: stackLine.column + 15,
            }),
        };
        // Testing different stack line formats that this regex should handle
        const originalStack = [
            'Error: Test error',
            '    at someFunction (at eval:12345:15) (at source.ts:129874:12)',
            '    at testFunction (source.ts:129874:12)',
            '    at Object.type (pen.js:2:6598)',
            '    at v.lde [as RenderPost] (pen.js:2:6651)',
            '    at new (../devvit-test/node_modules/@devvit/public-api/devvit/Devvit.js:389:46)',
            '    at /Users/andrew.gunsch/src/gunsch-sourcemaps/test6-runner.js:24:5',
        ];
        const mappedStack = sm.parseStack(originalStack.join('\n'), mockConsumer);
        expect(mappedStack.split('\n')).toEqual([
            'Error: Test error',
            '    at someFunction (source.ts:874:27)',
            '    at testFunction (source.ts:874:27)',
            '    at Object.type (source.ts:2:6613)',
            '    at v.lde [as RenderPost] (source.ts:2:6666)',
            '    at new (source.ts:389:61)',
            '    at <unknown> (source.ts:24:20)',
        ]);
    });
});
