import { describe, it } from "mocha";
import { expect } from "chai";
import { buildModuleDependencyGraph } from "./dependency-graph.js";
import { materializeFrontendFixture } from "../testing/filesystem-fixtures.js";
const normalizeSlashes = (value) => value.replace(/\\/g, "/");
const findModuleByFilePath = (modules, filePath) => modules.find((module) => normalizeSlashes(module.filePath) === normalizeSlashes(filePath));
const findEqualCallStatement = (statements) => {
    for (const statement of statements) {
        if (!statement || typeof statement !== "object") {
            continue;
        }
        const candidate = statement;
        if (candidate.kind === "expressionStatement" &&
            candidate.expression?.kind === "call" &&
            candidate.expression.callee?.kind === "memberAccess" &&
            candidate.expression.callee.property === "Equal") {
            return candidate;
        }
        const nestedBlocks = [
            candidate.body,
            candidate.thenStatement,
            candidate.elseStatement,
        ].filter((entry) => entry !== undefined);
        for (const nested of nestedBlocks) {
            const nestedStatements = typeof nested === "object" &&
                nested &&
                nested.kind === "blockStatement"
                ? (nested
                    .statements ?? [])
                : [nested];
            const resolved = findEqualCallStatement(nestedStatements);
            if (resolved) {
                return resolved;
            }
        }
        if (candidate.kind === "blockStatement") {
            const resolved = findEqualCallStatement(candidate.statements ?? []);
            if (resolved) {
                return resolved;
            }
        }
    }
    return undefined;
};
const buildFixtureGraph = (fixtureName) => {
    const fixture = materializeFrontendFixture([
        "fragments/minimal-surfaces/tsonic-js",
        `program/xunit-equal-overload/${fixtureName}`,
    ]);
    const projectRoot = fixture.path("app");
    const entryPath = fixture.path("app/src/index.ts");
    const result = buildModuleDependencyGraph(entryPath, {
        projectRoot,
        sourceRoot: fixture.path("app/src"),
        rootNamespace: "TestApp",
        surface: "@tsonic/js",
    });
    return { fixture, result };
};
const expectGraphSuccess = (result) => {
    expect(result.ok, result.ok
        ? undefined
        : result.error
            .map((diagnostic) => `${diagnostic.code}: ${diagnostic.message}`)
            .join("\n")).to.equal(true);
};
const expectRunEqualCall = (modules) => {
    const module = findModuleByFilePath(modules, "index.ts");
    expect(module).to.not.equal(undefined);
    if (!module) {
        return undefined;
    }
    const runFn = module.body.find((statement) => statement.kind === "functionDeclaration" && statement.name === "run");
    expect(runFn).to.not.equal(undefined);
    if (!runFn) {
        return undefined;
    }
    const callStatement = findEqualCallStatement(runFn.body.statements);
    expect(callStatement).to.not.equal(undefined);
    return callStatement;
};
const formatTypeName = (type) => {
    if (!type) {
        return "missing";
    }
    if (type.kind === "primitiveType" || type.kind === "referenceType") {
        return type.name;
    }
    return type.kind;
};
const expectEqualCallTypes = (callStatement, expected) => {
    const call = callStatement.expression;
    expect(call.kind).to.equal("call");
    if (call.kind !== "call") {
        return;
    }
    expect((call.parameterTypes ?? []).map(formatTypeName)).to.deep.equal(expected);
    expect((call.surfaceParameterTypes ?? []).map(formatTypeName)).to.deep.equal(expected);
};
describe("Dependency Graph", function () {
    this.timeout(60000);
    it("widens generic xunit equality to object in the full dependency graph when later arguments are JsValue", () => {
        const { fixture, result } = buildFixtureGraph("scalar-jsvalue-instance");
        try {
            expectGraphSuccess(result);
            if (!result.ok) {
                return;
            }
            const callStatement = expectRunEqualCall(result.value.modules);
            if (!callStatement) {
                return;
            }
            expectEqualCallTypes(callStatement, ["object", "object"]);
        }
        finally {
            fixture.cleanup();
        }
    });
    it("keeps string equality overloads in the full dependency graph when char aliases flow into string surfaces", () => {
        const { fixture, result } = buildFixtureGraph("char-string-instance");
        try {
            expectGraphSuccess(result);
            if (!result.ok) {
                return;
            }
            const callStatement = expectRunEqualCall(result.value.modules);
            if (!callStatement) {
                return;
            }
            expectEqualCallTypes(callStatement, ["string", "string"]);
        }
        finally {
            fixture.cleanup();
        }
    });
    it("widens generic xunit class equality to object through facade re-exports when later arguments are JsValue", () => {
        const { fixture, result } = buildFixtureGraph("scalar-jsvalue-class-reexport");
        try {
            expectGraphSuccess(result);
            if (!result.ok) {
                return;
            }
            const callStatement = expectRunEqualCall(result.value.modules);
            if (!callStatement) {
                return;
            }
            expectEqualCallTypes(callStatement, ["object", "object"]);
        }
        finally {
            fixture.cleanup();
        }
    });
    it("widens generic numeric equality to object when later arguments are JsValue over real xunit overloads", () => {
        const { fixture, result } = buildFixtureGraph("numeric-jsvalue");
        try {
            expectGraphSuccess(result);
            if (!result.ok) {
                return;
            }
            const callStatement = expectRunEqualCall(result.value.modules);
            if (!callStatement) {
                return;
            }
            expectEqualCallTypes(callStatement, ["object", "object"]);
        }
        finally {
            fixture.cleanup();
        }
    });
    it("preserves explicit JsValue storage as object across callback writes for later xunit equality", () => {
        const { fixture, result } = buildFixtureGraph("callback-jsvalue");
        try {
            expectGraphSuccess(result);
            if (!result.ok) {
                return;
            }
            const callStatement = expectRunEqualCall(result.value.modules);
            if (!callStatement) {
                return;
            }
            expectEqualCallTypes(callStatement, ["object", "object"]);
        }
        finally {
            fixture.cleanup();
        }
    });
    it("keeps string equality class overloads through facade re-exports when char aliases flow into string surfaces", () => {
        const { fixture, result } = buildFixtureGraph("char-string-class-reexport");
        try {
            expectGraphSuccess(result);
            if (!result.ok) {
                return;
            }
            const callStatement = expectRunEqualCall(result.value.modules);
            if (!callStatement) {
                return;
            }
            expectEqualCallTypes(callStatement, ["string", "string"]);
        }
        finally {
            fixture.cleanup();
        }
    });
});
//# sourceMappingURL=xunit-equal-overload-resolution.test.js.map