"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TypeChecker = exports.TypeCheckErrorCode = void 0;
/**
 * Stable error codes for type checking diagnostics
 */
exports.TypeCheckErrorCode = {
    TypeMismatch: 'TYPE_NOT_ASSIGNABLE',
    ClassNotFound: 'CLASS_NOT_FOUND',
    InvalidExtends: 'INVALID_EXTENDS',
    CircularInheritance: 'CIRCULAR_INHERITANCE',
};
/**
 * Type checker for SomonScript AST
 * Provides comprehensive type checking with Tajik Cyrillic type annotations
 */
class TypeChecker {
    constructor(source) {
        this.errors = [];
        this.warnings = [];
        this.symbolTable = new Map();
        this.interfaceTable = new Map();
        this.typeAliasTable = new Map();
        this.sourceLines = [];
        this.sourceLines = source ? source.split(/\r?\n/) : [];
        this.initializePrimitiveTypes();
    }
    /**
     * Initialize primitive types in the symbol table
     */
    initializePrimitiveTypes() {
        this.symbolTable.set('сатр', { kind: 'primitive', name: 'string' });
        this.symbolTable.set('рақам', { kind: 'primitive', name: 'number' });
        this.symbolTable.set('мантиқӣ', { kind: 'primitive', name: 'boolean' });
        this.symbolTable.set('холӣ', { kind: 'primitive', name: 'null' });
    }
    getSnippet(line) {
        return this.sourceLines[line - 1] ?? '';
    }
    /**
     * Perform type checking on the entire program
     * @param program - The AST program to type check
     * @returns Type checking result with errors and warnings
     */
    check(program) {
        this.errors = [];
        this.warnings = [];
        // First pass: collect type definitions
        this.collectTypeDefinitions(program);
        // Second pass: type check statements
        for (const statement of program.body) {
            this.checkStatement(statement);
        }
        return {
            errors: this.errors,
            warnings: this.warnings,
        };
    }
    /**
     * Get the symbol table for language server completions
     * @returns The symbol table with all defined types
     */
    getSymbolTable() {
        return this.symbolTable;
    }
    collectTypeDefinitions(program) {
        for (const statement of program.body) {
            if (statement.type === 'InterfaceDeclaration') {
                this.collectInterface(statement);
            }
            else if (statement.type === 'TypeAlias') {
                this.collectTypeAlias(statement);
            }
            else if (statement.type === 'ClassDeclaration') {
                this.collectClass(statement);
            }
        }
    }
    collectInterface(interfaceDecl) {
        const properties = new Map();
        for (const prop of interfaceDecl.body.properties) {
            const propType = this.resolveTypeNode(prop.typeAnnotation.typeAnnotation);
            properties.set(prop.key.name, {
                type: propType,
                optional: prop.optional,
            });
        }
        const interfaceType = {
            kind: 'interface',
            name: interfaceDecl.name.name,
            properties,
        };
        this.interfaceTable.set(interfaceDecl.name.name, interfaceType);
    }
    collectTypeAlias(typeAlias) {
        const aliasType = this.resolveTypeNode(typeAlias.typeAnnotation.typeAnnotation);
        this.typeAliasTable.set(typeAlias.name.name, aliasType);
    }
    collectClass(classDecl) {
        // Collect class members (properties and methods)
        const properties = new Map();
        for (const member of classDecl.body.body) {
            if (member.type === 'PropertyDefinition') {
                const prop = member;
                const propType = prop.typeAnnotation
                    ? this.resolveTypeNode(prop.typeAnnotation.typeAnnotation)
                    : { kind: 'unknown' };
                properties.set(prop.key.name, {
                    type: propType,
                    optional: false,
                });
            }
            else if (member.type === 'MethodDefinition') {
                const method = member;
                if (method.kind === 'constructor')
                    continue; // Skip constructor
                const returnType = method.value.returnType
                    ? this.resolveTypeNode(method.value.returnType.typeAnnotation)
                    : { kind: 'unknown' };
                // Store method as a function type property
                properties.set(method.key.name, {
                    type: {
                        kind: 'function',
                        name: method.key.name,
                        returnType: returnType,
                    },
                    optional: false,
                });
            }
        }
        // If class extends another class, resolve parent properties
        let baseType;
        if (classDecl.superClass) {
            const parentName = classDecl.superClass.name;
            baseType = this.symbolTable.get(parentName);
        }
        // Register the class type with its members
        const classType = {
            kind: 'class',
            name: classDecl.name.name,
            properties,
            baseType,
        };
        this.symbolTable.set(classDecl.name.name, classType);
    }
    checkStatement(statement) {
        switch (statement.type) {
            case 'VariableDeclaration':
                this.checkVariableDeclaration(statement);
                break;
            case 'FunctionDeclaration':
                this.checkFunctionDeclaration(statement);
                break;
            case 'ClassDeclaration':
                this.checkClassDeclaration(statement);
                break;
            // Add more statement types as needed
        }
    }
    checkVariableDeclaration(varDecl) {
        let declaredType;
        // Get declared type if present
        if (varDecl.typeAnnotation) {
            const annotation = varDecl.typeAnnotation;
            declaredType = this.resolveTypeNode(annotation.typeAnnotation);
        }
        // Get inferred type from initializer
        let inferredType;
        if (varDecl.init) {
            inferredType = this.inferExpressionType(varDecl.init, declaredType);
        }
        // Type checking
        if (declaredType && inferredType) {
            if (!this.isAssignable(inferredType, declaredType)) {
                this.addError(exports.TypeCheckErrorCode.TypeMismatch, `Type '${this.typeToString(inferredType)}' is not assignable to type '${this.typeToString(declaredType)}'`, varDecl.line, varDecl.column);
            }
        }
        // Store variable type in symbol table
        const finalType = declaredType || inferredType;
        if (finalType) {
            if (varDecl.identifier.type === 'Identifier') {
                this.symbolTable.set(varDecl.identifier.name, finalType);
            }
            else {
                this.bindPatternTypes(varDecl.identifier, finalType);
            }
        }
    }
    bindPatternTypes(pattern, type) {
        if (pattern.type === 'Identifier') {
            this.symbolTable.set(pattern.name, type);
            return;
        }
        if (pattern.type === 'ArrayPattern') {
            this.bindArrayPatternTypes(pattern, type);
            return;
        }
        if (pattern.type === 'ObjectPattern') {
            this.bindObjectPatternTypes(pattern, type);
        }
    }
    bindArrayPatternTypes(pattern, type) {
        if (!type.elementType)
            return;
        pattern.elements.forEach(element => {
            if (element) {
                this.bindPatternTypes(element, type.elementType);
            }
        });
    }
    bindObjectPatternTypes(pattern, type) {
        if (!type.properties)
            return;
        for (const prop of pattern.properties) {
            if (prop.type !== 'PropertyPattern')
                continue;
            const keyName = prop.key.type === 'Identifier' ? prop.key.name : String(prop.key.value);
            const propType = type.properties.get(keyName);
            if (propType) {
                this.bindPatternTypes(prop.value, propType.type);
            }
        }
    }
    checkFunctionDeclaration(funcDecl) {
        // Create new scope for function parameters
        const savedSymbols = new Map(this.symbolTable);
        // Add parameters to symbol table
        for (const param of funcDecl.params) {
            if (param.typeAnnotation) {
                const annotation = param.typeAnnotation;
                const paramType = this.resolveTypeNode(annotation.typeAnnotation);
                this.symbolTable.set(param.name.name, paramType);
            }
        }
        // Check function body (simplified - would need full statement checking)
        // For now, just restore symbol table
        this.symbolTable = savedSymbols;
        // Store function type with return type
        const returnTypeAnnotation = funcDecl.returnType;
        const returnType = returnTypeAnnotation
            ? this.resolveTypeNode(returnTypeAnnotation.typeAnnotation)
            : { kind: 'unknown' };
        const functionType = {
            kind: 'function',
            name: funcDecl.name.name,
            returnType: returnType,
        };
        this.symbolTable.set(funcDecl.name.name, functionType);
    }
    checkClassDeclaration(classDecl) {
        // Phase 2: Validate class relationships and property types
        this.validateSuperClass(classDecl);
        this.validateClassPropertyTypes(classDecl);
    }
    validateSuperClass(classDecl) {
        if (!classDecl.superClass)
            return;
        const parentType = this.symbolTable.get(classDecl.superClass.name);
        const interfaceType = this.interfaceTable.get(classDecl.superClass.name);
        if (!parentType && !interfaceType) {
            this.addError(exports.TypeCheckErrorCode.ClassNotFound, `Base class '${classDecl.superClass.name}' not found`, classDecl.line, classDecl.column);
            return;
        }
        if (interfaceType) {
            this.addError(exports.TypeCheckErrorCode.InvalidExtends, `Class '${classDecl.name.name}' can only extend other classes, but '${classDecl.superClass.name}' is an interface`, classDecl.line, classDecl.column);
            return;
        }
        if (parentType && parentType.kind !== 'class') {
            this.addInvalidExtendsError(classDecl, parentType);
            return;
        }
        if (parentType) {
            this.checkCircularInheritance(classDecl.name.name, parentType, classDecl.line, classDecl.column);
        }
    }
    addInvalidExtendsError(classDecl, parentType) {
        const kindDescription = parentType.kind === 'interface' ? 'an interface' : `a ${parentType.kind}`;
        this.addError(exports.TypeCheckErrorCode.InvalidExtends, `Class '${classDecl.name.name}' can only extend other classes, but '${classDecl.superClass.name}' is ${kindDescription}`, classDecl.line, classDecl.column);
    }
    validateClassPropertyTypes(classDecl) {
        for (const member of classDecl.body.body) {
            if (member.type === 'PropertyDefinition') {
                this.validatePropertyDefinition(member);
            }
        }
    }
    validatePropertyDefinition(prop) {
        if (!prop.typeAnnotation || !prop.value)
            return;
        const declaredType = this.resolveTypeNode(prop.typeAnnotation.typeAnnotation);
        const inferredType = this.inferExpressionType(prop.value);
        if (!this.isAssignable(inferredType, declaredType)) {
            this.addError(exports.TypeCheckErrorCode.TypeMismatch, `Type '${this.typeToString(inferredType)}' is not assignable to type '${this.typeToString(declaredType)}' for property '${prop.key.name}'`, prop.line, prop.column);
        }
    }
    /**
     * Check for circular inheritance in class hierarchy
     * Follows fail-fast principle: errors are reported immediately
     */
    checkCircularInheritance(className, currentType, line, column, visited = new Set()) {
        // Check if we've already visited this class in the inheritance chain
        if (visited.has(className)) {
            this.addError(exports.TypeCheckErrorCode.CircularInheritance, `Circular inheritance detected involving class '${className}'`, line, column);
            return;
        }
        visited.add(className);
        // Traverse up the inheritance chain
        if (currentType.kind === 'class' && currentType.baseType) {
            // Direct self-inheritance check
            if (currentType.baseType.name === className) {
                this.addError(exports.TypeCheckErrorCode.CircularInheritance, `Circular inheritance detected: class '${className}' cannot extend itself`, line, column);
                return;
            }
            // Recursive check up the inheritance chain
            this.checkCircularInheritance(className, currentType.baseType, line, column, visited);
        }
    }
    resolveTypeNode(typeNode) {
        switch (typeNode.type) {
            case 'PrimitiveType':
                return this.resolvePrimitiveType(typeNode);
            case 'ArrayType':
                return this.resolveArrayType(typeNode);
            case 'UnionType':
                return this.resolveUnionType(typeNode);
            case 'IntersectionType':
                return this.resolveIntersectionType(typeNode);
            case 'TupleType':
                return this.resolveTupleType(typeNode);
            case 'GenericType':
                return this.resolveGenericType(typeNode);
            case 'UniqueType':
                return this.resolveUniqueType(typeNode);
            case 'LiteralType':
                return this.resolveLiteralType(typeNode);
            case 'Identifier':
                return this.resolveIdentifierType(typeNode);
            default:
                return { kind: 'unknown' };
        }
    }
    resolvePrimitiveType(primitiveType) {
        return { kind: 'primitive', name: this.mapTajikToPrimitive(primitiveType.name) };
    }
    resolveLiteralType(literalType) {
        return { kind: 'literal', value: literalType.value };
    }
    resolveArrayType(arrayType) {
        return {
            kind: 'array',
            elementType: this.resolveTypeNode(arrayType.elementType),
        };
    }
    resolveUnionType(unionType) {
        return {
            kind: 'union',
            types: unionType.types.map(t => this.resolveTypeNode(t)),
        };
    }
    resolveIntersectionType(intersectionType) {
        return {
            kind: 'intersection',
            types: intersectionType.types.map(t => this.resolveTypeNode(t)),
        };
    }
    resolveTupleType(tupleType) {
        return {
            kind: 'tuple',
            types: tupleType.elementTypes.map(t => this.resolveTypeNode(t)),
        };
    }
    resolveGenericType(genericType) {
        const baseName = genericType.name.name;
        const baseType = this.resolveNamedType(baseName);
        // Add type parameters for generic types
        if (genericType.typeParameters && genericType.typeParameters.length > 0) {
            const typeParams = genericType.typeParameters.map(tp => this.resolveTypeNode(tp));
            // For Map and Set, explicitly mark as generic
            if (baseName === 'Map' || baseName === 'Set') {
                return {
                    kind: 'generic',
                    name: baseName,
                    typeParameters: typeParams,
                };
            }
            // For type aliases and other generics, preserve the base type kind and add type parameters
            return {
                ...baseType,
                typeParameters: typeParams,
            };
        }
        return baseType;
    }
    resolveUniqueType(uniqueType) {
        return { kind: 'unique', baseType: this.resolveTypeNode(uniqueType.baseType) };
    }
    resolveIdentifierType(identifierType) {
        return this.resolveNamedType(identifierType.name);
    }
    resolveNamedType(typeName) {
        // Check if it's a known interface or type alias
        const interfaceType = this.interfaceTable.get(typeName);
        if (interfaceType) {
            return interfaceType;
        }
        const aliasType = this.typeAliasTable.get(typeName);
        if (aliasType) {
            return aliasType;
        }
        // Check if it's a class type
        const classType = this.symbolTable.get(typeName);
        if (classType && classType.kind === 'class') {
            return classType;
        }
        // Unknown type
        return { kind: 'unknown', name: typeName };
    }
    mapTajikToPrimitive(tajikType) {
        switch (tajikType) {
            case 'сатр':
                return 'string';
            case 'рақам':
                return 'number';
            case 'мантиқӣ':
                return 'boolean';
            case 'холӣ':
                return 'null';
            case 'беқимат':
                return 'undefined';
            case 'ҳар':
                return 'any';
            case 'ношинос':
                return 'unknown';
            case 'абадан':
                return 'never';
            case 'беджавоб':
                return 'void';
            case 'объект':
                return 'object';
            default:
                return 'unknown';
        }
    }
    inferExpressionType(expression, targetType) {
        switch (expression.type) {
            case 'Literal':
                return this.inferLiteralType(expression);
            case 'Identifier':
                return this.inferIdentifierType(expression);
            case 'ArrayExpression':
                return this.inferArrayExpressionType(expression, targetType);
            case 'ObjectExpression':
                return this.inferObjectType(expression, targetType);
            case 'CallExpression':
                return this.inferCallType(expression);
            case 'NewExpression':
                return this.inferNewExpressionType(expression);
            default:
                return { kind: 'unknown' };
        }
    }
    inferLiteralType(literal) {
        if (typeof literal.value === 'string') {
            // Return as literal type for better type inference
            return { kind: 'literal', value: literal.value };
        }
        else if (typeof literal.value === 'number') {
            // Return as literal type for better type inference
            return { kind: 'literal', value: literal.value };
        }
        else if (typeof literal.value === 'boolean') {
            // Return as literal type for better type inference
            return { kind: 'literal', value: literal.value };
        }
        else if (literal.value === null) {
            return { kind: 'primitive', name: 'null' };
        }
        return { kind: 'unknown' };
    }
    inferIdentifierType(identifier) {
        return this.symbolTable.get(identifier.name) || { kind: 'unknown' };
    }
    inferTupleTypeFromTarget(arrayExpr, targetTypes) {
        const inferredTypes = [];
        for (let i = 0; i < arrayExpr.elements.length; i++) {
            const element = arrayExpr.elements[i];
            const targetElementType = targetTypes[i] || { kind: 'unknown' };
            const inferredType = this.inferExpressionType(element, targetElementType);
            inferredTypes.push(inferredType);
        }
        return { kind: 'tuple', types: inferredTypes };
    }
    inferArrayElementType(arrayExpr) {
        if (arrayExpr.elements.length === 0) {
            return { kind: 'array', elementType: { kind: 'unknown' } };
        }
        const elementTypes = arrayExpr.elements.map((element) => this.inferExpressionType(element));
        const firstType = elementTypes[0];
        const baseType = this.getBaseType(firstType);
        // Check if all elements have the same base type
        const allSameBase = elementTypes.every((t) => {
            const tBase = this.getBaseType(t);
            return this.isExactMatch(tBase, baseType);
        });
        if (allSameBase) {
            return { kind: 'array', elementType: baseType };
        }
        // Mixed types: return as array of union type
        const unionType = { kind: 'union', types: elementTypes };
        return { kind: 'array', elementType: unionType };
    }
    inferArrayExpressionType(arrayExpr, targetType) {
        // If target type is a tuple, use bidirectional inference
        if (targetType && targetType.kind === 'tuple' && targetType.types) {
            return this.inferTupleTypeFromTarget(arrayExpr, targetType.types);
        }
        // If target is an array, infer array element type
        if (targetType && targetType.kind === 'array') {
            const elementType = targetType.elementType || { kind: 'unknown' };
            arrayExpr.elements.forEach((element) => this.inferExpressionType(element, elementType));
            return { kind: 'array', elementType };
        }
        // If target is an interface/object with numeric indices (simulating tuple)
        if (targetType && (targetType.kind === 'interface' || targetType.kind === 'object')) {
            const inferredTypes = arrayExpr.elements.map(element => this.inferExpressionType(element));
            return { kind: 'tuple', types: inferredTypes };
        }
        // Default: infer array type from elements
        return this.inferArrayElementType(arrayExpr);
    }
    /**
     * Get the base type of a type, converting literals to their base primitives
     * E.g., { kind: 'literal', value: 5 } -> { kind: 'primitive', name: 'number' }
     */
    getBaseType(type) {
        if (type.kind === 'literal') {
            const baseTypeName = typeof type.value === 'string'
                ? 'string'
                : typeof type.value === 'number'
                    ? 'number'
                    : typeof type.value === 'boolean'
                        ? 'boolean'
                        : 'null';
            return { kind: 'primitive', name: baseTypeName };
        }
        return type;
    }
    inferObjectType(objExpr, targetType) {
        if (targetType && targetType.kind === 'interface') {
            return targetType;
        }
        if (targetType &&
            (targetType.kind === 'intersection' || (targetType.kind === 'unknown' && targetType.name))) {
            return { kind: 'object', properties: this.extractObjectProperties(objExpr) };
        }
        return { kind: 'object', properties: this.extractObjectProperties(objExpr) };
    }
    extractObjectProperties(objExpr) {
        const properties = new Map();
        if (objExpr.properties) {
            for (const prop of objExpr.properties) {
                if (prop.key && prop.value) {
                    const keyName = prop.key.type === 'Identifier'
                        ? prop.key.name
                        : String(prop.key.value);
                    const valueType = this.inferExpressionType(prop.value);
                    properties.set(keyName, {
                        type: valueType,
                        optional: false,
                    });
                }
            }
        }
        return properties;
    }
    inferCallType(callExpr) {
        if (callExpr.callee && callExpr.callee.type === 'Identifier') {
            const functionName = callExpr.callee.name;
            const functionType = this.symbolTable.get(functionName);
            if (functionType && functionType.kind === 'function' && functionType.returnType) {
                return functionType.returnType;
            }
        }
        return { kind: 'unknown' };
    }
    inferNewExpressionType(newExpr) {
        if (newExpr.callee && newExpr.callee.type === 'Identifier') {
            const className = newExpr.callee.name;
            // Handle built-in generic types: Map and Set
            if (className === 'Map') {
                return {
                    kind: 'generic',
                    name: 'Map',
                    typeParameters: [
                        { kind: 'primitive', name: 'any' },
                        { kind: 'primitive', name: 'any' },
                    ],
                };
            }
            if (className === 'Set') {
                return {
                    kind: 'generic',
                    name: 'Set',
                    typeParameters: [{ kind: 'primitive', name: 'any' }],
                };
            }
            const classType = this.symbolTable.get(className);
            if (classType && classType.kind === 'class') {
                // Return the actual class type with all its properties and baseType
                return classType;
            }
        }
        return { kind: 'unknown' };
    }
    isLiteralAssignableToPrimitive(source, target) {
        if (source.kind !== 'literal' || target.kind !== 'primitive') {
            return false;
        }
        const baseType = typeof source.value === 'string'
            ? 'string'
            : typeof source.value === 'number'
                ? 'number'
                : typeof source.value === 'boolean'
                    ? 'boolean'
                    : 'null';
        return baseType === target.name;
    }
    isStandardTypeAssignable(source, target) {
        return (this.isArrayAssignable(source, target) ||
            this.isTupleAssignable(source, target) ||
            this.isArrayToTupleAssignable(source, target) ||
            this.isUnionAssignable(source, target) ||
            this.isIntersectionAssignable(source, target));
    }
    isStructuralTypeAssignable(source, target) {
        return (this.isInterfaceAssignable(source, target) ||
            this.isObjectAssignable(source, target) ||
            this.isClassAssignable(source, target) ||
            this.isUniqueAssignable(source, target));
    }
    isSpecialCaseAssignable(source, target) {
        // Tuples are assignable to interfaces (for interface-based tuples)
        if (source.kind === 'tuple' && target.kind === 'interface') {
            return true;
        }
        // Allow assignment to unknown types with names (unresolved type references)
        if (target.kind === 'unknown' && target.name) {
            if (source.kind === 'object' || source.kind === 'interface' || source.kind === 'tuple') {
                return true;
            }
        }
        return false;
    }
    isAssignable(source, target) {
        if (this.isExactMatch(source, target)) {
            return true;
        }
        // Anything is assignable to 'any' type
        if (target.kind === 'primitive' && target.name === 'any') {
            return true;
        }
        if (this.isLiteralAssignableToPrimitive(source, target)) {
            return true;
        }
        if (this.isStandardTypeAssignable(source, target)) {
            return true;
        }
        if (this.isStructuralTypeAssignable(source, target)) {
            return true;
        }
        return this.isSpecialCaseAssignable(source, target);
    }
    isExactMatch(source, target) {
        // Check for literal type match
        if (source.kind === 'literal' && target.kind === 'literal') {
            return source.value === target.value;
        }
        // Check for generic type match (Map<K,V>, Set<T>)
        if (source.kind === 'generic' && target.kind === 'generic') {
            if (source.name !== target.name) {
                return false;
            }
            if (!source.typeParameters || !target.typeParameters) {
                return source.typeParameters === target.typeParameters;
            }
            if (source.typeParameters.length !== target.typeParameters.length) {
                return false;
            }
            // For now, we consider generics with 'any' type parameters compatible
            // Full variance checking can be added later
            return true;
        }
        // Check for primitive type match
        return source.kind === target.kind && source.name === target.name;
    }
    isArrayAssignable(source, target) {
        if (source.kind !== 'array' || target.kind !== 'array') {
            return false;
        }
        return source.elementType && target.elementType
            ? this.isAssignable(source.elementType, target.elementType)
            : false;
    }
    isTupleAssignable(source, target) {
        if (source.kind !== 'tuple' || target.kind !== 'tuple') {
            return false;
        }
        if (!source.types || !target.types || source.types.length !== target.types.length) {
            return false;
        }
        return source.types.every((sourceType, index) => this.isAssignable(sourceType, target.types[index]));
    }
    isArrayToTupleAssignable(source, target) {
        if (source.kind !== 'array' || target.kind !== 'tuple') {
            return false;
        }
        // An array is assignable to a tuple if the array element type
        // is assignable to all tuple element types
        if (!target.types || target.types.length === 0) {
            return true; // Empty tuple accepts any array
        }
        const sourceElementType = source.elementType || { kind: 'unknown' };
        // Check if source element type is compatible with all tuple positions
        return target.types.every(tupleElementType => this.isAssignable(sourceElementType, tupleElementType));
    }
    isUnionAssignable(source, target) {
        if (target.kind === 'union' && target.types) {
            return target.types.some(t => this.isAssignable(source, t));
        }
        if (source.kind === 'union' && source.types) {
            return source.types.every(t => this.isAssignable(t, target));
        }
        return false;
    }
    isIntersectionAssignable(source, target) {
        if (target.kind === 'intersection' && target.types) {
            return target.types.every(t => this.isAssignable(source, t));
        }
        if (source.kind === 'intersection' && source.types) {
            return source.types.some(t => this.isAssignable(t, target));
        }
        return false;
    }
    isInterfaceAssignable(source, target) {
        if (target.kind !== 'interface') {
            return false;
        }
        if (source.kind === 'interface' || source.kind === 'object') {
            return this.isStructurallyCompatible(source, target);
        }
        return false;
    }
    isObjectAssignable(source, target) {
        if (target.kind !== 'object') {
            return false;
        }
        if (source.kind === 'object') {
            return this.isStructurallyCompatible(source, target);
        }
        return false;
    }
    isClassAssignable(source, target) {
        return source.kind === 'class' && target.kind === 'class' && source.name === target.name;
    }
    isUniqueAssignable(source, target) {
        if (source.kind === 'unique' && target.kind === 'unique') {
            return this.isAssignable(source.baseType, target.baseType);
        }
        if (source.kind === 'unique' || target.kind === 'unique') {
            return false;
        }
        return false;
    }
    typeToString(type) {
        switch (type.kind) {
            case 'primitive':
                return this.mapPrimitiveToTajik(type.name || 'unknown');
            case 'array':
                return `${this.typeToString(type.elementType)}[]`;
            case 'union':
                return type.types.map(t => this.typeToString(t)).join(' | ');
            case 'intersection':
                return type.types.map(t => this.typeToString(t)).join(' & ');
            case 'interface':
                return type.name || 'interface';
            case 'tuple':
                return `[${type.types.map(t => this.typeToString(t)).join(', ')}]`;
            case 'literal':
                return typeof type.value === 'string' ? `"${type.value}"` : String(type.value);
            case 'class':
                return type.name || 'class';
            case 'unique':
                return `беназир ${this.typeToString(type.baseType)}`;
            default:
                return type.name || 'unknown';
        }
    }
    isStructurallyCompatible(source, target) {
        if (!source.properties || !target.properties) {
            return false;
        }
        // Check if source has all required properties of target
        for (const [propName, targetProp] of target.properties) {
            const sourceProp = source.properties.get(propName);
            // Required property missing
            if (!sourceProp && !targetProp.optional) {
                return false;
            }
            // Property exists, check type compatibility
            if (sourceProp && !this.isAssignable(sourceProp.type, targetProp.type)) {
                return false;
            }
        }
        return true;
    }
    mapPrimitiveToTajik(primitiveType) {
        switch (primitiveType) {
            case 'string':
                return 'сатр';
            case 'number':
                return 'рақам';
            case 'boolean':
                return 'мантиқӣ';
            case 'null':
                return 'холӣ';
            default:
                return primitiveType;
        }
    }
    addError(code, message, line, column) {
        this.errors.push({
            code,
            message,
            line,
            column,
            snippet: this.getSnippet(line),
            severity: 'error',
        });
    }
    addWarning(code, message, line, column) {
        this.warnings.push({
            code,
            message,
            line,
            column,
            snippet: this.getSnippet(line),
            severity: 'warning',
        });
    }
}
exports.TypeChecker = TypeChecker;
//# sourceMappingURL=type-checker.js.map