"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.projectSetupService = exports.ProjectSetupService = void 0;
const child_process_1 = require("child_process");
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const os_1 = __importDefault(require("os"));
const util_1 = require("util");
const execAsync = (0, util_1.promisify)(child_process_1.exec);
class ProjectSetupService {
    constructor() {
        this.loadConfigs();
        this.logFile = path_1.default.join(__dirname, '../logs/deployment.log');
        this.ensureLogDirectory();
    }
    loadConfigs() {
        try {
            const configPath = path_1.default.join(__dirname, '../config/project-configs.json');
            const configData = fs_1.default.readFileSync(configPath, 'utf8');
            const parsedConfig = JSON.parse(configData);
            this.configs = parsedConfig.projectTypes;
        }
        catch (error) {
            console.error('Failed to load project configurations:', error);
            this.configs = {};
        }
    }
    ensureLogDirectory() {
        const logDir = path_1.default.dirname(this.logFile);
        if (!fs_1.default.existsSync(logDir)) {
            fs_1.default.mkdirSync(logDir, { recursive: true });
        }
    }
    log(message) {
        const timestamp = new Date().toISOString();
        const logMessage = `[${timestamp}] ${message}\n`;
        console.log(message);
        try {
            fs_1.default.appendFileSync(this.logFile, logMessage);
        }
        catch (error) {
            console.error('Failed to write to log file:', error);
        }
    }
    detectProjectType(projectPath) {
        this.log(`Detecting project type for: ${projectPath}`);
        for (const [type, config] of Object.entries(this.configs)) {
            // Check for specific files
            for (const file of config.detection.files) {
                const filePath = path_1.default.join(projectPath, file);
                if (fs_1.default.existsSync(filePath)) {
                    this.log(`Detected ${type} project based on file: ${file}`);
                    return type;
                }
            }
            // Check for file patterns (e.g., *.csproj)
            if (config.detection.files.some(file => file.includes('*'))) {
                try {
                    const files = fs_1.default.readdirSync(projectPath);
                    for (const pattern of config.detection.files) {
                        if (pattern.includes('*')) {
                            const extension = pattern.replace('*', '');
                            if (files.some(file => file.endsWith(extension))) {
                                this.log(`Detected ${type} project based on pattern: ${pattern}`);
                                return type;
                            }
                        }
                    }
                }
                catch (error) {
                    // Directory doesn't exist or can't be read
                }
            }
        }
        this.log('Could not detect project type');
        return null;
    }
    async setupProject(projectPath, projectType, onLog) {
        this.log(`Starting setup for ${projectType} project at: ${projectPath}`);
        onLog === null || onLog === void 0 ? void 0 : onLog(`Starting ${projectType} project setup at: ${projectPath}`);
        const config = this.configs[projectType];
        if (!config) {
            throw new Error(`Unknown project type: ${projectType}`);
        }
        const result = {
            success: true,
            steps: [],
            errors: [],
            warnings: [],
            environment: { ...config.setup.environment }
        };
        for (const step of config.setup.steps) {
            const stepStart = Date.now();
            try {
                // Check if step should be skipped
                if (this.shouldSkipStep(step, projectPath)) {
                    onLog === null || onLog === void 0 ? void 0 : onLog(`[SKIP] ${step.name}`);
                    result.steps.push({
                        name: step.name,
                        success: true,
                        output: 'Skipped - condition not met',
                        skipped: true,
                        duration: 0
                    });
                    continue;
                }
                this.log(`Executing step: ${step.name}`);
                onLog === null || onLog === void 0 ? void 0 : onLog(`\n[STEP] ${step.name}`);
                const stepResult = await this.executeStep(step, projectPath, result.environment, onLog);
                const duration = Date.now() - stepStart;
                result.steps.push({
                    ...stepResult,
                    duration
                });
                if (!stepResult.success && step.required) {
                    onLog === null || onLog === void 0 ? void 0 : onLog(`[ERROR] Step failed: ${step.name}`);
                    result.success = false;
                    result.errors.push(`Required step failed: ${step.name} - ${stepResult.error}`);
                    break;
                }
                else if (!stepResult.success) {
                    onLog === null || onLog === void 0 ? void 0 : onLog(`[WARN] Optional step failed: ${step.name}`);
                    result.warnings.push(`Optional step failed: ${step.name} - ${stepResult.error}`);
                }
                else {
                    onLog === null || onLog === void 0 ? void 0 : onLog(`[OK] ${step.name} completed (${duration}ms)`);
                }
            }
            catch (error) {
                const duration = Date.now() - stepStart;
                const errorMessage = error instanceof Error ? error.message : 'Unknown error';
                onLog === null || onLog === void 0 ? void 0 : onLog(`[ERROR] ${step.name}: ${errorMessage}`);
                result.steps.push({
                    name: step.name,
                    success: false,
                    output: '',
                    error: errorMessage,
                    duration
                });
                if (step.required) {
                    result.success = false;
                    result.errors.push(`Required step failed: ${step.name} - ${errorMessage}`);
                    break;
                }
                else {
                    result.warnings.push(`Optional step failed: ${step.name} - ${errorMessage}`);
                }
            }
        }
        // Set interpreter path for Python projects
        if (projectType === 'python' && result.success) {
            const platform = os_1.default.platform();
            const venvPath = platform === 'win32'
                ? path_1.default.join(projectPath, 'venv', 'Scripts', 'python.exe')
                : path_1.default.join(projectPath, 'venv', 'bin', 'python');
            if (fs_1.default.existsSync(venvPath)) {
                result.interpreterPath = venvPath;
                result.environment.PYTHON_INTERPRETER = venvPath;
            }
        }
        // Validate the setup (advisory only — step success/failure is the real gate)
        const validationResult = await this.validateSetup(projectPath, config);
        if (!validationResult.success) {
            result.warnings.push(...validationResult.errors);
        }
        this.log(`Setup completed. Success: ${result.success}`);
        onLog === null || onLog === void 0 ? void 0 : onLog(`\nSetup ${result.success ? 'completed successfully' : 'failed'}.`);
        return result;
    }
    shouldSkipStep(step, projectPath) {
        var _a, _b;
        // Check platform
        if (step.platform) {
            const currentPlatform = os_1.default.platform() === 'win32' ? 'win32' : 'unix';
            if (step.platform !== currentPlatform) {
                return true;
            }
        }
        // Check conditional requirements
        if (step.conditional) {
            switch (step.conditional) {
                case 'build_script_exists':
                    try {
                        const packageJsonPath = path_1.default.join(projectPath, 'package.json');
                        if (fs_1.default.existsSync(packageJsonPath)) {
                            const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf8'));
                            return !((_a = packageJson.scripts) === null || _a === void 0 ? void 0 : _a.build);
                        }
                        return true;
                    }
                    catch {
                        return true;
                    }
                case 'test_script_exists':
                    try {
                        const packageJsonPath = path_1.default.join(projectPath, 'package.json');
                        if (fs_1.default.existsSync(packageJsonPath)) {
                            const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf8'));
                            return !((_b = packageJson.scripts) === null || _b === void 0 ? void 0 : _b.test);
                        }
                        return true;
                    }
                    catch {
                        return true;
                    }
                case 'requirements_exists':
                    return !fs_1.default.existsSync(path_1.default.join(projectPath, 'requirements.txt'));
                case 'pyproject_exists':
                    return !fs_1.default.existsSync(path_1.default.join(projectPath, 'pyproject.toml'));
                case 'test_project_exists':
                    // Check for test projects in .NET solutions
                    try {
                        const files = fs_1.default.readdirSync(projectPath);
                        return !files.some(file => file.toLowerCase().includes('test') && file.endsWith('.csproj'));
                    }
                    catch {
                        return true;
                    }
                default:
                    return false;
            }
        }
        return false;
    }
    async executeStep(step, projectPath, environment, onLog) {
        const workingDir = step.workingDirectory === 'project' ? projectPath : process.cwd();
        let command = step.command;
        // Handle virtual environment activation for Python
        if (step.useVenv && os_1.default.platform() === 'win32') {
            const venvActivate = path_1.default.join(projectPath, 'venv', 'Scripts', 'Activate.ps1');
            if (fs_1.default.existsSync(venvActivate)) {
                command = `& "${venvActivate}"; ${command}`;
            }
        }
        return new Promise((resolve) => {
            var _a, _b;
            const timeout = step.timeout || 300000; // 5 minutes default
            // Use cmd for simpler commands, powershell for complex ones
            const isComplexCommand = command.includes('&') || command.includes(';') || step.useVenv;
            const shell = isComplexCommand ? 'powershell.exe' : 'cmd.exe';
            const shellArgs = isComplexCommand ? ['-ExecutionPolicy', 'Bypass', '-Command', command] : ['/c', command];
            const child = (0, child_process_1.spawn)(shell, shellArgs, {
                cwd: workingDir,
                env: { ...process.env, ...environment },
                stdio: 'pipe',
                timeout,
                shell: false
            });
            let output = '';
            let error = '';
            (_a = child.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
                const text = data.toString();
                output += text;
                for (const line of text.split('\n')) {
                    const trimmed = line.trim();
                    if (trimmed)
                        onLog === null || onLog === void 0 ? void 0 : onLog(trimmed);
                }
            });
            (_b = child.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (data) => {
                const text = data.toString();
                error += text;
                for (const line of text.split('\n')) {
                    const trimmed = line.trim();
                    if (trimmed)
                        onLog === null || onLog === void 0 ? void 0 : onLog(`[WARN] ${trimmed}`);
                }
            });
            child.on('close', (code) => {
                const success = code === 0;
                this.log(`Step "${step.name}" completed with exit code: ${code}`);
                resolve({
                    name: step.name,
                    success,
                    output: output.trim(),
                    error: error.trim() || undefined
                });
            });
            child.on('error', (err) => {
                this.log(`Step "${step.name}" failed with error: ${err.message}`);
                resolve({
                    name: step.name,
                    success: false,
                    output: '',
                    error: err.message
                });
            });
            // Set up timeout
            const timeoutId = setTimeout(() => {
                child.kill('SIGTERM');
                resolve({
                    name: step.name,
                    success: false,
                    output: output.trim(),
                    error: `Command timed out after ${timeout}ms`
                });
            }, timeout);
            child.on('close', () => {
                clearTimeout(timeoutId);
            });
        });
    }
    async validateSetup(projectPath, config) {
        const errors = [];
        for (const check of config.validation.checks) {
            let checkPassed = false;
            if (check.file) {
                checkPassed = fs_1.default.existsSync(path_1.default.join(projectPath, check.file));
            }
            else if (check.directory) {
                checkPassed = fs_1.default.existsSync(path_1.default.join(projectPath, check.directory));
            }
            else if (check.pattern) {
                try {
                    const files = fs_1.default.readdirSync(projectPath);
                    const extension = check.pattern.replace('*', '');
                    checkPassed = files.some(file => file.endsWith(extension));
                }
                catch {
                    checkPassed = false;
                }
            }
            if (!checkPassed && !check.optional) {
                errors.push(`Validation failed: ${check.name}`);
            }
        }
        return {
            success: errors.length === 0,
            errors
        };
    }
    getProjectConfig(projectType) {
        return this.configs[projectType] || null;
    }
    getSupportedProjectTypes() {
        return Object.keys(this.configs);
    }
}
exports.ProjectSetupService = ProjectSetupService;
exports.projectSetupService = new ProjectSetupService();
