/**
 * Tests for module import resolution, type alias resolution,
 * and identifier renaming with csharpName in IR conversion
 */
import { describe, it } from "mocha";
import { expect } from "chai";
import { buildIrModule, createTestProgram, BindingRegistry, } from "./helpers.js";
describe("Binding Resolution in IR", () => {
    describe("Module Import Resolution", () => {
        it("should mark module imports with resolved CLR types in import extraction", () => {
            const source = `
        import { readFileSync } from "fs";
      `;
            const bindings = new BindingRegistry();
            bindings.addBindings("/test/node.json", {
                bindings: {
                    fs: {
                        kind: "module",
                        assembly: "Tsonic.NodeApi",
                        type: "Tsonic.NodeApi.fs",
                    },
                },
            });
            const { testProgram, ctx, options } = createTestProgram(source, bindings);
            const sourceFile = testProgram.sourceFiles[0];
            if (!sourceFile)
                throw new Error("Failed to create source file");
            const result = buildIrModule(sourceFile, testProgram, options, ctx);
            // May fail due to unresolved import, but we can still check the IR structure
            if (!result.ok) {
                // This is expected for bound modules that don't actually exist
                return;
            }
            const module = result.value;
            expect(module.imports).to.have.lengthOf(1);
            const fsImport = module.imports[0];
            if (!fsImport)
                throw new Error("No import found");
            expect(fsImport.source).to.equal("fs");
            expect(fsImport.resolvedClrType).to.equal("Tsonic.NodeApi.fs");
            expect(fsImport.resolvedAssembly).to.equal("Tsonic.NodeApi");
        });
        it("should handle imports without bindings", () => {
            const source = `
        export const x = 42;
      `;
            const bindings = new BindingRegistry();
            // No bindings added
            const { testProgram, ctx, options } = createTestProgram(source, bindings);
            const sourceFile = testProgram.sourceFiles[0];
            if (!sourceFile)
                throw new Error("Failed to create source file");
            const result = buildIrModule(sourceFile, testProgram, options, ctx);
            expect(result.ok).to.equal(true);
            if (!result.ok)
                return;
            const module = result.value;
            // No imports in this test
            expect(module.imports).to.have.lengthOf(0);
        });
    });
    describe("Type Alias Resolution", () => {
        it("should follow type-alias-to-interface references in implements clauses", () => {
            const source = `
        export interface IFoo_1<T> {
          bar(): void;
        }

        // Facade-style ergonomic alias (tsbindgen does this frequently).
        export type IFoo<T> = IFoo_1<T>;

        export class C implements IFoo<number> {
          bar(): void {}
        }
      `;
            const { testProgram, ctx, options } = createTestProgram(source);
            const sourceFile = testProgram.sourceFiles[0];
            if (!sourceFile)
                throw new Error("Failed to create source file");
            const result = buildIrModule(sourceFile, testProgram, options, ctx);
            expect(result.ok).to.equal(true);
            if (!result.ok)
                return;
            const module = result.value;
            const classDecl = module.body.find((s) => s.kind === "classDeclaration");
            expect(classDecl?.kind).to.equal("classDeclaration");
            if (!classDecl || classDecl.kind !== "classDeclaration")
                return;
            expect(classDecl.implements).to.have.length.greaterThan(0);
            const impl = classDecl.implements[0];
            expect(impl?.kind).to.equal("referenceType");
            if (!impl || impl.kind !== "referenceType")
                return;
            // Without alias-following, this becomes IFoo (type alias) and the class
            // does not emit any C# interface implementation.
            expect(impl.name).to.equal("IFoo_1");
        });
    });
    describe("Identifier Renaming with csharpName", () => {
        it("should use csharpName when provided in binding", () => {
            const source = `
        export function test() {
          console.log("hello");
        }
      `;
            const bindings = new BindingRegistry();
            bindings.addBindings("/test/runtime.json", {
                bindings: {
                    console: {
                        kind: "global",
                        assembly: "System",
                        type: "System.Console",
                        csharpName: "Console",
                    },
                },
            });
            const { testProgram, ctx, options } = createTestProgram(source, bindings);
            const sourceFile = testProgram.sourceFiles[0];
            if (!sourceFile)
                throw new Error("Failed to create source file");
            const result = buildIrModule(sourceFile, testProgram, options, ctx);
            expect(result.ok).to.equal(true);
            if (!result.ok)
                return;
            const module = result.value;
            const funcDecl = module.body[0];
            expect(funcDecl?.kind).to.equal("functionDeclaration");
            if (funcDecl?.kind !== "functionDeclaration")
                return;
            // Find the console.log call
            const exprStmt = funcDecl.body.statements[0];
            expect(exprStmt?.kind).to.equal("expressionStatement");
            if (exprStmt?.kind !== "expressionStatement")
                return;
            const callExpr = exprStmt.expression;
            expect(callExpr.kind).to.equal("call");
            if (callExpr.kind !== "call")
                return;
            const memberExpr = callExpr.callee;
            expect(memberExpr.kind).to.equal("memberAccess");
            if (memberExpr.kind !== "memberAccess")
                return;
            // Check that the identifier has csharpName set
            const consoleExpr = memberExpr.object;
            expect(consoleExpr.kind).to.equal("identifier");
            expect(consoleExpr.name).to.equal("console");
            expect(consoleExpr.csharpName).to.equal("Console");
            expect(consoleExpr.resolvedClrType).to.equal("System.Console");
            expect(consoleExpr.resolvedAssembly).to.equal("System");
        });
        it("should work without csharpName (use resolvedClrType)", () => {
            const source = `
        export function test() {
          Math.sqrt(4);
        }
      `;
            const bindings = new BindingRegistry();
            bindings.addBindings("/test/runtime.json", {
                bindings: {
                    Math: {
                        kind: "global",
                        assembly: "Tsonic.Runtime",
                        type: "Tsonic.Runtime.Math",
                        // No csharpName specified
                    },
                },
            });
            const { testProgram, ctx, options } = createTestProgram(source, bindings);
            const sourceFile = testProgram.sourceFiles[0];
            if (!sourceFile)
                throw new Error("Failed to create source file");
            const result = buildIrModule(sourceFile, testProgram, options, ctx);
            expect(result.ok).to.equal(true);
            if (!result.ok)
                return;
            const module = result.value;
            const funcDecl = module.body[0];
            if (funcDecl?.kind !== "functionDeclaration")
                return;
            const exprStmt = funcDecl.body.statements[0];
            if (exprStmt?.kind !== "expressionStatement")
                return;
            const callExpr = exprStmt.expression;
            if (callExpr.kind !== "call")
                return;
            const memberExpr = callExpr.callee;
            if (memberExpr.kind !== "memberAccess")
                return;
            const mathExpr = memberExpr.object;
            expect(mathExpr.kind).to.equal("identifier");
            expect(mathExpr.name).to.equal("Math");
            expect(mathExpr.csharpName).to.equal(undefined); // No csharpName
            expect(mathExpr.resolvedClrType).to.equal("Tsonic.Runtime.Math");
            expect(mathExpr.resolvedAssembly).to.equal("Tsonic.Runtime");
        });
    });
});
//# sourceMappingURL=module-import-type-alias-renaming.test.js.map