var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
import { Resolver, Mutation, Query, Arg, Ctx, ObjectType, Field, PubSub, PubSubEngine, ID, Int } from 'type-graphql';
import { LogError, LogStatus } from '@memberjunction/core';
import { TestEngine } from '@memberjunction/testing-engine';
import { ResolverBase } from '../generic/ResolverBase.js';
import { PUSH_STATUS_UPDATES_TOPIC } from '../generic/PushStatusResolver.js';
// ===== GraphQL Types =====
let TestRunResult = class TestRunResult {
};
__decorate([
    Field(),
    __metadata("design:type", Boolean)
], TestRunResult.prototype, "success", void 0);
__decorate([
    Field({ nullable: true }),
    __metadata("design:type", String)
], TestRunResult.prototype, "errorMessage", void 0);
__decorate([
    Field({ nullable: true }),
    __metadata("design:type", Number)
], TestRunResult.prototype, "executionTimeMs", void 0);
__decorate([
    Field(),
    __metadata("design:type", String)
], TestRunResult.prototype, "result", void 0);
TestRunResult = __decorate([
    ObjectType()
], TestRunResult);
export { TestRunResult };
let TestSuiteRunResult = class TestSuiteRunResult {
};
__decorate([
    Field(),
    __metadata("design:type", Boolean)
], TestSuiteRunResult.prototype, "success", void 0);
__decorate([
    Field({ nullable: true }),
    __metadata("design:type", String)
], TestSuiteRunResult.prototype, "errorMessage", void 0);
__decorate([
    Field({ nullable: true }),
    __metadata("design:type", Number)
], TestSuiteRunResult.prototype, "executionTimeMs", void 0);
__decorate([
    Field(),
    __metadata("design:type", String)
], TestSuiteRunResult.prototype, "result", void 0);
TestSuiteRunResult = __decorate([
    ObjectType()
], TestSuiteRunResult);
export { TestSuiteRunResult };
let TestExecutionProgress = class TestExecutionProgress {
};
__decorate([
    Field(),
    __metadata("design:type", String)
], TestExecutionProgress.prototype, "currentStep", void 0);
__decorate([
    Field(() => Int),
    __metadata("design:type", Number)
], TestExecutionProgress.prototype, "percentage", void 0);
__decorate([
    Field(),
    __metadata("design:type", String)
], TestExecutionProgress.prototype, "message", void 0);
__decorate([
    Field({ nullable: true }),
    __metadata("design:type", String)
], TestExecutionProgress.prototype, "testName", void 0);
__decorate([
    Field({ nullable: true }),
    __metadata("design:type", String)
], TestExecutionProgress.prototype, "driverType", void 0);
__decorate([
    Field({ nullable: true }),
    __metadata("design:type", String)
], TestExecutionProgress.prototype, "oracleEvaluation", void 0);
TestExecutionProgress = __decorate([
    ObjectType()
], TestExecutionProgress);
export { TestExecutionProgress };
let TestExecutionStreamMessage = class TestExecutionStreamMessage {
};
__decorate([
    Field(() => ID),
    __metadata("design:type", String)
], TestExecutionStreamMessage.prototype, "sessionId", void 0);
__decorate([
    Field(() => ID),
    __metadata("design:type", String)
], TestExecutionStreamMessage.prototype, "testRunId", void 0);
__decorate([
    Field(),
    __metadata("design:type", String)
], TestExecutionStreamMessage.prototype, "type", void 0);
__decorate([
    Field({ nullable: true }),
    __metadata("design:type", TestExecutionProgress)
], TestExecutionStreamMessage.prototype, "progress", void 0);
__decorate([
    Field(),
    __metadata("design:type", Date)
], TestExecutionStreamMessage.prototype, "timestamp", void 0);
TestExecutionStreamMessage = __decorate([
    ObjectType()
], TestExecutionStreamMessage);
export { TestExecutionStreamMessage };
// ===== Resolver =====
let RunTestResolver = class RunTestResolver extends ResolverBase {
    /**
     * Execute a single test.
     * Supports fire-and-forget mode to avoid Azure proxy timeouts on long-running tests.
     */
    async RunTest(testId, verbose = true, environment, tags, variables, fireAndForget, pubSub, { userPayload } = {}) {
        const user = this.GetUserFromPayload(userPayload);
        if (!user) {
            return { success: false, errorMessage: 'User context required', result: JSON.stringify({}) };
        }
        if (fireAndForget && pubSub) {
            // Fire-and-forget: start in background, return immediately
            this.executeTestInBackground(testId, verbose, environment, tags, variables, pubSub, userPayload, user);
            LogStatus(`🔥 Fire-and-forget: Test ${testId} execution started in background`);
            return {
                success: true,
                result: JSON.stringify({ accepted: true, fireAndForget: true })
            };
        }
        // Synchronous mode (default): wait for execution to complete
        return this.executeTest(testId, verbose, environment, tags, variables, pubSub, userPayload, user);
    }
    /**
     * Execute a test suite.
     * Supports fire-and-forget mode to avoid Azure proxy timeouts on long-running suites.
     */
    async RunTestSuite(suiteId, verbose = true, environment, parallel = false, tags, variables, selectedTestIds, sequenceStart, sequenceEnd, fireAndForget, pubSub, { userPayload } = {}) {
        const user = this.GetUserFromPayload(userPayload);
        if (!user) {
            return { success: false, errorMessage: 'User context required', result: JSON.stringify({}) };
        }
        if (fireAndForget && pubSub) {
            // Fire-and-forget: start in background, return immediately
            this.executeSuiteInBackground(suiteId, verbose, environment, parallel, tags, variables, selectedTestIds, sequenceStart, sequenceEnd, pubSub, userPayload, user);
            LogStatus(`🔥 Fire-and-forget: Suite ${suiteId} execution started in background`);
            return {
                success: true,
                result: JSON.stringify({ accepted: true, fireAndForget: true })
            };
        }
        // Synchronous mode (default): wait for execution to complete
        return this.executeSuite(suiteId, verbose, environment, parallel, tags, variables, selectedTestIds, sequenceStart, sequenceEnd, pubSub, userPayload, user);
    }
    /**
     * Query to check if a test is currently running
     */
    async IsTestRunning(testId, { userPayload } = {}) {
        // TODO: Implement running test tracking
        // For now, return false
        return false;
    }
    // ===== Core Execution Methods =====
    /**
     * Execute a single test synchronously and return the result.
     */
    async executeTest(testId, verbose, environment, tags, variables, pubSub, userPayload, user) {
        const startTime = Date.now();
        try {
            LogStatus(`[RunTestResolver] Starting test execution: ${testId}`);
            const engine = TestEngine.Instance;
            await engine.Config(verbose, user);
            const progressCallback = pubSub ? this.createProgressCallback(pubSub, userPayload, testId) : undefined;
            const logCallback = pubSub ? this.createLogCallback(pubSub, userPayload, testId) : undefined;
            const parsedVariables = this.parseVariablesJson(variables);
            const options = { verbose, environment, tags, variables: parsedVariables, progressCallback, logCallback };
            const result = await engine.RunTest(testId, options, user);
            const testRunResult = this.buildTestRunResult(result, startTime, pubSub, userPayload);
            // Publish fire-and-forget completion event (used by background mode, harmless in sync mode)
            if (pubSub) {
                this.publishFireAndForgetComplete(pubSub, userPayload, testId, testRunResult);
            }
            return testRunResult;
        }
        catch (error) {
            const executionTime = Date.now() - startTime;
            const errorMsg = error.message;
            LogError(`[RunTestResolver] Test execution failed: ${errorMsg}`);
            if (pubSub) {
                this.publishError(pubSub, userPayload, testId, errorMsg);
            }
            return { success: false, errorMessage: errorMsg, result: JSON.stringify({}), executionTimeMs: executionTime };
        }
    }
    /**
     * Execute a test in background (fire-and-forget).
     * Errors are published via PubSub, not propagated.
     */
    executeTestInBackground(testId, verbose, environment, tags, variables, pubSub, userPayload, user) {
        this.executeTest(testId, verbose, environment, tags, variables, pubSub, userPayload, user)
            .catch((error) => {
            const errorMessage = (error instanceof Error) ? error.message : 'Unknown background test execution error';
            LogError(`🔥 Fire-and-forget test execution failed: ${errorMessage}`, undefined, error);
            this.publishFireAndForgetError(pubSub, userPayload, testId, errorMessage);
        });
    }
    /**
     * Execute a test suite synchronously and return the result.
     */
    async executeSuite(suiteId, verbose, environment, parallel, tags, variables, selectedTestIds, sequenceStart, sequenceEnd, pubSub, userPayload, user) {
        const startTime = Date.now();
        try {
            LogStatus(`[RunTestResolver] Starting suite execution: ${suiteId}`);
            const engine = TestEngine.Instance;
            await engine.Config(verbose, user);
            const progressCallback = pubSub ? this.createProgressCallback(pubSub, userPayload, suiteId) : undefined;
            const logCallback = pubSub ? this.createLogCallback(pubSub, userPayload, suiteId) : undefined;
            const parsedSelectedTestIds = this.parseJsonArray(selectedTestIds, 'selectedTestIds');
            const parsedVariables = this.parseVariablesJson(variables);
            const options = {
                verbose, environment, parallel, tags,
                variables: parsedVariables, selectedTestIds: parsedSelectedTestIds,
                sequenceStart, sequenceEnd, progressCallback, logCallback
            };
            const result = await engine.RunSuite(suiteId, options, user);
            const executionTime = Date.now() - startTime;
            LogStatus(`[RunTestResolver] Suite completed: ${result.totalTests} tests in ${executionTime}ms`);
            const suiteRunResult = {
                success: result.status === 'Completed',
                result: JSON.stringify(result),
                executionTimeMs: executionTime
            };
            // Publish fire-and-forget completion event
            if (pubSub) {
                this.publishFireAndForgetSuiteComplete(pubSub, userPayload, suiteId, suiteRunResult);
            }
            return suiteRunResult;
        }
        catch (error) {
            const executionTime = Date.now() - startTime;
            const errorMsg = error.message;
            LogError(`[RunTestResolver] Suite execution failed: ${errorMsg}`);
            return { success: false, errorMessage: errorMsg, result: JSON.stringify({}), executionTimeMs: executionTime };
        }
    }
    /**
     * Execute a test suite in background (fire-and-forget).
     * Errors are published via PubSub, not propagated.
     */
    executeSuiteInBackground(suiteId, verbose, environment, parallel, tags, variables, selectedTestIds, sequenceStart, sequenceEnd, pubSub, userPayload, user) {
        this.executeSuite(suiteId, verbose, environment, parallel, tags, variables, selectedTestIds, sequenceStart, sequenceEnd, pubSub, userPayload, user).catch((error) => {
            const errorMessage = (error instanceof Error) ? error.message : 'Unknown background suite execution error';
            LogError(`🔥 Fire-and-forget suite execution failed: ${errorMessage}`, undefined, error);
            this.publishFireAndForgetSuiteError(pubSub, userPayload, suiteId, errorMessage);
        });
    }
    // ===== Result Building =====
    /**
     * Build a TestRunResult from the engine result, handling both single and array results.
     */
    buildTestRunResult(result, startTime, pubSub, userPayload) {
        const executionTime = Date.now() - startTime;
        if (Array.isArray(result)) {
            const allPassed = result.every((r) => r.status === 'Passed');
            if (pubSub) {
                for (const iterationResult of result) {
                    this.publishComplete(pubSub, userPayload, iterationResult);
                }
            }
            LogStatus(`[RunTestResolver] Test completed: ${result.length} iterations, ${allPassed ? 'all passed' : 'some failed'} in ${executionTime}ms`);
            return { success: allPassed, result: JSON.stringify(result), executionTimeMs: executionTime };
        }
        const passed = result.status === 'Passed';
        if (pubSub && result.testRunId) {
            this.publishComplete(pubSub, userPayload, result);
        }
        LogStatus(`[RunTestResolver] Test completed: ${result.status} in ${executionTime}ms`);
        return { success: passed, result: JSON.stringify(result), executionTimeMs: executionTime };
    }
    // ===== Input Parsing =====
    parseVariablesJson(variables) {
        if (!variables)
            return undefined;
        try {
            return JSON.parse(variables);
        }
        catch (e) {
            LogError(`[RunTestResolver] Failed to parse variables: ${variables}`);
            return undefined;
        }
    }
    parseJsonArray(json, fieldName) {
        if (!json)
            return undefined;
        try {
            return JSON.parse(json);
        }
        catch (e) {
            LogError(`[RunTestResolver] Failed to parse ${fieldName}: ${json}`);
            return undefined;
        }
    }
    // ===== Progress Callbacks =====
    /**
     * Create progress callback for test execution
     */
    createProgressCallback(pubSub, userPayload, testId) {
        return (progress) => {
            LogStatus(`[RunTestResolver] Progress: ${progress.step} - ${progress.percentage}%`);
            const testRun = progress.metadata?.testRun;
            const progressMsg = {
                sessionId: userPayload.sessionId || '',
                testRunId: testRun?.ID || testId,
                type: 'progress',
                testRun: testRun && typeof testRun.GetAll === 'function'
                    ? testRun.GetAll()
                    : undefined,
                progress: {
                    currentStep: progress.step,
                    percentage: progress.percentage,
                    message: progress.message,
                    testName: progress.metadata?.testName,
                    driverType: progress.metadata?.driverType,
                    oracleEvaluation: progress.metadata?.oracleType
                },
                timestamp: new Date()
            };
            this.publishProgress(pubSub, progressMsg, userPayload);
        };
    }
    /**
     * Create log callback that streams driver/engine log messages to the UI
     * as progress updates, so they appear in the execution log in real-time.
     */
    createLogCallback(pubSub, userPayload, testId) {
        return (message) => {
            const progressMsg = {
                sessionId: userPayload.sessionId || '',
                testRunId: testId,
                type: 'progress',
                progress: {
                    currentStep: 'driver_log',
                    percentage: -1, // Signal that percentage should not be updated
                    message: message.message,
                },
                timestamp: message.timestamp
            };
            this.publishProgress(pubSub, progressMsg, userPayload);
        };
    }
    // ===== PubSub Publishing =====
    publishProgress(pubSub, data, userPayload) {
        pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
            message: JSON.stringify({
                resolver: 'RunTestResolver',
                type: 'TestExecutionProgress',
                status: 'ok',
                data,
            }),
            sessionId: userPayload.sessionId,
        });
    }
    publishComplete(pubSub, userPayload, result) {
        pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
            message: JSON.stringify({
                resolver: 'RunTestResolver',
                type: 'TestExecutionComplete',
                status: 'ok',
                data: {
                    sessionId: userPayload.sessionId,
                    testRunId: result.testRunId,
                    type: 'complete',
                    result: JSON.stringify(result),
                    timestamp: new Date()
                },
            }),
            sessionId: userPayload.sessionId,
        });
    }
    publishError(pubSub, userPayload, testId, errorMsg) {
        pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
            message: JSON.stringify({
                resolver: 'RunTestResolver',
                type: 'TestExecutionError',
                status: 'error',
                data: {
                    sessionId: userPayload.sessionId,
                    testRunId: testId,
                    type: 'error',
                    errorMessage: errorMsg,
                    timestamp: new Date()
                },
            }),
            sessionId: userPayload.sessionId,
        });
    }
    /**
     * Publish a fire-and-forget completion event for a single test.
     * Includes testId for client-side correlation and the full result.
     */
    publishFireAndForgetComplete(pubSub, userPayload, testId, testRunResult) {
        pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
            message: JSON.stringify({
                resolver: 'RunTestResolver',
                type: 'FireAndForgetComplete',
                status: 'ok',
                data: {
                    sessionId: userPayload.sessionId,
                    testId,
                    type: 'complete',
                    success: testRunResult.success,
                    errorMessage: testRunResult.errorMessage,
                    executionTimeMs: testRunResult.executionTimeMs,
                    result: testRunResult.result,
                    timestamp: new Date()
                },
            }),
            sessionId: userPayload.sessionId,
        });
    }
    /**
     * Publish a fire-and-forget error event for a single test.
     */
    publishFireAndForgetError(pubSub, userPayload, testId, errorMessage) {
        pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
            message: JSON.stringify({
                resolver: 'RunTestResolver',
                type: 'FireAndForgetComplete',
                status: 'ok',
                data: {
                    sessionId: userPayload.sessionId,
                    testId,
                    type: 'complete',
                    success: false,
                    errorMessage,
                    result: JSON.stringify({ success: false, errorMessage }),
                    timestamp: new Date()
                },
            }),
            sessionId: userPayload.sessionId,
        });
    }
    /**
     * Publish a fire-and-forget completion event for a test suite.
     * Includes suiteId for client-side correlation and the full result.
     */
    publishFireAndForgetSuiteComplete(pubSub, userPayload, suiteId, suiteRunResult) {
        pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
            message: JSON.stringify({
                resolver: 'RunTestResolver',
                type: 'FireAndForgetSuiteComplete',
                status: 'ok',
                data: {
                    sessionId: userPayload.sessionId,
                    suiteId,
                    type: 'complete',
                    success: suiteRunResult.success,
                    errorMessage: suiteRunResult.errorMessage,
                    executionTimeMs: suiteRunResult.executionTimeMs,
                    result: suiteRunResult.result,
                    timestamp: new Date()
                },
            }),
            sessionId: userPayload.sessionId,
        });
    }
    /**
     * Publish a fire-and-forget error event for a test suite.
     */
    publishFireAndForgetSuiteError(pubSub, userPayload, suiteId, errorMessage) {
        pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, {
            message: JSON.stringify({
                resolver: 'RunTestResolver',
                type: 'FireAndForgetSuiteComplete',
                status: 'ok',
                data: {
                    sessionId: userPayload.sessionId,
                    suiteId,
                    type: 'complete',
                    success: false,
                    errorMessage,
                    result: JSON.stringify({ success: false, errorMessage }),
                    timestamp: new Date()
                },
            }),
            sessionId: userPayload.sessionId,
        });
    }
};
__decorate([
    Mutation(() => TestRunResult),
    __param(0, Arg('testId')),
    __param(1, Arg('verbose', { nullable: true })),
    __param(2, Arg('environment', { nullable: true })),
    __param(3, Arg('tags', { nullable: true })),
    __param(4, Arg('variables', { nullable: true })),
    __param(5, Arg('fireAndForget', { nullable: true })),
    __param(6, PubSub()),
    __param(7, Ctx()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Boolean, String, String, String, Boolean, PubSubEngine, Object]),
    __metadata("design:returntype", Promise)
], RunTestResolver.prototype, "RunTest", null);
__decorate([
    Mutation(() => TestSuiteRunResult),
    __param(0, Arg('suiteId')),
    __param(1, Arg('verbose', { nullable: true })),
    __param(2, Arg('environment', { nullable: true })),
    __param(3, Arg('parallel', { nullable: true })),
    __param(4, Arg('tags', { nullable: true })),
    __param(5, Arg('variables', { nullable: true })),
    __param(6, Arg('selectedTestIds', { nullable: true })),
    __param(7, Arg('sequenceStart', () => Int, { nullable: true })),
    __param(8, Arg('sequenceEnd', () => Int, { nullable: true })),
    __param(9, Arg('fireAndForget', { nullable: true })),
    __param(10, PubSub()),
    __param(11, Ctx()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Boolean, String, Boolean, String, String, String, Number, Number, Boolean, PubSubEngine, Object]),
    __metadata("design:returntype", Promise)
], RunTestResolver.prototype, "RunTestSuite", null);
__decorate([
    Query(() => Boolean),
    __param(0, Arg('testId')),
    __param(1, Ctx()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Object]),
    __metadata("design:returntype", Promise)
], RunTestResolver.prototype, "IsTestRunning", null);
RunTestResolver = __decorate([
    Resolver()
], RunTestResolver);
export { RunTestResolver };
//# sourceMappingURL=RunTestResolver.js.map