"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.startCommand = void 0;
exports.startDebugSession = startDebugSession;
exports.assertSupportedTestType = assertSupportedTestType;
const path_1 = __importDefault(require("path"));
const expandSteps_1 = require("@mablhq-internal/runtime/debug/expandSteps");
const mablApi_1 = require("@mablhq-internal/runtime/mablApi");
const debugFailWrapper_1 = require("../util/debugFailWrapper");
const debugUtils_1 = require("../util/debugUtils");
const flowResolver_1 = require("@mablhq-internal/runtime/util/flowResolver");
const browserOptionsBuilder_1 = require("../../../commandUtil/browserOptionsBuilder");
const debugCommands_1 = require("../../debugCommands");
const constants_1 = require("../../../constants");
const constants_2 = require("@mablhq-internal/runtime/core/constants");
const mablApiClientFactory_1 = require("@mablhq-internal/runtime/api/mablApiClientFactory");
const DebugBrowserLauncher_1 = require("@mablhq-internal/runtime/debug/DebugBrowserLauncher");
const debugConstants_1 = require("@mablhq-internal/runtime/debug/debugConstants");
const DebugSession_1 = require("@mablhq-internal/runtime/debug/DebugSession");
const buildDebugSessionState_1 = require("@mablhq-internal/runtime/debug/buildDebugSessionState");
exports.startCommand = (0, debugCommands_1.defineLeaf)({
    command: `start [${constants_1.CommandArgTestId}]`,
    describe: 'Start a debug session for a test',
    builder: (yargs) => {
        (0, browserOptionsBuilder_1.applyBrowserAndEnvOptions)(yargs)
            .positional(constants_1.CommandArgTestId, {
            describe: 'The ID of the test to debug, e.g. abc123-j. Optional when --run-id is given (the test id and most flags are then derived from the run).',
            type: 'string',
        })
            .option(constants_1.CommandArgRunId, {
            describe: 'A test run ID (e.g. abc123-jr) to derive flags from. Sets test-id, url, browser, credentials, environment, locale and timezone from the run; explicit flags override.',
            type: 'string',
        })
            .option(constants_1.CommandArgPort, {
            describe: `Remote debugging port for Chrome. Defaults to ${debugConstants_1.DEFAULT_DEBUG_PORT} so it lines up with the chrome-for-mabl MCP server entry. Pass 0 to let Chrome pick a free port.`,
            type: 'number',
            default: debugConstants_1.DEFAULT_DEBUG_PORT,
        });
    },
    handler: (0, debugFailWrapper_1.debugFailWrapper)(startDebugSession),
});
async function startDebugSession(parsed) {
    const apiClient = await mablApiClientFactory_1.MablApiClientFactory.createApiClient();
    const runId = parsed[constants_1.CommandArgRunId];
    let runDefaults;
    if (runId !== undefined) {
        (0, debugUtils_1.assertTestRunId)(runId);
        runDefaults = await deriveDefaultsFromRun(apiClient, runId);
    }
    const testId = parsed[constants_1.CommandArgTestId] ?? runDefaults?.testId;
    if (!testId) {
        throw new Error('Either <test-id> or --run-id must be provided. Pass a test definition id (e.g. abc123-j) or a test run id (e.g. abc123-jr).');
    }
    const branchName = parsed[constants_2.CommandArgMablBranch] ?? debugConstants_1.DEFAULT_MABL_BRANCH;
    const sessionId = (0, DebugSession_1.generateSessionId)();
    const userDataDir = path_1.default.join((0, DebugSession_1.sessionDir)(sessionId), 'browser');
    const test = await apiClient.getJourney(testId, branchName);
    assertSupportedTestType(test.test_type, testId);
    const topLevelFlows = await (0, flowResolver_1.getJourneyFlowArray)(test, apiClient, branchName);
    const nestedFlows = await (0, flowResolver_1.resolveAllNestedFlowReferences)(topLevelFlows, apiClient, branchName);
    const flows = [...topLevelFlows, ...nestedFlows];
    const environmentId = parsed[constants_2.CommandArgEnvironmentId] ??
        runDefaults?.environmentId ??
        test.environment_id;
    const environment = environmentId !== undefined
        ? await apiClient.getEnvironment(environmentId)
        : undefined;
    const credentialsId = parsed[constants_2.CommandArgCredentials] ??
        runDefaults?.credentialsId ??
        test.credentials_id;
    if (credentialsId) {
        await assertNonCloudCredential(apiClient, credentialsId, constants_2.CommandArgCredentials);
    }
    const basicAuthCredentialsId = parsed[constants_2.CommandArgBasicAuthCredentials] ??
        runDefaults?.basicAuthCredentialsId;
    if (basicAuthCredentialsId) {
        await assertNonCloudCredential(apiClient, basicAuthCredentialsId, constants_2.CommandArgBasicAuthCredentials);
    }
    const browserConfig = {
        browser: parsed[constants_1.CommandArgBrowser] ?? runDefaults?.browser ?? 'chromium',
        width: parsed[constants_1.CommandArgWidth],
        height: parsed[constants_1.CommandArgHeight],
        headless: parsed[constants_1.CommandArgHeadless],
        locale: parsed[constants_1.CommandArgLocale] ?? runDefaults?.locale,
        timezone: parsed[constants_1.CommandArgTimezoneID] ?? runDefaults?.timezone,
        userAgent: parsed[constants_1.CommandArgUserAgent],
        interactionSpeed: parsed[constants_1.CommandArgTestInteractionSpeed] ?? 'normal',
    };
    const snapshot = {
        test,
        flows,
        environment,
        credentialsId,
        basicAuthCredentialsId,
        urlOverride: parsed[constants_2.CommandArgUrl] ?? runDefaults?.url,
        browserConfig,
        branchName,
    };
    const launched = await (0, DebugBrowserLauncher_1.launchDebugBrowser)(userDataDir, {
        remoteDebuggingPort: parsed[constants_1.CommandArgPort],
        headless: parsed[constants_1.CommandArgHeadless],
    });
    (0, DebugSession_1.createSession)({
        sessionId,
        testId,
        wsEndpoint: launched.wsEndpoint,
        browserPort: launched.browserPort,
        browserPid: launched.browserPid,
        userDataDir: launched.userDataDir,
        snapshot,
    });
    const debugState = (0, buildDebugSessionState_1.buildDebugSessionState)(snapshot, testId);
    try {
        await debugState.save((0, buildDebugSessionState_1.debugStateFilePath)(sessionId));
    }
    catch (error) {
        const reason = error instanceof Error ? error.message : String(error);
        process.stderr.write(`Warning: could not write state.json for session ${sessionId} (${reason}). ` +
            'Cursor mutations via set-current-step will be unavailable; ' +
            'list-steps and run-* will fall back to session.json.\n');
    }
    const stepCount = (0, expandSteps_1.expandSessionSteps)(snapshot).length;
    const resolvedUrl = snapshot.urlOverride ?? snapshot.test.url ?? undefined;
    const output = {
        sessionId,
        testId,
        browser: browserConfig.browser,
        url: resolvedUrl,
        wsEndpoint: launched.wsEndpoint,
        browserPort: launched.browserPort,
        userDataDir: launched.userDataDir,
        stepCount,
    };
    process.stdout.write(JSON.stringify(output) + '\n');
}
async function deriveDefaultsFromRun(apiClient, runId) {
    let testRun;
    try {
        testRun = await apiClient.getTestRun(runId);
    }
    catch (error) {
        const status = error.statusCode;
        if (status === 401 || status === 403) {
            throw new Error(`Access denied for test run ${runId}`);
        }
        throw new Error(`Test run ${runId} not found`);
    }
    const params = testRun.journey_parameters;
    const deploymentUri = params?.deployment?.uri || undefined;
    return {
        testId: testRun.test_id ?? testRun.journey_id,
        url: deploymentUri,
        environmentId: params?.deployment?.environment_id,
        credentialsId: params?.credentials_id,
        basicAuthCredentialsId: params?.http_auth_credentials_id,
        browser: testRun.browser ?? params?.browser_type,
        locale: testRun.localization_options?.locale,
        timezone: testRun.localization_options?.timezone_identifier,
    };
}
function assertSupportedTestType(testType, testId) {
    const unsupportedTypeLabels = {
        [mablApi_1.TestTypeEnum.Api]: 'API',
        [mablApi_1.TestTypeEnum.Performance]: 'performance',
        [mablApi_1.TestTypeEnum.Mobile]: 'mobile',
    };
    const label = testType ? unsupportedTypeLabels[testType] : undefined;
    if (label) {
        throw new Error(`Test ${testId} is a ${label} test; mabl agent debug only supports browser tests.`);
    }
}
async function assertNonCloudCredential(apiClient, credentialsId, flagName) {
    const credential = await apiClient.getCredential(credentialsId);
    if (credential.cloud_only) {
        throw new Error(`Cannot use cloud-only credentials [${credentialsId}] in a local debug session. ` +
            `Pass a non-cloud credential to --${flagName}.`);
    }
}
