/**
 * Shared test helpers for utility type expansion tests
 *
 * Covers the safety guarantees per Alice's review:
 * 1. Index signatures block expansion (never drop members)
 * 2. Symbol/computed keys block expansion (never drop members)
 * 3. Explicit undefined is preserved (not stripped)
 */
import { describe, it } from "mocha";
import { expect } from "chai";
import * as ts from "typescript";
import { expandUtilityType, expandConditionalUtilityType, expandRecordType, } from "../utility-types.js";
import { createBinding } from "../../../../binding/index.js";
/**
 * Assert value is not null/undefined and return it typed as non-null.
 * Throws if value is null or undefined.
 */
function assertDefined(value, msg) {
    if (value === null || value === undefined) {
        throw new Error(msg ?? "Expected value to be defined");
    }
    return value;
}
/**
 * Helper to create a TypeScript program from source code
 */
const createTestProgram = (source, fileName = "test.ts") => {
    const compilerOptions = {
        target: ts.ScriptTarget.ES2022,
        module: ts.ModuleKind.NodeNext,
        strict: true,
        noEmit: true,
    };
    const host = ts.createCompilerHost(compilerOptions);
    const originalGetSourceFile = host.getSourceFile;
    const originalFileExists = host.fileExists;
    const originalReadFile = host.readFile;
    host.getSourceFile = (name, languageVersionOrOptions, onError, shouldCreateNewSourceFile) => {
        if (name === fileName) {
            return ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
        }
        return originalGetSourceFile.call(host, name, languageVersionOrOptions, onError, shouldCreateNewSourceFile);
    };
    host.fileExists = (name) => name === fileName || originalFileExists.call(host, name);
    host.readFile = (name) => name === fileName ? source : originalReadFile.call(host, name);
    const program = ts.createProgram([fileName], compilerOptions, host);
    const sourceFile = assertDefined(program.getSourceFile(fileName), `Source file ${fileName} not found`);
    const checker = program.getTypeChecker();
    const binding = createBinding(checker);
    return { program, checker, sourceFile, binding };
};
/**
 * Helper to find a type alias by name and get its type reference node
 */
const findTypeAliasReference = (sourceFile, aliasName) => {
    let result = null;
    const visitor = (node) => {
        if (ts.isTypeAliasDeclaration(node) && node.name.text === aliasName) {
            if (ts.isTypeReferenceNode(node.type)) {
                result = node.type;
            }
        }
        ts.forEachChild(node, visitor);
    };
    ts.forEachChild(sourceFile, visitor);
    return result;
};
/**
 * Stub convertType for testing - just returns the type name
 */
const stubConvertType = (node, _binding) => {
    if (ts.isTypeReferenceNode(node)) {
        const name = ts.isIdentifier(node.typeName)
            ? node.typeName.text
            : node.typeName.getText();
        return { kind: "referenceType", name, typeArguments: [] };
    }
    if (node.kind === ts.SyntaxKind.StringKeyword) {
        return { kind: "primitiveType", name: "string" };
    }
    if (node.kind === ts.SyntaxKind.NumberKeyword) {
        return { kind: "primitiveType", name: "number" };
    }
    if (node.kind === ts.SyntaxKind.BooleanKeyword) {
        return { kind: "primitiveType", name: "boolean" };
    }
    if (node.kind === ts.SyntaxKind.BigIntKeyword) {
        return {
            kind: "referenceType",
            name: "BigInteger",
            typeArguments: [],
            resolvedClrType: "System.Numerics.BigInteger",
        };
    }
    if (node.kind === ts.SyntaxKind.UndefinedKeyword) {
        return { kind: "primitiveType", name: "undefined" };
    }
    if (node.kind === ts.SyntaxKind.NullKeyword) {
        return { kind: "primitiveType", name: "null" };
    }
    if (node.kind === ts.SyntaxKind.NeverKeyword) {
        return { kind: "neverType" };
    }
    // Handle literal type nodes (e.g., "a", 1, null, undefined)
    if (ts.isLiteralTypeNode(node)) {
        const literal = node.literal;
        if (ts.isStringLiteral(literal)) {
            return { kind: "literalType", value: literal.text };
        }
        if (ts.isNumericLiteral(literal)) {
            return { kind: "literalType", value: Number(literal.text) };
        }
        // null can appear as LiteralTypeNode in type positions
        if (literal.kind === ts.SyntaxKind.NullKeyword) {
            return { kind: "primitiveType", name: "null" };
        }
    }
    if (ts.isUnionTypeNode(node)) {
        return {
            kind: "unionType",
            types: node.types.map((t) => stubConvertType(t, _binding)),
        };
    }
    return { kind: "anyType" };
};
export { describe, it, expect, ts, expandUtilityType, expandConditionalUtilityType, expandRecordType, assertDefined, createTestProgram, findTypeAliasReference, stubConvertType, };
//# sourceMappingURL=helpers.js.map