"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.compile = void 0;
const core_1 = require("@babel/core");
const source_map_1 = require("source-map");
const typescript_1 = __importDefault(require("typescript"));
const codegen_1 = require("./codegen");
const lexer_1 = require("./lexer");
const parser_1 = require("./parser");
const type_checker_1 = require("./type-checker");
/**
 * Compile SomonScript source code to JavaScript.
 *
 * @param source Raw SomonScript program text.
 * @param options Optional compiler configuration. See {@link CompileOptions} for details.
 * @returns Structured {@link CompileResult} output containing emitted JavaScript, source maps,
 * diagnostics, and warnings.
 */
function compile(source, options = {}) {
    const errors = [];
    const warnings = [];
    try {
        const ast = parseSource(source);
        if (options.typeCheck !== false) {
            const result = runTypeCheck(source, ast);
            errors.push(...result.errors);
            warnings.push(...result.warnings);
            if (options.strict && result.errors.length > 0) {
                return { code: '', errors, warnings };
            }
        }
        let code = new codegen_1.CodeGenerator().generate(ast);
        const transpileResult = transpile(code, options);
        code = transpileResult.code;
        let map = transpileResult.map;
        if (options.sourceMap && !map) {
            map = generateIdentityMap(code);
        }
        if (options.minify) {
            ({ code, map } = minifyCode(code, map, options.sourceMap));
        }
        return {
            code,
            sourceMap: options.sourceMap && map ? JSON.stringify(map) : undefined,
            errors,
            warnings,
        };
    }
    catch (error) {
        errors.push(error instanceof Error ? error.message : String(error));
        return { code: '', errors, warnings };
    }
}
exports.compile = compile;
function parseSource(source) {
    const lexer = new lexer_1.Lexer(source);
    const tokens = lexer.tokenize();
    return new parser_1.Parser(tokens).parse();
}
function runTypeCheck(source, ast) {
    const checker = new type_checker_1.TypeChecker(source);
    const result = checker.check(ast);
    return {
        errors: result.errors.map(err => `Type error [${err.code}] at line ${err.line}, column ${err.column}: ${err.message}\n> ${err.snippet}`),
        warnings: result.warnings.map(warn => `Type warning [${warn.code}] at line ${warn.line}, column ${warn.column}: ${warn.message}\n> ${warn.snippet}`),
    };
}
function transpile(code, options) {
    const targetMap = {
        es5: typescript_1.default.ScriptTarget.ES5,
        es2015: typescript_1.default.ScriptTarget.ES2015,
        es2020: typescript_1.default.ScriptTarget.ES2020,
        esnext: typescript_1.default.ScriptTarget.ESNext,
    };
    const target = options.target ?? 'es2020';
    if (target === 'es2020') {
        return { code };
    }
    const transpile = typescript_1.default.transpileModule(code, {
        compilerOptions: {
            target: targetMap[target],
            module: typescript_1.default.ModuleKind.ESNext,
            sourceMap: options.sourceMap,
        },
    });
    const map = options.sourceMap && transpile.sourceMapText
        ? JSON.parse(transpile.sourceMapText)
        : undefined;
    return { code: transpile.outputText, map };
}
function generateIdentityMap(code) {
    const generator = new source_map_1.SourceMapGenerator({ file: 'compiled.js' });
    const lines = code.split('\n');
    for (let i = 0; i < lines.length; i++) {
        generator.addMapping({
            generated: { line: i + 1, column: 0 },
            original: { line: i + 1, column: 0 },
            source: 'source.som',
        });
    }
    return JSON.parse(generator.toString());
}
function minifyCode(code, map, sourceMap) {
    // Lazy-load minify preset to avoid hard dependency at runtime
    let presetModule = null;
    try {
        // eslint-disable-next-line @typescript-eslint/no-var-requires
        presetModule = require('babel-preset-minify');
    }
    catch {
        // Preset not available; apply a conservative whitespace trim as fallback
        const basic = code.replace(/\s*=\s*/g, '=').replace(/\s*;\s*/g, ';');
        return { code: basic, map };
    }
    // Safely coerce the dynamically required preset into a PluginItem if possible.
    // babel-preset-minify exports either a function or an object acceptable as a preset.
    const presetItems = [];
    if (presetModule && (typeof presetModule === 'function' || typeof presetModule === 'object')) {
        presetItems.push(presetModule);
    }
    const babel = (0, core_1.transformSync)(code, {
        sourceMaps: sourceMap,
        // Cast RawSourceMap to Babel's InputSourceMap - they have compatible structures
        inputSourceMap: map ? { ...map, file: map.file || '' } : undefined,
        presets: presetItems,
        comments: false,
        compact: true,
    });
    return {
        code: babel?.code && babel.code.length > 0 ? babel.code : code,
        map: sourceMap && babel?.map ? babel.map : map,
    };
}
//# sourceMappingURL=compiler.js.map