"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.captureJsonResponse = captureJsonResponse;
exports.resolveComparatorJarPath = resolveComparatorJarPath;
exports.buildComparatorArgs = buildComparatorArgs;
exports.compareCapturedResponse = compareCapturedResponse;
const promises_1 = __importDefault(require("node:fs/promises"));
const node_path_1 = __importDefault(require("node:path"));
const node_child_process_1 = require("node:child_process");
const node_util_1 = require("node:util");
const resources_1 = require("./resources");
const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
async function captureJsonResponse(page, match, trigger) {
    const waitForResponse = page.waitForResponse((response) => matchesResponse(response, match));
    const [response] = await Promise.all([waitForResponse, Promise.resolve().then(trigger)]);
    return readCapturedJsonResponse(response, match);
}
function resolveComparatorJarPath(config, packageRoot = resolvePackageRoot()) {
    const settings = config.jsonComparison;
    if (settings?.jarPath) {
        return node_path_1.default.resolve(packageRoot, settings.jarPath);
    }
    return node_path_1.default.resolve(packageRoot, 'tools', 'bdd-comparator.jar');
}
function buildComparatorArgs(options) {
    const args = [
        'json',
        '--expected', options.expectedPath,
        '--actual', options.actualPath,
        '--output', options.output ?? 'json'
    ];
    appendOptionalArg(args, '--parent-object', options.parentObject);
    appendOptionalArg(args, '--primary-key', options.primaryKey);
    appendOptionalArg(args, '--ignore', options.ignore);
    appendOptionalArg(args, '--fields', options.fields);
    appendOptionalArg(args, '--threshold-config', options.thresholdConfig);
    if (options.healthCheck) {
        args.push('--health-check');
    }
    appendOptionalArg(args, '--fail-on-extra', options.failOnExtra);
    return args;
}
async function compareCapturedResponse(options, dependencies = {}) {
    if (!options.capturedResponse) {
        throw new Error('No captured response is available for comparison.');
    }
    const cwd = options.cwd ?? process.cwd();
    const expectedPath = (0, resources_1.resolveExpectedResourcePath)(options.config, options.featureName, options.expectedResult, cwd);
    const actualPath = (0, resources_1.resolveActualResourcePath)(options.config, options.featureName, options.expectedResult, cwd);
    await ensureExpectedFileExists(expectedPath, options.expectedResult);
    await promises_1.default.mkdir(node_path_1.default.dirname(actualPath), { recursive: true });
    await promises_1.default.writeFile(actualPath, `${JSON.stringify(options.capturedResponse.body, null, 2)}\n`, 'utf8');
    const jarPath = resolveComparatorJarPath(options.config, dependencies.packageRoot);
    const args = buildComparatorArgs({
        expectedPath,
        actualPath,
        output: options.config.jsonComparison?.output ?? 'json',
        parentObject: options.parentObject,
        primaryKey: options.primaryKey,
        ignore: options.ignore,
        fields: options.fields,
        thresholdConfig: options.thresholdConfig,
        healthCheck: options.healthCheck,
        failOnExtra: options.failOnExtra
    });
    const runComparator = dependencies.runComparator ?? defaultRunComparator;
    const result = await runComparator(jarPath, args);
    if (result.exitCode === 0) {
        await promises_1.default.rm(actualPath, { force: true });
        return;
    }
    throw buildComparatorFailure(options.expectedResult, result);
}
async function readCapturedJsonResponse(response, match) {
    try {
        return {
            body: await response.json(),
            status: safeCall(() => response.status?.()),
            url: safeCall(() => response.url?.())
        };
    }
    catch {
        const text = await safeText(response);
        throw new Error(`Expected a JSON response matching "${match}" but the body was not valid JSON.` +
            (text ? ` Response text: ${text}` : ''));
    }
}
function matchesResponse(response, match) {
    try {
        const url = response.url();
        return typeof url === 'string' && url.includes(match);
    }
    catch {
        return false;
    }
}
function appendOptionalArg(args, flag, value) {
    if (value && value.trim()) {
        args.push(flag, value);
    }
}
async function ensureExpectedFileExists(expectedPath, expectedResult) {
    try {
        await promises_1.default.access(expectedPath);
    }
    catch {
        throw new Error(`Expected result "${expectedResult}" was not found at ${expectedPath}.`);
    }
}
async function defaultRunComparator(jarPath, args) {
    try {
        const { stdout, stderr } = await execFileAsync('java', ['-jar', jarPath, ...args], {
            windowsHide: true
        });
        return { exitCode: 0, stdout, stderr };
    }
    catch (error) {
        const failure = error;
        return {
            exitCode: typeof failure.code === 'number' ? failure.code : 5,
            stdout: failure.stdout ?? '',
            stderr: failure.stderr ?? failure.message
        };
    }
}
function buildComparatorFailure(expectedResult, result) {
    const details = [result.stderr, result.stdout].filter(Boolean).join('\n').trim();
    return new Error(`Comparator failed for expected result "${expectedResult}".` +
        ` Exit code: ${result.exitCode}.` +
        (details ? ` Output: ${details}` : ''));
}
function resolvePackageRoot() {
    return node_path_1.default.resolve(__dirname, '..', '..');
}
function safeCall(fn) {
    try {
        return fn();
    }
    catch {
        return undefined;
    }
}
async function safeText(response) {
    try {
        return await response.text();
    }
    catch {
        return '';
    }
}
