import { CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import { ConfigManager } from '../../config/config-manager.js';
import { StorageManager } from '../../storage/storage-manager.js';
import { TestRunner } from './test-runner.js';
import { TestingStore } from './store.js';
import crypto from 'crypto';
import { watch } from 'fs';
let configManager;
let storageManager;
let testRunner;
let testingStore;
let activeWatchers = new Map();
export async function setupTestingFrameworkTools(server, config) {
    configManager = config || new ConfigManager();
    storageManager = new StorageManager();
    testRunner = new TestRunner();
    testingStore = new TestingStore(configManager);
    await testingStore.initialize();
    server.setRequestHandler(CallToolRequestSchema, async (request) => {
        const { name, arguments: args } = request.params;
        if (!args) {
            throw new Error('No arguments provided');
        }
        switch (name) {
            case 'run_tests': {
                const options = args;
                const projectMarker = await storageManager.loadProjectMarker();
                const projectId = projectMarker?.projectId || 'default';
                try {
                    // Run tests
                    const result = await testRunner.runTests(options);
                    result.projectId = projectId;
                    // Get current git info if available
                    try {
                        const { exec } = await import('child_process');
                        const { promisify } = await import('util');
                        const execAsync = promisify(exec);
                        const { stdout: branch } = await execAsync('git rev-parse --abbrev-ref HEAD');
                        const { stdout: commit } = await execAsync('git rev-parse HEAD');
                        result.branch = branch.trim();
                        result.commit = commit.trim().substring(0, 7);
                    }
                    catch {
                        // Git info not available
                    }
                    // Save result to history
                    await testingStore.saveTestResult(result);
                    // Generate recommendations
                    const history = await testingStore.getTestHistory(10);
                    const recommendations = testRunner.generateTestRecommendations(result, history);
                    // Format output
                    const output = formatTestOutput(result, recommendations);
                    return {
                        content: [
                            {
                                type: 'text',
                                text: output,
                            },
                        ],
                        structured: {
                            result,
                            recommendations,
                            projectId,
                            timestamp: new Date().toISOString(),
                        },
                    };
                }
                catch (error) {
                    return {
                        content: [
                            {
                                type: 'text',
                                text: `❌ Test execution failed\n\n**Error**: ${error.message}\n\nTry running with --verbose flag for more details.`,
                            },
                        ],
                        structured: {
                            error: error.message,
                            command: error.command,
                            projectId,
                            timestamp: new Date().toISOString(),
                        },
                    };
                }
            }
            case 'track_test_results': {
                const { branch, commit, tags = [], compareWithBaseline = false, limit = 20 } = args;
                const projectMarker = await storageManager.loadProjectMarker();
                const projectId = projectMarker?.projectId || 'default';
                // Get test history
                const history = await testingStore.getTestHistoryForProject(projectId);
                // Filter by branch/commit if specified
                let filteredResults = history.results;
                if (branch) {
                    filteredResults = filteredResults.filter(r => r.branch === branch);
                }
                if (commit) {
                    filteredResults = filteredResults.filter(r => r.commit === commit);
                }
                if (tags.length > 0) {
                    filteredResults = filteredResults.filter(r => r.tags?.some(t => tags.includes(t)));
                }
                // Limit results
                if (limit > 0 && filteredResults.length > limit) {
                    filteredResults = filteredResults.slice(-limit);
                }
                // Compare with baseline if requested
                let comparison = null;
                if (compareWithBaseline && history.baseline && filteredResults.length > 0) {
                    const latest = filteredResults[filteredResults.length - 1];
                    comparison = await testingStore.compareTestResults(history.baseline.id, latest.id);
                }
                // Format output
                const output = formatTestHistoryOutput(filteredResults, history.trends, comparison);
                return {
                    content: [
                        {
                            type: 'text',
                            text: output,
                        },
                    ],
                    structured: {
                        results: filteredResults,
                        trends: history.trends,
                        comparison,
                        baseline: history.baseline,
                        projectId,
                        timestamp: new Date().toISOString(),
                    },
                };
            }
            case 'analyze_test_coverage': {
                const { targetCoverage = 80, granularity = 'file', includeUncovered = true } = args;
                // Get latest test result with coverage
                const latest = await testingStore.getLatestTestResult();
                if (!latest || !latest.coverage) {
                    return {
                        content: [
                            {
                                type: 'text',
                                text: '⚠️ No coverage data available\n\nRun tests with coverage enabled: `run_tests coverage=true`',
                            },
                        ],
                        structured: {
                            error: 'No coverage data available',
                            timestamp: new Date().toISOString(),
                        },
                    };
                }
                // Analyze coverage
                const analysis = analyzeCoverage(latest.coverage, targetCoverage, granularity, includeUncovered);
                // Format output
                const output = formatCoverageOutput(analysis, latest.coverage, targetCoverage);
                return {
                    content: [
                        {
                            type: 'text',
                            text: output,
                        },
                    ],
                    structured: {
                        coverage: latest.coverage,
                        analysis,
                        targetCoverage,
                        timestamp: new Date().toISOString(),
                    },
                };
            }
            case 'detect_flaky_tests': {
                const { minRuns = 5, threshold = 0.2 } = args;
                const history = await testingStore.getTestHistory(50);
                if (history.length < minRuns) {
                    return {
                        content: [
                            {
                                type: 'text',
                                text: `⚠️ Insufficient test runs for flaky test detection\n\nNeed at least ${minRuns} test runs, found ${history.length}.`,
                            },
                        ],
                        structured: {
                            error: 'Insufficient test runs',
                            required: minRuns,
                            found: history.length,
                            timestamp: new Date().toISOString(),
                        },
                    };
                }
                // Detect flaky tests
                const flakyTests = await testRunner.detectFlakyTests(history);
                const significantFlakes = flakyTests.filter(t => t.failureRate >= threshold);
                // Save flaky test data
                await testingStore.saveFlakyTests(significantFlakes);
                // Format output
                const output = formatFlakyTestOutput(significantFlakes);
                return {
                    content: [
                        {
                            type: 'text',
                            text: output,
                        },
                    ],
                    structured: {
                        flakyTests: significantFlakes,
                        totalAnalyzed: flakyTests.length,
                        threshold,
                        timestamp: new Date().toISOString(),
                    },
                };
            }
            case 'watch_tests': {
                const { patterns = ['**/*.test.*', '**/*.spec.*', 'src/**/*'], command, autoRerun = true, debounceMs = 1000 } = args;
                const watcherId = crypto.randomUUID();
                const watcherConfig = {
                    id: watcherId,
                    patterns,
                    command: command || 'npm test',
                    active: true,
                    autoRerun,
                    debounceMs,
                };
                // Set up file watchers
                let debounceTimer = null;
                const runTests = async () => {
                    if (!watcherConfig.active)
                        return;
                    try {
                        const result = await testRunner.runTests({ watch: false });
                        watcherConfig.lastRun = result;
                        console.log(`Tests completed: ${result.summary.passed}/${result.summary.total} passed`);
                    }
                    catch (error) {
                        console.error('Test run failed:', error);
                    }
                };
                const handleFileChange = () => {
                    if (debounceTimer)
                        clearTimeout(debounceTimer);
                    debounceTimer = setTimeout(() => {
                        if (watcherConfig.autoRerun) {
                            console.log('File changed, rerunning tests...');
                            runTests();
                        }
                    }, watcherConfig.debounceMs);
                };
                // Create watchers for each pattern
                const watchers = [];
                for (const pattern of patterns) {
                    const watcher = watch(pattern, { recursive: true }, handleFileChange);
                    watchers.push(watcher);
                }
                // Store watcher info
                activeWatchers.set(watcherId, {
                    watcher: watchers[0], // Store primary watcher
                    config: watcherConfig,
                });
                // Run initial test
                if (autoRerun) {
                    await runTests();
                }
                return {
                    content: [
                        {
                            type: 'text',
                            text: `👁️ Test watcher started\n\n**Watcher ID**: ${watcherId}\n**Patterns**: ${patterns.join(', ')}\n**Command**: ${command || 'npm test'}\n**Auto-rerun**: ${autoRerun ? 'Yes' : 'No'}\n\nWatching for file changes...`,
                        },
                    ],
                    structured: {
                        watcherId,
                        config: watcherConfig,
                        timestamp: new Date().toISOString(),
                    },
                };
            }
            case 'stop_test_watcher': {
                const { watcherId } = args;
                const watcher = activeWatchers.get(watcherId);
                if (!watcher) {
                    return {
                        content: [
                            {
                                type: 'text',
                                text: `❌ Watcher not found: ${watcherId}`,
                            },
                        ],
                        structured: {
                            error: 'Watcher not found',
                            watcherId,
                            timestamp: new Date().toISOString(),
                        },
                    };
                }
                // Stop watcher
                watcher.watcher.close();
                watcher.config.active = false;
                activeWatchers.delete(watcherId);
                return {
                    content: [
                        {
                            type: 'text',
                            text: `✅ Test watcher stopped\n\n**Watcher ID**: ${watcherId}`,
                        },
                    ],
                    structured: {
                        watcherId,
                        stopped: true,
                        timestamp: new Date().toISOString(),
                    },
                };
            }
            case 'set_test_baseline': {
                const { resultId, tags = ['baseline'] } = args;
                const projectMarker = await storageManager.loadProjectMarker();
                const projectId = projectMarker?.projectId || 'default';
                // Use latest result if no ID specified
                let targetId = resultId;
                if (!targetId) {
                    const latest = await testingStore.getLatestTestResult();
                    if (!latest) {
                        throw new Error('No test results available to set as baseline');
                    }
                    targetId = latest.id;
                }
                // Set baseline
                await testingStore.setBaseline(targetId, projectId);
                // Get the baseline result
                const baseline = await testingStore.getTestResultById(targetId);
                if (!baseline) {
                    throw new Error('Failed to set baseline');
                }
                return {
                    content: [
                        {
                            type: 'text',
                            text: `✅ Test baseline set\n\n**Result ID**: ${targetId}\n**Date**: ${new Date(baseline.timestamp).toLocaleString()}\n**Success Rate**: ${baseline.summary.successRate.toFixed(1)}%\n**Coverage**: ${baseline.coverage?.lines.percentage.toFixed(1)}%\n\nFuture test runs will be compared against this baseline.`,
                        },
                    ],
                    structured: {
                        baseline,
                        projectId,
                        timestamp: new Date().toISOString(),
                    },
                };
            }
            default:
                return null;
        }
    });
    return {
        tools: [
            {
                name: 'run_tests',
                description: 'Execute test suite with detailed reporting and coverage analysis',
                inputSchema: {
                    type: 'object',
                    properties: {
                        files: {
                            type: 'array',
                            items: { type: 'string' },
                            description: 'Specific test files to run',
                        },
                        pattern: {
                            type: 'string',
                            description: 'Test name pattern to match',
                        },
                        watch: {
                            type: 'boolean',
                            description: 'Run tests in watch mode',
                            default: false,
                        },
                        coverage: {
                            type: 'boolean',
                            description: 'Generate coverage report',
                            default: false,
                        },
                        bail: {
                            type: 'boolean',
                            description: 'Stop on first test failure',
                            default: false,
                        },
                        verbose: {
                            type: 'boolean',
                            description: 'Verbose output',
                            default: false,
                        },
                        updateSnapshots: {
                            type: 'boolean',
                            description: 'Update test snapshots',
                            default: false,
                        },
                        parallel: {
                            type: 'boolean',
                            description: 'Run tests in parallel',
                            default: true,
                        },
                        maxWorkers: {
                            type: 'number',
                            description: 'Maximum number of worker processes',
                        },
                        timeout: {
                            type: 'number',
                            description: 'Test timeout in milliseconds',
                        },
                        grep: {
                            type: 'string',
                            description: 'Pattern to filter tests (Mocha/RSpec)',
                        },
                        tags: {
                            type: 'array',
                            items: { type: 'string' },
                            description: 'Tags to associate with this test run',
                        },
                    },
                    required: [],
                },
            },
            {
                name: 'track_test_results',
                description: 'View test result history and trends over time',
                inputSchema: {
                    type: 'object',
                    properties: {
                        branch: {
                            type: 'string',
                            description: 'Filter by git branch',
                        },
                        commit: {
                            type: 'string',
                            description: 'Filter by git commit',
                        },
                        tags: {
                            type: 'array',
                            items: { type: 'string' },
                            description: 'Filter by tags',
                        },
                        compareWithBaseline: {
                            type: 'boolean',
                            description: 'Compare latest results with baseline',
                            default: false,
                        },
                        limit: {
                            type: 'number',
                            description: 'Maximum number of results to return',
                            default: 20,
                        },
                    },
                    required: [],
                },
            },
            {
                name: 'analyze_test_coverage',
                description: 'Analyze test coverage and identify areas needing more tests',
                inputSchema: {
                    type: 'object',
                    properties: {
                        targetCoverage: {
                            type: 'number',
                            description: 'Target coverage percentage',
                            default: 80,
                        },
                        granularity: {
                            type: 'string',
                            enum: ['project', 'file', 'function'],
                            description: 'Level of detail for coverage analysis',
                            default: 'file',
                        },
                        includeUncovered: {
                            type: 'boolean',
                            description: 'Include list of uncovered code',
                            default: true,
                        },
                    },
                    required: [],
                },
            },
            {
                name: 'detect_flaky_tests',
                description: 'Identify tests that fail intermittently',
                inputSchema: {
                    type: 'object',
                    properties: {
                        minRuns: {
                            type: 'number',
                            description: 'Minimum test runs required for analysis',
                            default: 5,
                        },
                        threshold: {
                            type: 'number',
                            description: 'Failure rate threshold (0-1)',
                            default: 0.2,
                        },
                    },
                    required: [],
                },
            },
            {
                name: 'watch_tests',
                description: 'Watch files and automatically rerun tests on changes',
                inputSchema: {
                    type: 'object',
                    properties: {
                        patterns: {
                            type: 'array',
                            items: { type: 'string' },
                            description: 'File patterns to watch',
                            default: ['**/*.test.*', '**/*.spec.*', 'src/**/*'],
                        },
                        command: {
                            type: 'string',
                            description: 'Test command to run',
                        },
                        autoRerun: {
                            type: 'boolean',
                            description: 'Automatically rerun tests on file change',
                            default: true,
                        },
                        debounceMs: {
                            type: 'number',
                            description: 'Debounce delay in milliseconds',
                            default: 1000,
                        },
                    },
                    required: [],
                },
            },
            {
                name: 'stop_test_watcher',
                description: 'Stop a running test watcher',
                inputSchema: {
                    type: 'object',
                    properties: {
                        watcherId: {
                            type: 'string',
                            description: 'ID of the watcher to stop',
                        },
                    },
                    required: ['watcherId'],
                },
            },
            {
                name: 'set_test_baseline',
                description: 'Set a test result as the baseline for comparisons',
                inputSchema: {
                    type: 'object',
                    properties: {
                        resultId: {
                            type: 'string',
                            description: 'Test result ID to set as baseline (defaults to latest)',
                        },
                        tags: {
                            type: 'array',
                            items: { type: 'string' },
                            description: 'Additional tags for the baseline',
                            default: ['baseline'],
                        },
                    },
                    required: [],
                },
            },
        ],
        resources: [],
    };
}
// Helper functions
function formatTestOutput(result, recommendations) {
    let output = result.summary.failed === 0 ? '✅ All tests passed!\n\n' : '❌ Some tests failed\n\n';
    output += `📊 **Test Summary**\n`;
    output += `• Total: ${result.summary.total}\n`;
    output += `• Passed: ${result.summary.passed} ✅\n`;
    output += `• Failed: ${result.summary.failed} ❌\n`;
    if (result.summary.skipped > 0)
        output += `• Skipped: ${result.summary.skipped} ⏭️\n`;
    if (result.summary.pending > 0)
        output += `• Pending: ${result.summary.pending} ⏸️\n`;
    output += `• Success Rate: ${result.summary.successRate.toFixed(1)}%\n`;
    output += `• Duration: ${(result.summary.duration / 1000).toFixed(2)}s\n\n`;
    if (result.coverage) {
        output += `📈 **Coverage Report**\n`;
        output += `• Lines: ${result.coverage.lines.percentage.toFixed(1)}% (${result.coverage.lines.covered}/${result.coverage.lines.total})\n`;
        output += `• Statements: ${result.coverage.statements.percentage.toFixed(1)}%\n`;
        output += `• Functions: ${result.coverage.functions.percentage.toFixed(1)}%\n`;
        output += `• Branches: ${result.coverage.branches.percentage.toFixed(1)}%\n\n`;
    }
    // Show failed tests
    const failedTests = result.suites.flatMap(s => s.tests.filter(t => t.status === 'failed').map(t => ({ suite: s.name, test: t })));
    if (failedTests.length > 0) {
        output += `❌ **Failed Tests**\n`;
        failedTests.forEach(({ suite, test }) => {
            output += `• ${suite} › ${test.name}\n`;
            if (test.error) {
                output += `  ${test.error.message}\n`;
            }
        });
        output += `\n`;
    }
    // Show recommendations
    if (recommendations.length > 0) {
        output += `💡 **Recommendations**\n`;
        recommendations.forEach(rec => {
            const icon = rec.priority === 'high' ? '🔴' : rec.priority === 'medium' ? '🟡' : '🟢';
            output += `${icon} ${rec.title}\n`;
            output += `   ${rec.description}\n`;
        });
    }
    return output;
}
function formatTestHistoryOutput(results, trends, comparison) {
    let output = `📊 Test Results History (${results.length} runs)\n\n`;
    if (trends.length > 0) {
        output += `📈 **Trends**\n`;
        trends.forEach(trend => {
            const icon = trend.trend === 'improving' ? '📈' : trend.trend === 'degrading' ? '📉' : '➡️';
            const metricName = trend.metric === 'successRate' ? 'Success Rate' :
                trend.metric === 'duration' ? 'Test Duration' :
                    trend.metric === 'coverage' ? 'Code Coverage' : 'Test Count';
            output += `• ${metricName}: ${icon} ${trend.trend} (${trend.changePercentage > 0 ? '+' : ''}${trend.changePercentage.toFixed(1)}%)\n`;
        });
        output += `\n`;
    }
    if (comparison) {
        output += `🔄 **Baseline Comparison**\n`;
        if (comparison.improvements.length > 0) {
            output += `✅ Improvements:\n`;
            comparison.improvements.forEach(imp => output += `   • ${imp}\n`);
        }
        if (comparison.regressions.length > 0) {
            output += `⚠️ Regressions:\n`;
            comparison.regressions.forEach(reg => output += `   • ${reg}\n`);
        }
        if (comparison.newTests.length > 0) {
            output += `🆕 New Tests: ${comparison.newTests.length}\n`;
        }
        if (comparison.removedTests.length > 0) {
            output += `🗑️ Removed Tests: ${comparison.removedTests.length}\n`;
        }
        output += `\n`;
    }
    // Recent results
    output += `📋 **Recent Results**\n`;
    const recentResults = results.slice(-5).reverse();
    recentResults.forEach(result => {
        const date = new Date(result.timestamp);
        const icon = result.summary.failed === 0 ? '✅' : '❌';
        output += `${icon} ${date.toLocaleDateString()} ${date.toLocaleTimeString()}`;
        if (result.branch)
            output += ` [${result.branch}]`;
        if (result.commit)
            output += ` (${result.commit})`;
        output += `\n   ${result.summary.passed}/${result.summary.total} passed`;
        if (result.coverage)
            output += ` | ${result.coverage.lines.percentage.toFixed(1)}% coverage`;
        output += `\n`;
    });
    return output;
}
function formatCoverageOutput(analysis, coverage, targetCoverage) {
    let output = `📊 Test Coverage Analysis\n\n`;
    const isGoalMet = coverage.lines.percentage >= targetCoverage;
    output += isGoalMet ?
        `✅ Coverage goal met! (${coverage.lines.percentage.toFixed(1)}% >= ${targetCoverage}%)\n\n` :
        `⚠️ Coverage below target (${coverage.lines.percentage.toFixed(1)}% < ${targetCoverage}%)\n\n`;
    output += `**Overall Coverage**\n`;
    output += `• Lines: ${coverage.lines.percentage.toFixed(1)}% (${coverage.lines.covered}/${coverage.lines.total})\n`;
    output += `• Statements: ${coverage.statements.percentage.toFixed(1)}%\n`;
    output += `• Functions: ${coverage.functions.percentage.toFixed(1)}%\n`;
    output += `• Branches: ${coverage.branches.percentage.toFixed(1)}%\n\n`;
    if (analysis.lowCoverageFiles && analysis.lowCoverageFiles.length > 0) {
        output += `⚠️ **Files Needing More Tests**\n`;
        analysis.lowCoverageFiles.forEach((file) => {
            output += `• ${file.path}: ${file.coverage.toFixed(1)}%\n`;
        });
        output += `\n`;
    }
    if (analysis.uncoveredFunctions && analysis.uncoveredFunctions.length > 0) {
        output += `🔍 **Uncovered Functions**\n`;
        analysis.uncoveredFunctions.slice(0, 10).forEach((func) => {
            output += `• ${func.name} (${func.file}:${func.line})\n`;
        });
        if (analysis.uncoveredFunctions.length > 10) {
            output += `... and ${analysis.uncoveredFunctions.length - 10} more\n`;
        }
    }
    return output;
}
function formatFlakyTestOutput(flakyTests) {
    if (flakyTests.length === 0) {
        return '✅ No flaky tests detected!\n\nAll tests show consistent pass/fail behavior.';
    }
    let output = `⚠️ Flaky Tests Detected (${flakyTests.length})\n\n`;
    output += `These tests show intermittent failures:\n\n`;
    flakyTests.forEach((test, index) => {
        output += `${index + 1}. **${test.name}**\n`;
        output += `   Suite: ${test.suite}\n`;
        output += `   Failure Rate: ${(test.failureRate * 100).toFixed(1)}%\n`;
        output += `   Recent Results: ${test.recentResults.map(r => r === 'passed' ? '✅' : '❌').join(' ')}\n`;
        output += `\n`;
    });
    output += `💡 **Recommendations**\n`;
    output += `• Investigate timing dependencies in flaky tests\n`;
    output += `• Check for test isolation issues\n`;
    output += `• Consider adding retries for known flaky tests\n`;
    return output;
}
function analyzeCoverage(coverage, targetCoverage, granularity, includeUncovered) {
    const analysis = {
        meetsTarget: coverage.lines.percentage >= targetCoverage,
        gap: targetCoverage - coverage.lines.percentage,
        lowCoverageFiles: [],
        uncoveredFunctions: [],
    };
    // Find files below target
    if (granularity === 'file' || granularity === 'function') {
        analysis.lowCoverageFiles = coverage.files
            .filter((f) => f.lines.percentage < targetCoverage)
            .sort((a, b) => a.lines.percentage - b.lines.percentage)
            .map((f) => ({
            path: f.path,
            coverage: f.lines.percentage,
            uncoveredLines: f.uncoveredLines?.length || 0,
        }));
    }
    return analysis;
}
//# sourceMappingURL=index.js.map