"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createProgram = exports.compileFile = void 0;
const commander_1 = require("commander");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const module_1 = require("module");
// Read package.json at runtime to avoid import attribute issues
function findPackageJson() {
    let currentDir = __dirname;
    while (currentDir !== path.dirname(currentDir)) {
        const packagePath = path.join(currentDir, 'package.json');
        if (fs.existsSync(packagePath)) {
            return JSON.parse(fs.readFileSync(packagePath, 'utf8'));
        }
        currentDir = path.dirname(currentDir);
    }
    // Fallback for test environments
    const fallbackPath = path.resolve(process.cwd(), 'package.json');
    if (fs.existsSync(fallbackPath)) {
        return JSON.parse(fs.readFileSync(fallbackPath, 'utf8'));
    }
    throw new Error('package.json not found');
}
const pkg = findPackageJson();
let tsRuntimeRegistered = false;
const localRequire = (0, module_1.createRequire)(__filename);
const compiledCompilerModuleId = '../compiler';
const sourceCompilerModuleId = '../../src/compiler.ts';
const compiledConfigModuleId = '../config';
const sourceConfigModuleId = '../../src/config.ts';
const { compile } = loadCompiler();
const { loadConfig } = loadConfigModule();
async function executeBundleCommand(input, options) {
    try {
        const baseDir = path.dirname(path.resolve(input));
        const config = loadConfig(baseDir);
        const moduleSystem = await createModuleSystem(baseDir, config);
        const bundleOptions = createBundleOptions(input, options, config, baseDir);
        if (bundleOptions.format !== 'commonjs' && !bundleOptions.force) {
            console.error('ESM/UMD bundle formats are experimental. Re-run with --force to proceed.');
            process.exitCode = 1;
            return;
        }
        await performBundling(moduleSystem, bundleOptions, input);
    }
    catch (error) {
        console.error('Bundle error:', error instanceof Error ? error.message : error);
        process.exitCode = 1;
    }
}
async function createModuleSystem(baseDir, config) {
    const { ModuleSystem } = await Promise.resolve().then(() => __importStar(require('../module-system')));
    return new ModuleSystem({
        resolution: {
            baseUrl: baseDir,
            ...(config.moduleSystem?.resolution || {}),
        },
        loading: config.moduleSystem?.loading
            ? {
                ...config.moduleSystem.loading,
                encoding: config.moduleSystem.loading.encoding,
            }
            : undefined,
        compilation: config.moduleSystem?.compilation,
    });
}
function createBundleOptions(input, options, config, _baseDir) {
    return {
        entryPoint: path.resolve(input),
        outputPath: options.output ?? config.bundle?.output,
        format: (options.format ?? config.bundle?.format ?? 'commonjs'),
        minify: options.minify ?? config.bundle?.minify,
        sourceMaps: options.sourceMap ?? config.bundle?.sourceMaps,
        externals: options.externals ? options.externals.split(',') : config.bundle?.externals,
        force: options.force ?? config.bundle?.force,
    };
}
async function performBundling(moduleSystem, bundleOptions, input) {
    console.log(`📦 Bundling ${input}...`);
    if (bundleOptions.format !== 'commonjs') {
        console.warn('Warning: ESM/UMD formats are experimental; prefer commonjs for execution.');
    }
    const bundle = await moduleSystem.bundle(bundleOptions);
    const outputPath = getBundleOutputPath(bundleOptions, input);
    fs.mkdirSync(path.dirname(outputPath), { recursive: true });
    fs.writeFileSync(outputPath, bundle);
    console.log(`✅ Bundle created: ${outputPath}`);
    const stats = moduleSystem.getStatistics();
    console.log(`📊 Bundled ${stats.totalModules} modules`);
}
function getBundleOutputPath(bundleOptions, input) {
    if (bundleOptions.outputPath) {
        return path.isAbsolute(bundleOptions.outputPath)
            ? bundleOptions.outputPath
            : path.resolve(path.dirname(path.resolve(input)), bundleOptions.outputPath);
    }
    return input.replace(/\.som$/, '.bundle.js');
}
function mergeOptions(input, options) {
    const config = loadConfig(path.dirname(path.resolve(input)));
    const merged = { ...(config.compilerOptions ?? {}), ...options };
    // Handle negation flags - they override positive flags
    if (options.noSourceMap) {
        merged.sourceMap = false;
    }
    if (options.noMinify) {
        merged.minify = false;
    }
    // Set default target if not specified
    if (!merged.target) {
        merged.target = 'es2020';
    }
    return merged;
}
function compileFile(input, options) {
    try {
        if (!fs.existsSync(input)) {
            const message = `Error: File '${input}' not found`;
            console.error(message);
            process.exitCode = 1;
            return { code: '', errors: [message], warnings: [] };
        }
        const source = fs.readFileSync(input, 'utf-8');
        const result = compile(source, {
            target: options.target,
            sourceMap: options.sourceMap,
            minify: options.minify,
            typeCheck: !options.noTypeCheck,
            strict: options.strict,
        });
        if (result.errors.length > 0) {
            console.error('Compilation errors:');
            result.errors.forEach(error => console.error(`  ${error}`));
            process.exitCode = 1;
        }
        if (result.warnings.length > 0) {
            console.warn('Warnings:');
            result.warnings.forEach(warning => console.warn(`  ${warning}`));
        }
        return result;
    }
    catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        console.error('Error:', message);
        process.exitCode = 1;
        return { code: '', errors: [message], warnings: [] };
    }
}
exports.compileFile = compileFile;
function createProgram() {
    const program = new commander_1.Command();
    program
        .name('somon')
        .description('SomonScript compiler - Compile Tajik Cyrillic code to JavaScript')
        .version(pkg.version);
    program
        .command('compile')
        .alias('c')
        .description('Compile SomonScript files to JavaScript')
        .usage('[input] [options]')
        .argument('<input>', 'Input .som file')
        .option('-o, --output <file>', 'Output file (default: same name with .js extension)')
        .option('--out-dir <dir>', 'Output directory')
        .option('--target <target>', 'Compilation target')
        .option('--source-map', 'Generate source maps')
        .option('--no-source-map', 'Disable source maps')
        .option('--minify', 'Minify output')
        .option('--no-minify', 'Disable minification')
        .option('--no-type-check', 'Disable type checking')
        .option('--strict', 'Enable strict type checking')
        .option('-w, --watch', 'Recompile on file changes')
        .action((input, options) => {
        try {
            const merged = mergeOptions(input, options);
            const compileOnce = () => {
                const result = compileFile(input, merged);
                if (result.errors.length > 0)
                    return;
                const baseDir = path.dirname(path.resolve(input));
                const outputFile = merged.output ||
                    (merged.outDir
                        ? path.join(path.resolve(baseDir, merged.outDir), path.basename(input).replace(/\.som$/, '.js'))
                        : input.replace(/\.som$/, '.js'));
                fs.mkdirSync(path.dirname(outputFile), { recursive: true });
                fs.writeFileSync(outputFile, result.code);
                console.log(`Compiled '${input}' to '${outputFile}'`);
                if (merged.sourceMap && result.sourceMap) {
                    const sourceMapFile = `${outputFile}.map`;
                    fs.writeFileSync(sourceMapFile, result.sourceMap);
                    console.log(`Generated source map: '${sourceMapFile}'`);
                }
            };
            compileOnce();
            if ((merged.watch || merged.compileOnSave) && process.env.NODE_ENV !== 'test') {
                console.log(`Watching '${input}' for changes...`);
                fs.watch(input, { persistent: true }, () => {
                    console.log(`Recompiling '${input}'...`);
                    compileOnce();
                });
            }
            else if (merged.watch || merged.compileOnSave) {
                // In test environment, just log the message without actually watching
                console.log(`Watching '${input}' for changes...`);
            }
        }
        catch (error) {
            console.error('Error:', error instanceof Error ? error.message : error);
            process.exitCode = 1;
            return;
        }
    });
    program
        .command('run')
        .alias('r')
        .description('Compile and run SomonScript file')
        .usage('[input] [options]')
        .argument('<input>', 'Input .som file')
        .option('--target <target>', 'Compilation target')
        .option('--source-map', 'Generate source maps')
        .option('--no-source-map', 'Disable source maps')
        .option('--minify', 'Minify output')
        .option('--no-minify', 'Disable minification')
        .option('--no-type-check', 'Disable type checking')
        .option('--strict', 'Enable strict type checking')
        .action((input, options) => {
        try {
            const merged = mergeOptions(input, options);
            const result = compileFile(input, merged);
            if (result.errors.length > 0)
                return;
            // eslint-disable-next-line no-eval
            eval(result.code);
        }
        catch (error) {
            console.error('Error:', error instanceof Error ? error.message : error);
            process.exitCode = 1;
            return;
        }
    });
    program
        .command('init')
        .description('Initialize a new SomonScript project')
        .argument('[name]', 'Project name', 'somon-project')
        .action((name) => {
        try {
            const projectDir = path.resolve(name);
            if (fs.existsSync(projectDir)) {
                console.error(`Error: Directory '${name}' already exists`);
                process.exitCode = 1;
                return;
            }
            // Create project directory
            fs.mkdirSync(projectDir, { recursive: true });
            // Create package.json
            const packageJson = {
                name,
                version: '0.1.0',
                description: 'A SomonScript project',
                main: 'dist/main.js',
                scripts: {
                    build: 'somon compile src/main.som -o dist/main.js',
                    dev: 'somon run src/main.som',
                },
                devDependencies: {
                    'somon-script': '^0.2.0',
                },
            };
            fs.writeFileSync(path.join(projectDir, 'package.json'), JSON.stringify(packageJson, null, 2));
            // Create src and dist directories
            fs.mkdirSync(path.join(projectDir, 'src'));
            fs.mkdirSync(path.join(projectDir, 'dist'));
            // Create default configuration
            const somonConfig = {
                compilerOptions: {
                    target: 'es2020',
                    sourceMap: false,
                    minify: false,
                    noTypeCheck: false,
                    strict: false,
                    outDir: 'dist',
                    watch: false,
                    compileOnSave: false,
                },
            };
            fs.writeFileSync(path.join(projectDir, 'somon.config.json'), JSON.stringify(somonConfig, null, 2));
            // Create main file
            const mainSom = `// SomonScript main file
функсия салом(): void {
    чоп.сабт("Салом, ҷаҳон!");
}

салом();
`;
            fs.writeFileSync(path.join(projectDir, 'src', 'main.som'), mainSom);
            console.log(`✅ Created SomonScript project '${name}'`);
            console.log(`\nNext steps:`);
            console.log(`  cd ${name}`);
            console.log(`  npm install`);
            console.log(`  npm run dev`);
        }
        catch (error) {
            console.error('Error:', error instanceof Error ? error.message : error);
            process.exit(1);
        }
    });
    // Bundle command
    program
        .command('bundle')
        .alias('b')
        .description('Bundle SomonScript modules into a single file')
        .usage('[input] [options]')
        .argument('<input>', 'Entry point file')
        .option('-o, --output <file>', 'Output file path')
        .option('-f, --format <format>', 'Bundle format (commonjs, esm, umd)', 'commonjs')
        .option('--minify', 'Minify the output')
        .option('--source-map', 'Generate source maps')
        .option('--externals <modules>', 'External modules (comma-separated)')
        .option('--force', 'Allow experimental formats (esm/umd)')
        .action(async (input, options) => {
        await executeBundleCommand(input, options);
    });
    // Module info command
    program
        .command('module-info')
        .alias('info')
        .description('Show module dependency information')
        .usage('[input] [options]')
        .argument('<input>', 'Entry point file')
        .option('--graph', 'Show dependency graph')
        .option('--stats', 'Show module statistics')
        .option('--circular', 'Check for circular dependencies')
        .action(async (input, options) => {
        try {
            const { ModuleSystem } = await Promise.resolve().then(() => __importStar(require('../module-system')));
            const moduleSystem = new ModuleSystem({
                resolution: {
                    baseUrl: path.dirname(path.resolve(input)),
                },
            });
            console.log(`🔍 Analyzing ${input}...`);
            await moduleSystem.loadModule(path.resolve(input), process.cwd());
            if (options.stats) {
                const stats = moduleSystem.getStatistics();
                console.log('\n📊 Module Statistics:');
                console.log(`  Total modules: ${stats.totalModules}`);
                console.log(`  Total dependencies: ${stats.totalDependencies}`);
                console.log(`  Average dependencies per module: ${stats.averageDependencies.toFixed(2)}`);
                console.log(`  Maximum dependency depth: ${stats.maxDependencyDepth}`);
                console.log(`  Circular dependencies: ${stats.circularDependencies}`);
            }
            if (options.graph) {
                const graph = moduleSystem.getDependencyGraph();
                console.log('\n🕸️  Dependency Graph:');
                for (const [moduleId, deps] of graph) {
                    const relativePath = path.relative(process.cwd(), moduleId);
                    console.log(`  ${relativePath}:`);
                    for (const dep of deps) {
                        console.log(`    └── ${dep}`);
                    }
                }
            }
            if (options.circular) {
                const validation = moduleSystem.validate();
                if (validation.isValid) {
                    console.log('\n✅ No circular dependencies found');
                }
                else {
                    console.log('\n❌ Issues found:');
                    for (const error of validation.errors) {
                        console.log(`  • ${error}`);
                    }
                }
            }
        }
        catch (error) {
            console.error('Analysis error:', error instanceof Error ? error.message : error);
            process.exit(1);
        }
    });
    // Resolve command
    program
        .command('resolve')
        .description('Resolve a module specifier to its file path')
        .usage('<specifier> [options]')
        .argument('<specifier>', 'Module specifier to resolve')
        .option('-f, --from <file>', 'Resolve from this file', process.cwd())
        .action(async (specifier, options) => {
        try {
            const { ModuleResolver } = await Promise.resolve().then(() => __importStar(require('../module-system')));
            const fromFile = options.from ?? process.cwd();
            const resolver = new ModuleResolver({
                baseUrl: path.dirname(path.resolve(fromFile)),
            });
            const resolved = resolver.resolve(specifier, fromFile);
            console.log(`🎯 Resolved '${specifier}':`);
            console.log(`  Path: ${resolved.resolvedPath}`);
            console.log(`  Extension: ${resolved.extension}`);
            console.log(`  External: ${resolved.isExternalLibrary ? 'Yes' : 'No'}`);
            if (resolved.packageName) {
                console.log(`  Package: ${resolved.packageName}`);
            }
        }
        catch (error) {
            console.error('Resolve error:', error instanceof Error ? error.message : error);
            process.exit(1);
        }
    });
    return program;
}
exports.createProgram = createProgram;
function loadCompiler() {
    try {
        return localRequire(compiledCompilerModuleId);
    }
    catch (error) {
        if (!isModuleNotFound(error)) {
            throw error;
        }
    }
    try {
        return localRequire('../compiler.js');
    }
    catch (error) {
        if (!isModuleNotFound(error)) {
            throw error;
        }
    }
    const ts = loadTypeScript();
    const compilerSourcePath = path.resolve(__dirname, '..', '..', 'src', 'compiler.ts');
    if (!fs.existsSync(compilerSourcePath)) {
        throw new Error("Compiler module not found. Run 'npm run build' before executing the CLI.");
    }
    registerRuntimeTsTranspiler(ts);
    return localRequire(sourceCompilerModuleId);
}
function loadConfigModule() {
    try {
        return localRequire(compiledConfigModuleId);
    }
    catch (error) {
        if (!isModuleNotFound(error)) {
            throw error;
        }
    }
    try {
        return localRequire('../config.js');
    }
    catch (error) {
        if (!isModuleNotFound(error)) {
            throw error;
        }
    }
    const ts = loadTypeScript();
    const configSourcePath = path.resolve(__dirname, '..', '..', 'src', 'config.ts');
    if (!fs.existsSync(configSourcePath)) {
        throw new Error("Config module not found. Run 'npm run build' before executing the CLI.");
    }
    registerRuntimeTsTranspiler(ts);
    return localRequire(sourceConfigModuleId);
}
function isModuleNotFound(error) {
    return error instanceof Error && error.code === 'MODULE_NOT_FOUND';
}
function loadTypeScript() {
    try {
        return require('typescript');
    }
    catch (error) {
        throw new Error('TypeScript runtime is required to execute the CLI without compiled artifacts. Please install dev dependencies.');
    }
}
function registerRuntimeTsTranspiler(ts) {
    if (tsRuntimeRegistered || require.extensions['.ts']) {
        // Either we already registered our hook or another tool (e.g. ts-node) is handling .ts files.
        tsRuntimeRegistered = true;
        return;
    }
    require.extensions['.ts'] = (module, filename) => {
        const source = fs.readFileSync(filename, 'utf-8');
        const { outputText } = ts.transpileModule(source, {
            compilerOptions: {
                module: ts.ModuleKind.CommonJS,
                target: ts.ScriptTarget.ES2020,
                esModuleInterop: true,
                sourceMap: false,
            },
            fileName: filename,
        });
        module._compile(outputText, filename);
    };
    tsRuntimeRegistered = true;
}
//# sourceMappingURL=program.js.map