import { z } from "zod";
import { stripVTControlCharacters } from "util";
import { TestExecutionService } from "../services/TestExecutionService.js";
import { AnalyticsService } from "../services/AnalyticsService.js";
import { getWorkspaceBaseUrl } from "../utils/workspaceAuth.js";
import { ProgrammingLanguage, TestType } from "../types/TestTypes.js";
import { StateManager } from "../utils/AnalysisStateManager.js";
import { logger } from "../utils/logger.js";
const TOOL_NAME = "skyramp_execute_test";
export function registerExecuteSkyrampTestTool(server) {
    server.registerTool(TOOL_NAME, {
        description: `Execute a Skyramp-generated test in isolated containerized environments for reliable, deterministic testing.

Skyramp is a comprehensive testing platform that generates reliable, out-of-the-box functional and performance tests with AI-powered scaffolding. The test execution engine runs your generated tests in controlled, isolated environments to ensure consistent results.

KEY FEATURES:
• Isolated Execution: Tests run in containerized environments for consistency
• Multi-Language Support: Execute tests written in Python, Java, JavaScript, or TypeScript
• Out-of-the-Box Execution: Generated tests work immediately without modification
• StateFile Integration: Optionally write execution results back to stateFile for health analysis

REQUIRED PARAMETERS:
- language: Programming language of your test file (python, javascript, typescript, java)
- testType: Type of test to execute (smoke, contract, fuzz, integration, load, ui, e2e)
- testFile: Absolute path to the generated test file to execute
- token: Authentication token for your service (use empty string if no authentication required)

OPTIONAL PARAMETERS:
- stateFile: Path to state file from skyramp_analyze_changes. When provided, execution results (passed/failed, errors, duration) will be written back to enrich test health analysis.

AUTHENTICATION:
Pass an empty string as the token parameter — the server automatically injects SKYRAMP_TEST_TOKEN from the environment. The executor applies it with the correct header defined in workspace.yml:
- bearer / oauth / token (or custom via authScheme) → Authorization: <scheme> <token>
- cookie / session                   → Cookie: <token>
- apiKey                             → <authHeader>: <token>  (header name from workspace.yml)
- basic                              → Authorization: Basic <token>
Use an empty string only if the endpoint is confirmed unauthenticated.
IMPORTANT NOTES:
- First-time usage may take longer as Docker images are downloaded
- Tests run in isolated containers for maximum reliability
- Generated tests are designed to work out-of-the-box without modification
- Results include detailed execution logs and test outcomes
- When stateFile is provided, results are merged back for use by skyramp_actions

For detailed documentation visit: https://www.skyramp.dev/docs/quickstart`,
        inputSchema: {
            workspacePath: z
                .string()
                .describe("The path to the workspace directory where the test file is located"),
            language: z
                .nativeEnum(ProgrammingLanguage)
                .describe("Programming language of the test file to execute (e.g., python, javascript, typescript, java)"),
            testType: z
                .nativeEnum(TestType)
                .describe("Type of the test to execute."),
            testFile: z
                .string()
                .describe("ALWAYS USE ABSOLUTE PATH to the test file to execute"),
            token: z
                .string()
                .describe("Skyramp authentication token for test execution. USE EMPTY STRING WHEN USER CONFIRMS 'No token required'"),
            playwrightSaveStoragePath: z
                .string()
                .optional()
                .describe("Path to save Playwright session storage after test execution for authentication purposes. Can be a relative path to the workspace (e.g., 'auth-session.json') or an absolute path. The session will be saved after the test completes."),
            stateFile: z
                .string()
                .optional()
                .describe("Optional path to state file from skyramp_analyze_changes. When provided, execution results (passed/failed, errors, duration) will be written back to enrich the test health analysis."),
        },
        _meta: {
            keywords: ["run test", "execute test"],
        },
    }, async (params, extra) => {
        let errorResult;
        // Helper to send progress notifications to the MCP client
        const sendProgress = async (progress, total, message) => {
            const progressToken = extra._meta?.progressToken;
            if (progressToken !== undefined) {
                const notification = {
                    method: "notifications/progress",
                    params: {
                        progressToken,
                        progress,
                        total,
                        message,
                    },
                };
                await extra.sendNotification(notification);
            }
        };
        // Progress callback adapter for TestExecutionService
        const onExecutionProgress = async (progress) => {
            await sendProgress(progress.percent, 100, progress.message);
        };
        const previousBaseUrl = process.env.SKYRAMP_TEST_BASE_URL;
        let didSetSkyrampBaseUrl = false;
        let useHostNetwork = false;
        try {
            // Send initial progress
            await sendProgress(0, 100, "Starting test execution...");
            // Always resolve workspace config for dockerNetwork (host networking)
            // and optionally inject SKYRAMP_TEST_BASE_URL if not already set.
            if (params.workspacePath) {
                const { baseUrl, dockerNetwork, candidates } = await getWorkspaceBaseUrl(params.workspacePath, params.testFile, params.language);
                useHostNetwork = !!dockerNetwork;
                if (!process.env.SKYRAMP_TEST_BASE_URL) {
                    if (baseUrl) {
                        process.env.SKYRAMP_TEST_BASE_URL = baseUrl;
                        didSetSkyrampBaseUrl = true;
                    }
                    else if (candidates.length > 0) {
                        return {
                            content: [{
                                    type: "text",
                                    text: [
                                        `Cannot determine SKYRAMP_TEST_BASE_URL — test file matches multiple services:`,
                                        ...candidates.map((c) => `  • ${c.serviceName}: ${c.baseUrl}`),
                                        ``,
                                        `Re-invoke with SKYRAMP_TEST_BASE_URL set to the correct service URL, or make each service's testDirectory unique in .skyramp/workspace.yml.`,
                                    ].join("\n"),
                                }],
                            isError: true,
                        };
                    }
                }
            }
            const executionService = new TestExecutionService();
            // Fall back to SKYRAMP_TEST_TOKEN when caller passes empty string or omits the token.
            // This keeps secrets server-side and avoids embedding credential values in prompts.
            const effectiveToken = params.token || process.env.SKYRAMP_TEST_TOKEN || "";
            // Execute test with progress callback - reports Docker cache/pull status
            const result = await executionService.executeTest({
                testFile: params.testFile,
                workspacePath: params.workspacePath,
                language: params.language,
                testType: params.testType,
                token: effectiveToken,
                playwrightSaveStoragePath: params.playwrightSaveStoragePath,
                useHostNetwork,
            }, onExecutionProgress);
            // Update stateFile with execution results if provided
            if (params.stateFile) {
                try {
                    const stateManager = StateManager.fromStatePath(params.stateFile);
                    const stateData = await stateManager.readData();
                    if (stateData && stateData.existingTests) {
                        const testIndex = stateData.existingTests.findIndex((t) => t.testFile === params.testFile);
                        if (testIndex >= 0) {
                            stateData.existingTests[testIndex].execution = result;
                            await stateManager.writeData(stateData);
                            logger.info(`Updated stateFile with execution results for ${params.testFile}`);
                        }
                        else {
                            logger.warning(`Test file ${params.testFile} not found in stateFile`);
                        }
                    }
                }
                catch (err) {
                    logger.error(`Failed to update stateFile: ${err.message}`);
                    // Don't fail the tool call if stateFile update fails
                }
            }
            // Progress is already reported by TestExecutionService
            // Only report final status if not already at 100%
            if (!result.passed) {
                errorResult = {
                    content: [
                        {
                            type: "text",
                            text: `Test execution failed: ${stripVTControlCharacters(result.output || "")}
                **IMPORTANT: IF THE EXECUTION FAILED BECAUSE OF A 401 STATUS CODE, ASK THE USER TO PROVIDE THE AUTHENTICATION TOKEN TO THE TOOL OR A PLACE FROM WHERE WE CAN READ THE TOKEN. DO NOT UPDATE THE GENERATED TEST FILE.**`,
                        },
                    ],
                    isError: true,
                };
                return errorResult;
            }
            // Success - progress already reported by TestExecutionService
            return {
                content: [
                    {
                        type: "text",
                        text: `Test execution result: ${stripVTControlCharacters(result.output || "")}`,
                    },
                ],
            };
        }
        catch (err) {
            errorResult = {
                content: [
                    {
                        type: "text",
                        text: `Test execution failed: ${err.message}`,
                    },
                ],
                isError: true,
            };
            return errorResult;
        }
        finally {
            if (didSetSkyrampBaseUrl) {
                if (previousBaseUrl === undefined) {
                    delete process.env.SKYRAMP_TEST_BASE_URL;
                }
                else {
                    process.env.SKYRAMP_TEST_BASE_URL = previousBaseUrl;
                }
            }
            AnalyticsService.pushMCPToolEvent(TOOL_NAME, errorResult, {
                testFile: params.testFile,
                workspacePath: params.workspacePath,
                language: params.language,
                testType: params.testType,
            });
        }
    });
}
