import { spawn } from 'child_process';
import { createModuleLogger } from '../../../logger.js';
import { Platform } from '../../../types.js';
import { PlatformHandler } from '../../../platformHandler.js';
import { existsSync, mkdirSync, rmSync } from 'fs';
import path from 'path';
import { config } from '../../../config.js';
const logger = createModuleLogger('XcodeTester');
/**
 * Handles testing Xcode projects (Single Responsibility: Testing)
 */
export class XcodeTester {
    /**
     * Run tests for an Xcode project
     */
    async test(projectPath, isWorkspace, options = {}) {
        const startTime = Date.now();
        const { scheme, configuration = 'Debug', platform = Platform.iOS, deviceId, testFilter, testTarget } = options;
        // Create a unique result bundle path
        const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
        const derivedDataPath = config.getDerivedDataPath(projectPath);
        const resultBundlePath = path.join(derivedDataPath, 'Logs', 'Test', `Test-${scheme || 'tests'}-${timestamp}.xcresult`);
        // Ensure result directory exists
        const resultDir = path.dirname(resultBundlePath);
        if (!existsSync(resultDir)) {
            mkdirSync(resultDir, { recursive: true });
        }
        const projectFlag = isWorkspace ? '-workspace' : '-project';
        let command = `xcodebuild ${projectFlag} "${projectPath}"`;
        if (scheme) {
            command += ` -scheme "${scheme}"`;
        }
        command += ` -configuration "${configuration}"`;
        // Determine destination
        let destination;
        if (deviceId) {
            destination = PlatformHandler.getDestination(platform, deviceId);
        }
        else {
            destination = PlatformHandler.getGenericDestination(platform);
        }
        command += ` -destination '${destination}'`;
        // Add test filters
        if (testTarget) {
            command += ` -only-testing:${testTarget}`;
        }
        if (testFilter) {
            command += ` -only-testing:${testFilter}`;
        }
        // Disable parallel testing
        command += ' -parallel-testing-enabled NO';
        // Add result bundle path
        command += ` -resultBundlePath "${resultBundlePath}"`;
        command += ' test';
        logger.debug({ command }, 'Test command');
        return new Promise((resolve) => {
            const child = spawn('sh', ['-c', command]);
            let output = '';
            let lastProgressTime = Date.now();
            const progressInterval = 10000;
            child.stdout.on('data', (data) => {
                const chunk = data.toString();
                output += chunk;
                const now = Date.now();
                if (now - lastProgressTime > progressInterval) {
                    logger.info('Tests still running... (preventing timeout)');
                    lastProgressTime = now;
                }
            });
            child.stderr.on('data', (data) => {
                const chunk = data.toString();
                output += '\n' + chunk;
            });
            child.on('close', async (code) => {
                const duration = Date.now() - startTime;
                let testResult = {
                    passed: 0,
                    failed: 0,
                    success: false,
                    output,
                    duration
                };
                try {
                    // Parse xcresult bundle
                    const { execSync } = require('child_process');
                    const testReportJson = execSync(`xcrun xcresulttool get test-report --format json --path "${resultBundlePath}"`, { encoding: 'utf8', maxBuffer: 50 * 1024 * 1024 });
                    const testReport = JSON.parse(testReportJson);
                    let totalPassed = 0;
                    let totalFailed = 0;
                    const failingTestNames = [];
                    if (testReport.tests) {
                        const countTests = (tests) => {
                            for (const test of tests) {
                                if (test.subtests) {
                                    countTests(test.subtests);
                                }
                                else if (test.testStatus) {
                                    if (test.testStatus === 'Success') {
                                        totalPassed++;
                                    }
                                    else if (test.testStatus === 'Failure' || test.testStatus === 'Expected Failure') {
                                        totalFailed++;
                                        if (test.identifier) {
                                            const parts = test.identifier.split('/');
                                            failingTestNames.push(parts[parts.length - 1] || test.identifier);
                                        }
                                    }
                                }
                            }
                        };
                        countTests(testReport.tests);
                    }
                    testResult = {
                        passed: totalPassed,
                        failed: totalFailed,
                        success: totalFailed === 0 && code === 0,
                        failingTests: failingTestNames.length > 0 ? failingTestNames : undefined,
                        output,
                        duration
                    };
                    logger.info({
                        projectPath,
                        ...testResult,
                        exitCode: code,
                        resultBundlePath
                    }, 'Tests completed (parsed from xcresult)');
                }
                catch (parseError) {
                    logger.error({
                        error: parseError.message,
                        resultBundlePath
                    }, 'Failed to parse xcresult bundle');
                    testResult = {
                        passed: 0,
                        failed: code === 0 ? 0 : 1,
                        success: code === 0,
                        output,
                        error: `Failed to parse test results: ${parseError.message}`,
                        duration
                    };
                }
                // Clean up result bundle if tests passed
                if (testResult.success) {
                    try {
                        rmSync(resultBundlePath, { recursive: true, force: true });
                    }
                    catch {
                        // Ignore cleanup errors
                    }
                }
                resolve(testResult);
            });
            child.on('error', (error) => {
                const duration = Date.now() - startTime;
                logger.error({ error: error.message, projectPath }, 'Failed to spawn test process');
                resolve({
                    success: false,
                    passed: 0,
                    failed: 1,
                    output: '',
                    error: `Failed to run tests: ${error.message}`,
                    duration
                });
            });
        });
    }
}
//# sourceMappingURL=XcodeTester.js.map