"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateNegativeTestsFile = void 0;
const promises_1 = __importDefault(require("node:fs/promises"));
const node_path_1 = __importDefault(require("node:path"));
const node_fs_1 = require("node:fs");
const ts_morph_1 = require("ts-morph");
const fixWithEslint_1 = require("../format/fixWithEslint");
const collectInvalidInputTestCases_1 = require("../strategies/invalidInput/collectInvalidInputTestCases");
const formatWithPrettier_1 = require("../format/formatWithPrettier");
const renderGeneratedSpecFile_1 = require("./renderGeneratedSpecFile");
const INVALID_INPUT_STRATEGY_SLUG = 'invalid-input';
async function generateNegativeTestsFile(args) {
    const inputDir = node_path_1.default.dirname(args.inputTestFilePath);
    const resolvedOutputDir = args.outputDir
        ? node_path_1.default.isAbsolute(args.outputDir)
            ? args.outputDir
            : node_path_1.default.resolve(inputDir, args.outputDir)
        : inputDir;
    await promises_1.default.mkdir(resolvedOutputDir, { recursive: true });
    const baseName = deriveBaseName(args.inputTestFilePath);
    const outputFilePath = await resolveIncrementingOutputPath(resolvedOutputDir, `${baseName}-${INVALID_INPUT_STRATEGY_SLUG}-generated.spec.ts`);
    const tsConfigPath = args.tsConfigPath ?? (await findNearestTsConfig(args.inputTestFilePath));
    if (!tsConfigPath) {
        throw new Error(`Unable to locate tsconfig.json (use --project). Started from: ${args.inputTestFilePath}`);
    }
    const project = new ts_morph_1.Project({ tsConfigFilePath: tsConfigPath });
    const sourceFile = project.addSourceFileAtPathIfExists(args.inputTestFilePath);
    if (!sourceFile) {
        throw new Error(`Input test file not found: ${args.inputTestFilePath}`);
    }
    const testCases = await (0, collectInvalidInputTestCases_1.collectInvalidInputTestCases)({ project, sourceFile });
    const rendered = (0, renderGeneratedSpecFile_1.renderGeneratedSpecFile)({
        sourceFile,
        testCases,
        strategySlug: INVALID_INPUT_STRATEGY_SLUG,
    });
    await promises_1.default.writeFile(outputFilePath, rendered, 'utf8');
    // Apply autofixes first (eslint), then normalize formatting (prettier).
    await (0, fixWithEslint_1.fixWithEslint)({
        filePath: outputFilePath,
        startSearchFrom: args.inputTestFilePath,
    });
    await (0, formatWithPrettier_1.formatWithPrettier)({
        filePath: outputFilePath,
        startSearchFrom: args.inputTestFilePath,
    });
    return { outputFilePath };
}
exports.generateNegativeTestsFile = generateNegativeTestsFile;
function deriveBaseName(inputPath) {
    const fileName = node_path_1.default.basename(inputPath);
    if (fileName.endsWith('.spec.ts')) {
        return fileName.slice(0, -'.spec.ts'.length);
    }
    const parsed = node_path_1.default.parse(fileName);
    return parsed.name;
}
async function resolveIncrementingOutputPath(dir, fileName) {
    const initial = node_path_1.default.join(dir, fileName);
    if (!(0, node_fs_1.existsSync)(initial))
        return initial;
    const specSuffix = '.spec.ts';
    if (fileName.endsWith(specSuffix)) {
        const stem = fileName.slice(0, -specSuffix.length);
        for (let i = 1; i < 10000; i++) {
            const candidate = node_path_1.default.join(dir, `${stem}-${i}${specSuffix}`);
            if (!(0, node_fs_1.existsSync)(candidate))
                return candidate;
        }
        throw new Error(`Unable to find an available output filename in: ${dir}`);
    }
    const ext = node_path_1.default.extname(fileName);
    const withoutExt = fileName.slice(0, -ext.length);
    for (let i = 1; i < 10000; i++) {
        const candidate = node_path_1.default.join(dir, `${withoutExt}-${i}${ext}`);
        if (!(0, node_fs_1.existsSync)(candidate))
            return candidate;
    }
    throw new Error(`Unable to find an available output filename in: ${dir}`);
}
async function findNearestTsConfig(startFile) {
    let current = node_path_1.default.dirname(startFile);
    // Walk upward until filesystem root.
    // eslint-disable-next-line no-constant-condition
    while (true) {
        const candidate = node_path_1.default.join(current, 'tsconfig.json');
        if ((0, node_fs_1.existsSync)(candidate))
            return candidate;
        const parent = node_path_1.default.dirname(current);
        if (parent === current)
            return null;
        current = parent;
    }
}
