"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CodeGenerator = void 0;
class CodeGenerator {
    constructor() {
        this.indentLevel = 0;
        this.indentSize = 2;
        this.importCounter = 0;
        // Mapping of Tajik built-in functions to JavaScript equivalents
        this.builtinMappings = new Map([
            // Console functions
            ['чоп', 'console'],
            ['сабт', 'log'],
            ['хато', 'error'],
            ['огоҳӣ', 'warn'],
            ['маълумот', 'info'],
            ['исфти', 'debug'],
            ['ҷадвал', 'table'],
            ['гуруҳ', 'group'],
            ['гуруҳ_охир', 'groupEnd'],
            ['гуруҳ_пӯшида', 'groupCollapsed'],
            ['вақт', 'time'],
            ['вақт_охир', 'timeEnd'],
            ['вақт_сабт', 'timeLog'],
            ['қайд', 'count'],
            ['қайд_асл', 'countReset'],
            ['тасдиқ', 'assert'],
            ['полиз', 'clear'],
            ['феҳрист', 'dir'],
            ['xml_феҳрист', 'dirxml'],
            ['пайҷо', 'trace'],
            // Error handling
            ['Хато', 'Error'],
            // Array methods
            ['рӯйхат', 'Array'],
            ['илова', 'push'],
            ['пуш', 'push'],
            ['баровардан', 'pop'],
            ['дарозӣ', 'length'],
            ['харита', 'map'],
            ['филтр', 'filter'],
            ['кофтан', 'find'],
            ['буридан', 'slice'], // Tajik word for slice/cut
            // String methods
            ['сатр_методҳо', 'String'],
            ['дарозии_сатр', 'length'],
            ['пайвастан', 'concat'],
            ['ҷойивазкунӣ', 'replace'],
            ['ҷудокунӣ', 'join'], // Array join method (combines elements into string)
            // Object methods
            ['объект', 'Object'],
            ['калидҳо', 'keys'],
            ['қиматҳо', 'values'],
            // Math
            ['математика', 'Math'],
            // Control flow
            ['шикастан', 'break'],
            ['давом', 'continue'],
            ['кӯшиш', 'try'],
            // Async/Promise
            ['ваъда', 'Promise'],
            ['Ваъда', 'Promise'],
            ['гирифтан', 'catch'],
            ['ниҳоят', 'finally'],
            ['партофтан', 'throw'],
            // Async
            ['ҳамзамон', 'async'],
            ['интизор', 'await'],
            ['ваъда', 'Promise'],
            // Note: 'хато' is handled specially in generateIdentifier
        ]);
        // Operator precedence table (higher number = higher precedence = evaluated first)
        // Based on JavaScript operator precedence: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence
        this.operatorPrecedence = new Map([
            // Comma (lowest precedence)
            [',', 1],
            // Assignment operators
            ['=', 2],
            ['+=', 2],
            ['-=', 2],
            ['*=', 2],
            ['/=', 2],
            ['%=', 2],
            ['**=', 2],
            ['<<=', 2],
            ['>>=', 2],
            ['>>>=', 2],
            ['&=', 2],
            ['^=', 2],
            ['|=', 2],
            ['&&=', 2],
            ['||=', 2],
            ['??=', 2],
            // Conditional (ternary)
            ['?', 3],
            // Nullish coalescing
            ['??', 4],
            // Logical OR
            ['||', 5],
            // Logical AND
            ['&&', 6],
            // Bitwise OR
            ['|', 7],
            // Bitwise XOR
            ['^', 8],
            // Bitwise AND
            ['&', 9],
            // Equality operators
            ['==', 10],
            ['!=', 10],
            ['===', 10],
            ['!==', 10],
            // Relational operators
            ['<', 11],
            ['<=', 11],
            ['>', 11],
            ['>=', 11],
            ['in', 11],
            ['instanceof', 11],
            // Bitwise shift
            ['<<', 12],
            ['>>', 12],
            ['>>>', 12],
            // Additive
            ['+', 13],
            ['-', 13],
            // Multiplicative
            ['*', 14],
            ['/', 14],
            ['%', 14],
            // Exponentiation (right-associative, highest precedence for binary operators)
            ['**', 15],
        ]);
    }
    generate(ast) {
        return this.generateProgram(ast);
    }
    generateProgram(node) {
        const statements = node.body
            .map(stmt => this.generateStatement(stmt))
            .filter(stmt => stmt.length > 0);
        return statements.join('\n');
    }
    // eslint-disable-next-line complexity
    generateStatement(node) {
        switch (node.type) {
            case 'ImportDeclaration':
                return this.generateImportDeclaration(node);
            case 'ExportDeclaration':
                return this.generateExportDeclaration(node);
            case 'VariableDeclaration':
                return this.generateVariableDeclaration(node);
            case 'FunctionDeclaration':
                return this.generateFunctionDeclaration(node);
            case 'BlockStatement':
                return this.generateBlockStatement(node);
            case 'ReturnStatement':
                return this.generateReturnStatement(node);
            case 'IfStatement':
                return this.generateIfStatement(node);
            case 'WhileStatement':
                return this.generateWhileStatement(node);
            case 'ForStatement':
                return this.generateForStatement(node);
            case 'ForInStatement':
                return this.generateForInStatement(node);
            case 'ForOfStatement':
                return this.generateForOfStatement(node);
            case 'ExpressionStatement':
                return this.generateExpressionStatement(node);
            case 'TryStatement':
                return this.generateTryStatement(node);
            case 'ThrowStatement':
                return this.generateThrowStatement(node);
            case 'InterfaceDeclaration':
                return this.generateInterfaceDeclaration(node);
            case 'TypeAlias':
                return this.generateTypeAlias(node);
            case 'NamespaceDeclaration':
                return this.generateNamespaceDeclaration(node);
            case 'ClassDeclaration':
                return this.generateClassDeclaration(node);
            case 'SwitchStatement':
                return this.generateSwitchStatement(node);
            case 'BreakStatement':
                return this.indent('break;');
            case 'ContinueStatement':
                return this.indent('continue;');
            default:
                throw new Error(`Unknown statement type: ${node.type}`);
        }
    }
    generateVariableDeclaration(node) {
        const kind = node.kind === 'СОБИТ' ? 'const' : 'let';
        const pattern = this.generatePattern(node.identifier);
        const init = node.init ? ` = ${this.generateExpression(node.init)}` : '';
        return this.indent(`${kind} ${pattern}${init};`);
    }
    generateFunctionDeclaration(node) {
        const async = node.async ? 'async ' : '';
        const name = this.generateIdentifier(node.name);
        // Handle new Parameter type or legacy Identifier type
        const params = node.params
            .map(param => {
            if (param.type === 'Parameter') {
                const p = param;
                return this.generateIdentifier(p.name);
            }
            else {
                // Legacy Identifier type
                return this.generateIdentifier(param.name);
            }
        })
            .join(', ');
        const body = this.generateBlockStatement(node.body);
        return this.indent(`${async}function ${name}(${params}) ${body}`);
    }
    generateBlockStatement(node) {
        if (node.body.length === 0) {
            return '{}';
        }
        this.indentLevel++;
        const statements = node.body
            .map(stmt => this.generateStatement(stmt))
            .filter(stmt => stmt.length > 0);
        this.indentLevel--;
        return `{\n${statements.join('\n')}\n${this.getIndent()}}`;
    }
    generateReturnStatement(node) {
        const argument = node.argument ? ` ${this.generateExpression(node.argument)}` : '';
        return this.indent(`return${argument};`);
    }
    generateIfStatement(node) {
        const test = this.generateExpression(node.test);
        const consequent = this.generateStatement(node.consequent);
        let result = this.indent(`if (${test}) `);
        if (node.consequent.type === 'BlockStatement') {
            result += consequent.replace(this.getIndent(), '');
        }
        else {
            result += `{\n${consequent}\n${this.getIndent()}}`;
        }
        if (node.alternate) {
            result += ' else ';
            if (node.alternate.type === 'BlockStatement') {
                result += this.generateStatement(node.alternate).replace(this.getIndent(), '');
            }
            else if (node.alternate.type === 'IfStatement') {
                result += this.generateStatement(node.alternate).replace(this.getIndent(), '');
            }
            else {
                result += `{\n${this.generateStatement(node.alternate)}\n${this.getIndent()}}`;
            }
        }
        return result;
    }
    generateWhileStatement(node) {
        const test = this.generateExpression(node.test);
        const body = this.generateStatement(node.body);
        let result = this.indent(`while (${test}) `);
        if (node.body.type === 'BlockStatement') {
            result += body.replace(this.getIndent(), '');
        }
        else {
            result += `{\n${body}\n${this.getIndent()}}`;
        }
        return result;
    }
    generateForStatement(node) {
        const init = node.init ? this.generateStatement(node.init).trim().replace(/;$/, '') : '';
        const test = node.test ? this.generateExpression(node.test) : '';
        const update = node.update ? this.generateExpression(node.update) : '';
        const body = this.generateStatement(node.body);
        let result = this.indent(`for (${init}; ${test}; ${update}) `);
        if (node.body.type === 'BlockStatement') {
            result += body.replace(this.getIndent(), '');
        }
        else {
            result += `{\n${body}\n${this.getIndent()}}`;
        }
        return result;
    }
    generateForInStatement(node) {
        const left = this.generateStatement(node.left).trim().replace(/;$/, '');
        const right = this.generateExpression(node.right);
        const body = this.generateStatement(node.body);
        let result = this.indent(`for (${left} in ${right}) `);
        if (node.body.type === 'BlockStatement') {
            result += body.replace(this.getIndent(), '');
        }
        else {
            result += `{\n${body}\n${this.getIndent()}}`;
        }
        return result;
    }
    generateForOfStatement(node) {
        const left = this.generateStatement(node.left).trim().replace(/;$/, '');
        const right = this.generateExpression(node.right);
        const body = this.generateStatement(node.body);
        let result = this.indent(`for (${left} of ${right}) `);
        if (node.body.type === 'BlockStatement') {
            result += body.replace(this.getIndent(), '');
        }
        else {
            result += `{\n${body}\n${this.getIndent()}}`;
        }
        return result;
    }
    generateExpressionStatement(node) {
        const expr = node.expression;
        // Special handling: Skip orphaned method signatures that should not generate executable code
        // This happens when interface method signatures escape the interface context due to parsing issues
        if (expr.type === 'CallExpression') {
            const callExpr = expr;
            // Check if this looks like an orphaned method signature (function call with single parameter
            // that looks like it should be a type parameter)
            if (callExpr.callee.type === 'Identifier' &&
                callExpr.arguments.length === 1 &&
                callExpr.arguments[0].type === 'Identifier') {
                const calleeName = callExpr.callee.name;
                const argName = callExpr.arguments[0].name;
                // Skip if this looks like a method signature pattern
                // Common patterns: танзим_кардан(нави_қимат), ғайр_танзим(параметр), etc.
                if (calleeName.includes('_') ||
                    argName.includes('_') ||
                    calleeName.length > 8 ||
                    argName.length > 8) {
                    return ''; // Skip generating this statement
                }
            }
        }
        return this.indent(`${this.generateExpression(node.expression)};`);
    }
    // eslint-disable-next-line complexity
    generateExpression(node) {
        // Handle null or undefined node
        if (!node) {
            return '';
        }
        // Use a more direct delegation approach
        const simpleExpressions = [
            'Identifier',
            'Literal',
            'TemplateLiteral',
            'ThisExpression',
            'Super',
        ];
        if (simpleExpressions.includes(node.type)) {
            return this.generateSimpleExpression(node);
        }
        const operatorExpressions = ['BinaryExpression', 'UnaryExpression', 'UpdateExpression'];
        if (operatorExpressions.includes(node.type)) {
            return this.generateOperatorExpression(node);
        }
        const callExpressions = ['CallExpression', 'AssignmentExpression', 'MemberExpression'];
        if (callExpressions.includes(node.type)) {
            return this.generateCallAssignmentExpression(node);
        }
        const structuralExpressions = ['ArrayExpression', 'ObjectExpression', 'SpreadElement'];
        if (structuralExpressions.includes(node.type)) {
            return this.generateStructuralExpression(node);
        }
        const specialExpressions = [
            'AwaitExpression',
            'NewExpression',
            'ImportExpression',
            'ArrowFunctionExpression',
            'FunctionExpression',
        ];
        if (specialExpressions.includes(node.type)) {
            return this.generateSpecialExpression(node);
        }
        return this.handleUnknownExpression(node);
    }
    generateSimpleExpression(node) {
        switch (node.type) {
            case 'Identifier':
                return this.generateIdentifier(node);
            case 'Literal':
                return this.generateLiteral(node);
            case 'TemplateLiteral':
                return this.generateTemplateLiteral(node);
            case 'ThisExpression':
                return 'this';
            case 'Super':
                return 'super';
            default:
                return this.handleUnknownExpression(node);
        }
    }
    generateOperatorExpression(node) {
        switch (node.type) {
            case 'BinaryExpression':
                return this.generateBinaryExpression(node);
            case 'UnaryExpression':
                return this.generateUnaryExpression(node);
            case 'UpdateExpression':
                return this.generateUpdateExpression(node);
            default:
                return this.handleUnknownExpression(node);
        }
    }
    generateCallAssignmentExpression(node) {
        switch (node.type) {
            case 'CallExpression':
                return this.generateCallExpression(node);
            case 'AssignmentExpression':
                return this.generateAssignmentExpression(node);
            case 'MemberExpression':
                return this.generateMemberExpression(node);
            default:
                return this.handleUnknownExpression(node);
        }
    }
    generateStructuralExpression(node) {
        switch (node.type) {
            case 'ArrayExpression':
                return this.generateArrayExpression(node);
            case 'ObjectExpression':
                return this.generateObjectExpression(node);
            case 'SpreadElement':
                return this.generateSpreadElement(node);
            default:
                return this.handleUnknownExpression(node);
        }
    }
    generateSpecialExpression(node) {
        switch (node.type) {
            case 'AwaitExpression':
                return this.generateAwaitExpression(node);
            case 'NewExpression':
                return this.generateNewExpression(node);
            case 'ImportExpression':
                return this.generateImportExpression(node);
            case 'ArrowFunctionExpression':
                return this.generateArrowFunctionExpression(node);
            case 'FunctionExpression':
                return this.generateFunctionExpression(node);
            default:
                return this.handleUnknownExpression(node);
        }
    }
    generateFunctionExpression(node) {
        // Handle new Parameter type or legacy Identifier type
        const params = node.params
            .map(p => {
            if ('name' in p && typeof p.name === 'object' && 'name' in p.name) {
                return p.name.name;
            }
            else if ('name' in p && typeof p.name === 'string') {
                return p.name;
            }
            return '';
        })
            .join(', ');
        const body = this.generateBlockStatement(node.body);
        return `function(${params}) ${body}`;
    }
    generateArrowFunctionExpression(node) {
        const params = node.params.map(p => p.name.name).join(', ');
        if (node.body.type === 'BlockStatement') {
            // Block body
            const body = this.generateBlockStatement(node.body);
            return `(${params}) => ${body}`;
        }
        else {
            // Expression body
            const body = this.generateExpression(node.body);
            return `(${params}) => ${body}`;
        }
    }
    generateImportExpression(node) {
        // Dynamic import: ворид(specifier) -> import(specifier)
        let source = this.generateExpression(node.source);
        // Handle .som extension conversion for dynamic imports
        if (source.includes('.som')) {
            source = source.replaceAll('.som', '.js');
        }
        else if (source.match(/^["']\.\.?\/[^"']*["']$/) && !source.includes('.js')) {
            // For relative imports without extension, add .js
            source = source.replace(/["']$/, '.js"').replace(/^'/, '"');
        }
        return `import(${source})`;
    }
    handleUnknownExpression(node) {
        throw new Error(`Unknown expression type: ${node.type}`);
    }
    generateImportDeclaration(node) {
        const specifiers = node.specifiers;
        let source = this.generateLiteral(node.source);
        // Module resolution: convert .som extensions to .js
        if (source.includes('.som')) {
            source = source.replaceAll('.som"', '.js"').replaceAll(".som'", ".js'");
        }
        else if (source.match(/^["']\.\.?\/[^"']*["']$/) && !source.includes('.js')) {
            // For relative imports without extension, add .js
            source = source.replace(/["']$/, '.js"').replace(/^'/, '"');
        }
        const results = [];
        const tmpVar = `__somon_import_${this.importCounter++}`;
        results.push(this.indent(`const ${tmpVar} = require(${source});`));
        // Handle default imports
        const defaultImports = specifiers.filter(s => s.type === 'ImportDefaultSpecifier');
        if (defaultImports.length > 0) {
            const localName = defaultImports[0].local.name;
            results.push(this.indent(`const ${localName} = ${tmpVar}.default ?? ${tmpVar};`));
        }
        const namespaceImport = specifiers.find(s => s.type === 'ImportNamespaceSpecifier');
        if (namespaceImport) {
            results.push(this.indent(`const ${namespaceImport.local.name} = ${tmpVar};`));
        }
        // Handle named imports
        const namedImports = specifiers.filter(s => s.type === 'ImportSpecifier');
        if (namedImports.length > 0) {
            const destructuring = namedImports
                .map(spec => {
                const imported = spec.imported.name;
                const local = spec.local.name;
                return imported === local ? imported : `${imported}: ${local}`;
            })
                .join(', ');
            results.push(this.indent(`const { ${destructuring} } = ${tmpVar};`));
        }
        return results.join('\n');
    }
    generateExportDeclaration(node) {
        if (node.declaration) {
            return this.generateExportWithDeclaration(node);
        }
        if (node.specifiers && node.specifiers.length > 0) {
            return this.generateExportWithSpecifiers(node);
        }
        if (node.source) {
            return this.generateWildcardExport(node);
        }
        return '';
    }
    generateExportWithDeclaration(node) {
        const declaration = this.generateStatement(node.declaration);
        const exportName = this.extractExportName(node.declaration);
        // Type-only declarations don't generate runtime exports
        if (!exportName) {
            return declaration;
        }
        const commonjsExport = node.default
            ? `module.exports.default = ${exportName};`
            : `module.exports.${exportName} = ${exportName};`;
        return declaration + '\n' + this.indent(commonjsExport);
    }
    extractExportName(declaration) {
        if (declaration.type === 'FunctionDeclaration') {
            return declaration.name.name;
        }
        if (declaration.type === 'VariableDeclaration') {
            return declaration.identifier.name;
        }
        if (declaration.type === 'ClassDeclaration') {
            return declaration.name.name;
        }
        // Interfaces and TypeAlias don't generate runtime code
        return '';
    }
    generateExportWithSpecifiers(node) {
        if (node.source) {
            return this.generateReExportWithSpecifiers(node);
        }
        return this.generateDirectExportSpecifiers(node);
    }
    generateReExportWithSpecifiers(node) {
        const source = this.convertSourcePath(this.generateLiteral(node.source));
        const tmpVar = `__somon_reexport_${this.importCounter++}`;
        const results = [];
        results.push(this.indent(`const ${tmpVar} = require(${source});`));
        for (const spec of node.specifiers) {
            const exported = spec.exported.name;
            const local = spec.local.name;
            results.push(this.indent(`module.exports.${exported} = ${tmpVar}.${local};`));
        }
        return results.join('\n');
    }
    generateDirectExportSpecifiers(node) {
        return node
            .specifiers.map(spec => {
            const exported = spec.exported.name;
            const local = spec.local.name;
            return this.indent(`module.exports.${exported} = ${local};`);
        })
            .join('\n');
    }
    generateWildcardExport(node) {
        const source = this.convertSourcePath(this.generateLiteral(node.source));
        const tmpVar = `__somon_reexport_${this.importCounter++}`;
        const results = [];
        results.push(this.indent(`const ${tmpVar} = require(${source});`));
        results.push(this.indent(`Object.keys(${tmpVar}).forEach(key => {`));
        this.indentLevel++;
        results.push(this.indent(`if (key !== 'default') module.exports[key] = ${tmpVar}[key];`));
        this.indentLevel--;
        results.push(this.indent(`});`));
        return results.join('\n');
    }
    convertSourcePath(source) {
        if (source.includes('.som')) {
            return source.replaceAll('.som"', '.js"').replaceAll(".som'", ".js'");
        }
        else if (source.match(/^["']\.\.?\/[^"']*["']$/) && !source.includes('.js')) {
            // For relative imports without extension, add .js
            return source.replace(/["']$/, '.js"').replace(/^'/, '"');
        }
        return source;
    }
    generateIdentifier(node) {
        // Only map specific built-in identifiers, not general variable names
        // This prevents variable names like 'рӯйхат' from being mapped to 'Array'
        // Map built-in literals
        if (node.name === 'беқимат') {
            return 'undefined';
        }
        // Special case: don't map 'хато' when it's used as Error constructor
        if (node.name === 'хато') {
            // This is a bit of a hack - we need context to know if it's console.error or Error
            // For now, we'll assume standalone 'хато' means Error, and 'чоп.хато' means console.error
            return 'Error';
        }
        // Handle Хато (capitalized) as Error constructor
        if (node.name === 'Хато') {
            return 'Error';
        }
        // Don't map variable names that could conflict with JS built-ins
        // Only map in specific contexts (handled in generateMemberExpression)
        return node.name;
    }
    generateLiteral(node) {
        if (typeof node.value === 'string') {
            // Properly escape string literals
            const escaped = node.value
                .replaceAll('\\', '\\\\') // Escape backslashes first
                .replaceAll('"', '\\"') // Escape quotes
                .replaceAll('\n', '\\n') // Escape newlines
                .replaceAll('\t', '\\t') // Escape tabs
                .replaceAll('\r', '\\r'); // Escape carriage returns
            return `"${escaped}"`;
        }
        if (node.value === null) {
            return 'null';
        }
        return String(node.value);
    }
    generateTemplateLiteral(node) {
        let result = '`';
        for (let i = 0; i < node.quasis.length; i++) {
            const quasi = node.quasis[i];
            // Add the text part
            result += quasi.value.raw;
            // Add the expression part if it exists
            if (i < node.expressions.length) {
                result += '${';
                result += this.generateExpression(node.expressions[i]);
                result += '}';
            }
        }
        result += '`';
        return result;
    }
    generateBinaryExpression(node, parentOperator) {
        // Generate left and right operands, passing parent operator context for proper precedence
        const left = node.left.type === 'BinaryExpression'
            ? this.generateBinaryExpression(node.left, node.operator)
            : this.generateExpression(node.left);
        const right = node.right.type === 'BinaryExpression'
            ? this.generateBinaryExpression(node.right, node.operator)
            : this.generateExpression(node.right);
        const expr = `${left} ${node.operator} ${right}`;
        // Add parentheses if this expression has lower precedence than its parent
        if (parentOperator && this.needsParentheses(node.operator, parentOperator)) {
            return `(${expr})`;
        }
        return expr;
    }
    generateUnaryExpression(node) {
        const argument = this.generateExpression(node.argument);
        return `${node.operator}${argument}`;
    }
    generateUpdateExpression(node) {
        const argument = this.generateExpression(node.argument);
        if (node.prefix) {
            return `${node.operator}${argument}`;
        }
        else {
            return `${argument}${node.operator}`;
        }
    }
    generateCallExpression(node) {
        let callee = this.generateExpression(node.callee);
        // Special handling for нишондиҳӣ function
        if (callee === 'нишондиҳӣ') {
            callee = 'console.log';
        }
        const args = node.arguments.map(arg => this.generateExpression(arg)).join(', ');
        return `${callee}(${args})`;
    }
    generateAssignmentExpression(node) {
        const left = this.generateExpression(node.left);
        const right = this.generateExpression(node.right);
        return `${left} ${node.operator} ${right}`;
    }
    /**
     * Try to map object name if it's a built-in object
     */
    mapBuiltinObject(node, object) {
        if (node.object.type !== 'Identifier') {
            return { mapped: object, wasMapped: false };
        }
        const objectName = node.object.name;
        const builtinObjects = ['чоп', 'математика', 'объект'];
        if (!builtinObjects.includes(objectName)) {
            return { mapped: object, wasMapped: false };
        }
        const mappedObject = this.builtinMappings.get(objectName);
        if (mappedObject) {
            return { mapped: mappedObject, wasMapped: true };
        }
        return { mapped: object, wasMapped: false };
    }
    /**
     * Check if object looks like a user class instance
     */
    looksLikeClassInstance(node) {
        return (node.object.type === 'Identifier' &&
            /^[а-яё]/.test(node.object.name) &&
            node.object.name.includes('_'));
    }
    /**
     * Try to map property name based on context
     */
    mapPropertyName(node, property, objectMapped) {
        if (node.computed || node.property.type !== 'Identifier') {
            return property;
        }
        const propertyName = node.property.name;
        const mappedProperty = this.builtinMappings.get(propertyName);
        if (!mappedProperty) {
            return property;
        }
        const commonMethods = [
            'пуш',
            'илова',
            'баровардан',
            'дарозӣ',
            'харита',
            'филтр',
            'кофтан',
            'пайвастан',
            'ҷойивазкунӣ',
            'ҷудокунӣ',
            'буридан',
        ];
        const shouldMap = objectMapped || (!this.looksLikeClassInstance(node) && commonMethods.includes(propertyName));
        return shouldMap ? mappedProperty : property;
    }
    generateMemberExpression(node) {
        let object = this.generateExpression(node.object);
        let property = this.generateExpression(node.property);
        const { mapped: mappedObject, wasMapped: objectMapped } = this.mapBuiltinObject(node, object);
        object = mappedObject;
        property = this.mapPropertyName(node, property, objectMapped);
        // Special case: чоп.хато should become console.error
        if (object === 'console' && property === 'Error') {
            property = 'error';
        }
        return node.computed ? `${object}[${property}]` : `${object}.${property}`;
    }
    generateArrayExpression(node) {
        const elements = node.elements.map(element => this.generateExpression(element));
        return `[${elements.join(', ')}]`;
    }
    generateObjectExpression(node) {
        const properties = node.properties
            .map((prop) => {
            const key = prop.computed
                ? `[${this.generateExpression(prop.key)}]`
                : this.generateExpression(prop.key);
            const value = this.generateExpression(prop.value);
            return `${key}: ${value}`;
        })
            .join(', ');
        return `{${properties}}`;
    }
    generateTryStatement(node) {
        let result = this.indent('try ') + this.generateBlockStatement(node.block).replace(this.getIndent(), '');
        if (node.handler) {
            result += ' catch ';
            if (node.handler.param) {
                result += `(${this.generateIdentifier(node.handler.param)}) `;
            }
            else {
                result += '(error) ';
            }
            result += this.generateBlockStatement(node.handler.body).replace(this.getIndent(), '');
        }
        if (node.finalizer) {
            result += ' finally ';
            result += this.generateBlockStatement(node.finalizer).replace(this.getIndent(), '');
        }
        return result;
    }
    generateThrowStatement(node) {
        const argument = this.generateExpression(node.argument);
        return this.indent(`throw ${argument};`);
    }
    generateAwaitExpression(node) {
        const argument = this.generateExpression(node.argument);
        return `await ${argument}`;
    }
    generateNewExpression(node) {
        const callee = this.generateExpression(node.callee);
        const args = node.arguments
            ? node.arguments.map((arg) => this.generateExpression(arg)).join(', ')
            : '';
        return `new ${callee}(${args})`;
    }
    generateInterfaceDeclaration(node) {
        // Interfaces are TypeScript-only constructs, so we generate a comment in JavaScript
        // They should NOT generate any executable code at all
        const name = this.generateIdentifier(node.name);
        return this.indent(`// Interface: ${name}\n`);
    }
    generateTypeAlias(node) {
        // Type aliases are TypeScript-only constructs, so we generate a comment in JavaScript
        const name = this.generateIdentifier(node.name);
        return this.indent(`// Type alias: ${name}\n`);
    }
    generateNamespaceDeclaration(node) {
        // Generate namespace as an IIFE (Immediately Invoked Function Expression)
        const name = this.generateIdentifier(node.name);
        const exported = node.exported ? 'exports.' : '';
        let result = this.indent(`${exported}${name} = (function() {\n`);
        this.indentLevel++;
        result += this.indent(`const ${name} = {};\n`);
        // Generate namespace body
        if (node.body && node.body.statements) {
            for (const stmt of node.body.statements) {
                const isExported = stmt.exported;
                // Skip interface declarations and type aliases - they don't generate runtime code
                if (stmt.type === 'InterfaceDeclaration' || stmt.type === 'TypeAlias') {
                    result += this.generateStatement(stmt);
                    continue;
                }
                result += isExported
                    ? this.generateExportedNamespaceMember(stmt, name)
                    : this.generateStatement(stmt);
            }
        }
        result += this.indent(`return ${name};\n`);
        this.indentLevel--;
        result += this.indent('})();\n');
        return result;
    }
    generateExportedNamespaceMember(stmt, namespaceName) {
        const memberName = this.getMemberName(stmt);
        if (!memberName) {
            return '';
        }
        if (stmt.type === 'NamespaceDeclaration') {
            return this.generateNestedNamespaceExport(stmt, namespaceName, memberName);
        }
        // For functions, variables, classes
        const stmtCode = this.generateStatement(stmt);
        let result = stmtCode;
        if (stmtCode.trim() && stmt.type !== 'ExpressionStatement') {
            // Ensure there's a newline before the assignment if stmtCode doesn't end with one
            if (!stmtCode.endsWith('\n')) {
                result += '\n';
            }
            result += this.indent(`${namespaceName}.${memberName} = ${memberName};\n`);
        }
        return result;
    }
    generateNestedNamespaceExport(nestedNs, parentName, memberName) {
        // Remove the exported flag for nested generation
        const originalExported = nestedNs.exported;
        nestedNs.exported = false;
        const nestedCode = this.generateStatement(nestedNs).trim();
        nestedNs.exported = originalExported;
        // Generate as a property of parent namespace
        // Remove the initial assignment part from the nested code (e.g., "Дарунӣ = ")
        const assignmentStart = nestedCode.indexOf('= (function()');
        const nestedIIFE = assignmentStart !== -1
            ? nestedCode.substring(assignmentStart + 2) // Skip "= "
            : nestedCode;
        return this.indent(`${parentName}.${memberName} = ${nestedIIFE}`);
    }
    getMemberName(stmt) {
        switch (stmt.type) {
            case 'FunctionDeclaration':
                return stmt.name.name;
            case 'VariableDeclaration': {
                const varDecl = stmt;
                if (varDecl.identifier && varDecl.identifier.type === 'Identifier') {
                    return varDecl.identifier.name;
                }
                break;
            }
            case 'ClassDeclaration':
                return stmt.name.name;
            case 'NamespaceDeclaration':
                return stmt.name.name;
        }
        return null;
    }
    generateClassDeclaration(node) {
        const className = this.generateIdentifier(node.name);
        const extendsClause = node.superClass
            ? ` extends ${this.generateIdentifier(node.superClass)}`
            : '';
        // Note: JavaScript doesn't support abstract classes, so we skip the abstract modifier
        let classBody = '';
        // Generate class members
        if (node.body && node.body.body) {
            const members = node.body.body
                .map(member => {
                switch (member.type) {
                    case 'MethodDefinition':
                        return this.generateMethodDefinition(member);
                    case 'PropertyDefinition':
                        return this.generatePropertyDefinition(member);
                    default:
                        return '';
                }
            })
                .filter((member) => member.length > 0);
            if (members.length > 0) {
                this.indentLevel++;
                classBody = '\n' + members.join('\n') + '\n' + this.getIndent();
                this.indentLevel--;
            }
        }
        return this.indent(`class ${className}${extendsClause} {${classBody}}`);
    }
    generateMethodDefinition(node) {
        const methodName = node.kind === 'constructor' ? 'constructor' : this.generateIdentifier(node.key);
        const isStatic = node.static ? 'static ' : '';
        // Note: JavaScript doesn't support abstract methods, so we skip them entirely
        // Generate parameters
        const params = node.value.params
            ? node.value.params.map(param => this.generateIdentifier(param.name)).join(', ')
            : '';
        // Skip abstract methods - they don't exist in JavaScript
        if (node.abstract) {
            // Abstract methods should not generate any code in JavaScript
            return '';
        }
        else {
            // Regular methods have a body
            // Handle cases where body might be null or undefined
            if (!node.value || !node.value.body) {
                return this.indent(`${isStatic}${methodName}(${params}) {}`);
            }
            const body = this.generateBlockStatement(node.value.body);
            return this.indent(`${isStatic}${methodName}(${params}) ${body}`);
        }
    }
    generatePropertyDefinition(node) {
        const propertyName = this.generateIdentifier(node.key);
        const isStatic = node.static ? 'static ' : '';
        const initializer = node.value ? ` = ${this.generateExpression(node.value)}` : '';
        return this.indent(`${isStatic}${propertyName}${initializer};`);
    }
    needsParentheses(operator, parentOperator) {
        // Get precedence values (default to 0 if not found)
        const opPrecedence = this.operatorPrecedence.get(operator) || 0;
        const parentPrecedence = this.operatorPrecedence.get(parentOperator) || 0;
        // Parentheses are needed if:
        // 1. Current operator has lower precedence than parent (evaluated later)
        // 2. Same precedence but parent is not left-associative (for right-associative like **)
        if (opPrecedence < parentPrecedence) {
            return true;
        }
        // Special case: exponentiation is right-associative
        // a ** b ** c means a ** (b ** c), so we need parens for left operand
        // This is handled by parser, so we just need to preserve them
        if (opPrecedence === parentPrecedence && parentOperator === '**') {
            return operator !== '**'; // Add parens if operators differ at same level under **
        }
        return false;
    }
    generateSwitchStatement(node) {
        const discriminant = this.generateExpression(node.discriminant);
        this.indentLevel++;
        const cases = node.cases
            .map((switchCase) => {
            if (!switchCase.test) {
                const consequent = switchCase.consequent
                    .map(stmt => this.generateStatement(stmt))
                    .join('\n');
                return this.indent(`default:\n${consequent}`);
            }
            else {
                const test = this.generateExpression(switchCase.test);
                const consequent = switchCase.consequent
                    .map(stmt => this.generateStatement(stmt))
                    .join('\n');
                return this.indent(`case ${test}:\n${consequent}`);
            }
        })
            .join('\n');
        this.indentLevel--;
        return this.indent(`switch (${discriminant}) {\n${cases}\n${this.getIndent()}}`);
    }
    // Pattern generation methods
    generatePattern(node) {
        switch (node.type) {
            case 'Identifier':
                return this.generateIdentifier(node);
            case 'ArrayPattern':
                return this.generateArrayPattern(node);
            case 'ObjectPattern':
                return this.generateObjectPattern(node);
            default:
                throw new Error('Unknown pattern type');
        }
    }
    generateArrayPattern(node) {
        const elements = node.elements
            .map(element => {
            if (element === null) {
                return '';
            }
            else if (element.type === 'SpreadElement') {
                return this.generateSpreadElement(element);
            }
            else {
                return this.generatePattern(element);
            }
        })
            .join(', ');
        return `[${elements}]`;
    }
    generateObjectPattern(node) {
        const properties = node.properties
            .map(prop => {
            if (prop.type === 'SpreadElement') {
                return this.generateSpreadElement(prop);
            }
            else {
                return this.generatePropertyPattern(prop);
            }
        })
            .join(', ');
        return `{${properties}}`;
    }
    generatePropertyPattern(node) {
        const key = node.computed
            ? `[${this.generateExpression(node.key)}]`
            : this.generateIdentifier(node.key);
        if (node.key.type === 'Identifier' &&
            node.value.type === 'Identifier' &&
            node.key.name === node.value.name) {
            return key;
        }
        else {
            const value = this.generatePattern(node.value);
            return `${key}: ${value}`;
        }
    }
    generateSpreadElement(node) {
        const argument = this.generateExpression(node.argument);
        return `...${argument}`;
    }
    indent(text) {
        return this.getIndent() + text;
    }
    getIndent() {
        return ' '.repeat(this.indentLevel * this.indentSize);
    }
}
exports.CodeGenerator = CodeGenerator;
//# sourceMappingURL=codegen.js.map