/**
 * Beads Integration Utilities
 *
 * Provides utilities for integrating with beads distributed issue tracker:
 * - Project epic creation
 * - Phase task management
 * - Task hierarchy setup
 */
import { execSync } from 'node:child_process';
import { createLogger } from './logger.js';
const logger = createLogger('BeadsIntegration');
/**
 * Beads integration manager for responsible-vibe-mcp
 */
export class BeadsIntegration {
    projectPath;
    constructor(projectPath) {
        this.projectPath = projectPath;
    }
    /**
     * Ensure beads is initialized in the project directory
     */
    async ensureBeadsInitialized() {
        try {
            // Check if beads is already initialized by running a simple command
            execSync('bd list --limit 1', {
                cwd: this.projectPath,
                encoding: 'utf-8',
                stdio: ['ignore', 'pipe', 'pipe'],
            });
            // If we get here, beads is already initialized
            return;
        }
        catch (error) {
            const errorMessage = error instanceof Error ? error.message : String(error);
            // Check if the error suggests beads is not initialized
            if (errorMessage.includes('not initialized') ||
                errorMessage.includes('no database') ||
                errorMessage.includes('init')) {
                logger.info('Beads not initialized, running bd init --no-db', {
                    projectPath: this.projectPath,
                });
                try {
                    // Initialize beads without database
                    execSync('bd init --no-db', {
                        cwd: this.projectPath,
                        encoding: 'utf-8',
                        stdio: ['ignore', 'pipe', 'pipe'],
                    });
                    logger.info('Successfully initialized beads in project', {
                        projectPath: this.projectPath,
                    });
                }
                catch (initError) {
                    const initErrorMessage = initError instanceof Error ? initError.message : String(initError);
                    logger.error('Failed to initialize beads', initError instanceof Error
                        ? initError
                        : new Error(initErrorMessage), { projectPath: this.projectPath });
                    throw new Error(`Failed to initialize beads: ${initErrorMessage}`);
                }
            }
            else {
                // Some other beads error, re-throw
                throw error;
            }
        }
    }
    /**
     * Create a project epic in beads for the development session
     */
    async createProjectEpic(projectName, workflowName, description, planFilename) {
        // Validate parameters first
        this.validateCreateEpicParameters(projectName, workflowName, description, planFilename);
        // Ensure beads is initialized
        await this.ensureBeadsInitialized();
        const epicTitle = planFilename
            ? `${projectName}: ${workflowName} (${planFilename})`
            : `${projectName}: ${workflowName}`;
        const epicDescription = description ||
            `Responsible vibe engineering session using ${workflowName} workflow for ${projectName}`;
        const priority = 2;
        const command = `bd create "${epicTitle}" --description "${epicDescription}" --priority ${priority}`;
        logger.debug('Creating beads project epic', {
            command,
            projectName,
            workflowName,
            projectPath: this.projectPath,
        });
        try {
            const output = execSync(command, {
                cwd: this.projectPath,
                encoding: 'utf-8',
                stdio: ['ignore', 'pipe', 'pipe'],
            });
            // Extract task ID from beads output
            // Support both new format (v0.47.1+): "✓ Created issue: project-name-123"
            // and legacy format: "Created bd-a1b2c3"
            const match = output.match(/✓ Created issue: ([\w\d.-]+)/) ||
                output.match(/Created issue: ([\w\d.-]+)/) ||
                output.match(/Created (bd-[\w\d.]+)/);
            if (!match) {
                logger.warn('Failed to extract task ID from beads output', {
                    command: `bd create "${epicTitle}" --description "${epicDescription}" --priority 2`,
                    output: output.slice(0, 200), // Truncated for logging
                });
                throw new Error(`Failed to extract task ID from beads output: ${output.slice(0, 100)}...`);
            }
            const epicId = match[1] || '';
            logger.info('Created beads project epic', {
                epicId,
                epicTitle,
                projectPath: this.projectPath,
            });
            return epicId;
        }
        catch (error) {
            const errorMessage = error instanceof Error ? error.message : String(error);
            const commandInfo = {
                command,
                projectName,
                workflowName,
                projectPath: this.projectPath,
            };
            logger.error('Failed to create beads project epic', error instanceof Error ? error : new Error(errorMessage), commandInfo);
            // Include stderr if available for better debugging
            const execError = error;
            if (execError?.stderr) {
                logger.error('Beads command stderr output', new Error('Command stderr'), {
                    stderr: execError.stderr.toString(),
                    ...commandInfo,
                });
            }
            throw new Error(`Failed to create beads project epic: ${errorMessage}`);
        }
    }
    /**
     * Create phase tasks for all workflow phases under the project epic
     */
    async createPhaseTasks(epicId, phases, workflowName) {
        // Validate parameters
        this.validateCreatePhaseParameters(epicId, phases, workflowName);
        const phaseTasks = [];
        const phaseNames = Object.keys(phases);
        for (const phase of phaseNames) {
            const phaseTitle = `${this.capitalizePhase(phase)}`;
            const priority = 3;
            const stateDefinition = phases[phase];
            // Escape the description to prevent shell injection and handle special characters
            const description = (stateDefinition?.default_instructions ||
                `${workflowName} workflow ${phase} phase tasks`)
                .replace(/"/g, '\\"') // Escape double quotes
                .replace(/\n/g, ' ') // Replace newlines with spaces
                .replace(/\r/g, '') // Remove carriage returns
                .trim();
            const command = `bd create "${phaseTitle}" --description "${description}" --parent ${epicId} --priority ${priority}`;
            logger.debug('Creating beads phase task', {
                command,
                phase,
                epicId,
                projectPath: this.projectPath,
            });
            try {
                const output = execSync(command, {
                    cwd: this.projectPath,
                    encoding: 'utf-8',
                    stdio: ['ignore', 'pipe', 'pipe'],
                });
                // Extract task ID from beads output
                // Support both new format (v0.47.1+): "✓ Created issue: project-name-123"
                // and legacy format: "Created bd-a1b2c3"
                const match = output.match(/✓ Created issue: ([\w\d.-]+)/) ||
                    output.match(/Created issue: ([\w\d.-]+)/) ||
                    output.match(/Created (bd-[\w\d.]+)/);
                if (!match) {
                    logger.warn('Failed to extract phase task ID from beads output', {
                        command,
                        output: output.slice(0, 200), // Truncated for logging
                    });
                    throw new Error(`Failed to extract task ID from beads output: ${output.slice(0, 100)}...`);
                }
                const phaseTaskId = match[1] || '';
                phaseTasks.push({
                    phaseId: phase,
                    phaseName: phaseTitle,
                    taskId: phaseTaskId,
                });
                logger.debug('Created beads phase task', {
                    phase,
                    phaseTaskId,
                    epicId,
                    projectPath: this.projectPath,
                });
            }
            catch (error) {
                const errorMessage = error instanceof Error ? error.message : String(error);
                const commandInfo = {
                    command,
                    phase,
                    epicId,
                    projectPath: this.projectPath,
                };
                logger.error('Failed to create beads phase task', error instanceof Error ? error : new Error(errorMessage), commandInfo);
                // Include stderr if available for better debugging
                const execError = error;
                if (execError?.stderr) {
                    logger.error('Beads phase command stderr output', new Error('Command stderr'), {
                        stderr: execError.stderr.toString(),
                        ...commandInfo,
                    });
                }
                throw new Error(`Failed to create beads phase task for ${phase}: ${errorMessage}`);
            }
        }
        logger.info('Created all beads phase tasks', {
            count: phaseTasks.length,
            epicId,
            projectPath: this.projectPath,
        });
        return phaseTasks;
    }
    /**
     * Create sequential dependencies between workflow phase tasks
     * Implements graceful error handling: logs warnings for failed dependencies but continues
     */
    async createPhaseDependencies(phaseTasks) {
        if (phaseTasks.length < 2) {
            logger.debug('Skipping phase dependencies - less than 2 phases', {
                phaseCount: phaseTasks.length,
                projectPath: this.projectPath,
            });
            return;
        }
        logger.info('Creating sequential phase dependencies', {
            phaseCount: phaseTasks.length,
            projectPath: this.projectPath,
        });
        // Track failed dependencies for logging
        const failedDependencies = [];
        // Create dependencies in sequence: each phase blocks the next one
        for (let i = 0; i < phaseTasks.length - 1; i++) {
            const currentPhase = phaseTasks[i];
            const nextPhase = phaseTasks[i + 1];
            if (!currentPhase || !nextPhase) {
                logger.warn('Skipping phase dependency - missing phase data', {
                    currentPhaseIndex: i,
                    nextPhaseIndex: i + 1,
                    totalPhases: phaseTasks.length,
                    projectPath: this.projectPath,
                });
                failedDependencies.push({
                    from: `Phase ${i}`,
                    to: `Phase ${i + 1}`,
                    error: 'Missing phase data',
                });
                continue;
            }
            const command = `bd dep ${currentPhase.taskId} --blocks ${nextPhase.taskId}`;
            logger.debug('Creating phase dependency', {
                command,
                currentPhase: currentPhase.phaseName,
                nextPhase: nextPhase.phaseName,
                currentTaskId: currentPhase.taskId,
                nextTaskId: nextPhase.taskId,
                projectPath: this.projectPath,
            });
            try {
                execSync(command, {
                    cwd: this.projectPath,
                    encoding: 'utf-8',
                    stdio: ['ignore', 'pipe', 'pipe'],
                });
                logger.debug('Successfully created phase dependency', {
                    currentPhase: currentPhase.phaseName,
                    nextPhase: nextPhase.phaseName,
                    projectPath: this.projectPath,
                });
            }
            catch (error) {
                // Log as warning but don't fail the entire setup
                const errorMessage = error instanceof Error ? error.message : String(error);
                logger.warn('Failed to create phase dependency - continuing anyway', {
                    error: errorMessage,
                    command,
                    currentPhase: currentPhase.phaseName,
                    nextPhase: nextPhase.phaseName,
                    projectPath: this.projectPath,
                });
                // Include stderr if available for better debugging
                const execError = error;
                if (execError?.stderr) {
                    logger.debug('Beads dependency command stderr', {
                        stderr: execError.stderr.toString(),
                        command,
                        projectPath: this.projectPath,
                    });
                }
                // Track failed dependency but continue
                failedDependencies.push({
                    from: currentPhase.phaseName,
                    to: nextPhase.phaseName,
                    error: errorMessage,
                });
            }
        }
        if (failedDependencies.length > 0) {
            logger.warn('Some phase dependencies could not be created - app continues without these dependencies', {
                failedCount: failedDependencies.length,
                failedDependencies,
                projectPath: this.projectPath,
            });
        }
        logger.info('Completed phase dependency creation', {
            dependencyCount: phaseTasks.length - 1,
            successCount: phaseTasks.length - 1 - failedDependencies.length,
            failedCount: failedDependencies.length,
            projectPath: this.projectPath,
        });
    }
    /**
     * Capitalize phase name for display
     */
    capitalizePhase(phase) {
        return phase
            .split('_')
            .map(word => word.charAt(0).toUpperCase() + word.slice(1))
            .join(' ');
    }
    /**
     * Validate parameters for epic creation
     */
    validateCreateEpicParameters(projectName, workflowName, description, planFilename) {
        if (!projectName ||
            typeof projectName !== 'string' ||
            projectName.trim() === '') {
            throw new Error('Project name is required and cannot be empty');
        }
        if (!workflowName ||
            typeof workflowName !== 'string' ||
            workflowName.trim() === '') {
            throw new Error('Workflow name is required and cannot be empty');
        }
        // Optional description validation - if provided, must be a valid string
        if (description !== undefined &&
            (typeof description !== 'string' || description.trim() === '')) {
            throw new Error('Description, if provided, must be a non-empty string');
        }
        // Optional plan filename validation - if provided, must be a valid string
        if (planFilename !== undefined &&
            (typeof planFilename !== 'string' || planFilename.trim() === '')) {
            throw new Error('Plan filename, if provided, must be a non-empty string');
        }
    }
    /**
     * Validate parameters for phase task creation
     */
    validateCreatePhaseParameters(epicId, phases, workflowName) {
        if (!epicId || typeof epicId !== 'string' || epicId.trim() === '') {
            throw new Error('Epic ID is required and cannot be empty');
        }
        if (!phases ||
            typeof phases !== 'object' ||
            Object.keys(phases).length === 0) {
            throw new Error('Phases object is required and cannot be empty');
        }
        if (!workflowName ||
            typeof workflowName !== 'string' ||
            workflowName.trim() === '') {
            throw new Error('Workflow name is required and cannot be empty');
        }
        // Validate each phase
        for (const [phaseName, phaseState] of Object.entries(phases)) {
            if (!phaseName ||
                typeof phaseName !== 'string' ||
                phaseName.trim() === '') {
                throw new Error(`Invalid phase name: "${phaseName}" - phase names must be non-empty strings`);
            }
            if (!phaseState || typeof phaseState !== 'object') {
                throw new Error(`Invalid phase state for "${phaseName}" - phase states must be objects`);
            }
            if (!phaseState.default_instructions ||
                typeof phaseState.default_instructions !== 'string') {
                throw new Error(`Invalid phase state for "${phaseName}" - default_instructions must be a non-empty string`);
            }
        }
    }
}
//# sourceMappingURL=beads-integration.js.map