/**
 * Backlog Scanner - Intelligent backlog scanning and ranking
 *
 * Scans .specweave/increments/_backlog/ directory and ranks items
 * by priority, dependencies, and project match.
 *
 * Part of increment 0039: Ultra-Smart Next Command
 *
 * @module core/workflow/backlog-scanner
 * @since v0.22.0
 */
import * as fs from '../../utils/fs-native.js';
import * as path from 'path';
import * as yaml from 'js-yaml';
/**
 * Backlog Scanner - Scan and rank backlog items
 */
export class BacklogScanner {
    constructor(rootDir = process.cwd()) {
        this.backlogDir = path.join(rootDir, '.specweave/increments/_backlog');
    }
    /**
     * Scan backlog directory
     */
    async scanBacklog() {
        if (!await fs.pathExists(this.backlogDir)) {
            return [];
        }
        const items = [];
        const dirs = await fs.readdir(this.backlogDir);
        for (const dir of dirs) {
            const dirPath = path.join(this.backlogDir, dir);
            const stat = await fs.stat(dirPath);
            if (!stat.isDirectory())
                continue;
            const specPath = path.join(dirPath, 'spec.md');
            if (!await fs.pathExists(specPath))
                continue;
            try {
                const item = await this.parseBacklogItem(dir, specPath);
                items.push(item);
            }
            catch {
                console.warn(`Failed to parse backlog item ${dir}`);
            }
        }
        return items;
    }
    /**
     * Parse backlog item from spec.md
     */
    async parseBacklogItem(incrementId, specPath) {
        const content = await fs.readFile(specPath, 'utf-8');
        // Extract YAML frontmatter
        const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
        if (!frontmatterMatch) {
            throw new Error('No YAML frontmatter found in spec.md');
        }
        const frontmatter = yaml.load(frontmatterMatch[1]);
        // Extract title from first heading
        const titleMatch = content.match(/^#\s+(.+)$/m);
        const title = titleMatch ? titleMatch[1] : frontmatter.title || incrementId;
        // v0.34.1: Prioritize per-US **Project**: fields over frontmatter.project (ADR-0140)
        const perUSProject = this.extractPrimaryPerUSProject(content);
        const project = perUSProject || frontmatter.project;
        return {
            incrementId,
            specPath,
            title,
            priority: frontmatter.priority || 'P2',
            project,
            dependencies: frontmatter.dependencies || [],
            estimatedEffort: frontmatter.estimated_effort || frontmatter.estimatedEffort,
            created: frontmatter.created
        };
    }
    /**
     * Extract primary project from per-US **Project**: fields
     * v0.34.1: New method for ADR-0140
     */
    extractPrimaryPerUSProject(content) {
        const projectPattern = /\*\*Project\*\*:\s*([a-zA-Z0-9-]+)/i;
        const match = content.match(projectPattern);
        return match ? match[1].toLowerCase() : undefined;
    }
    /**
     * Rank backlog items by priority, dependencies, and project match.
     * Scores: Priority (P0=10, P1=7, P2=5, P3=3), Dependencies met +5, Project match +3, Recent +2
     */
    rankItems(items, currentProject, completedIncrements = []) {
        const PRIORITY_SCORES = { P0: 10, P1: 7, P2: 5, P3: 3 };
        const MS_PER_DAY = 1000 * 60 * 60 * 24;
        const ranked = items.map(item => {
            let score = 0;
            const reasons = [];
            const priorityScore = PRIORITY_SCORES[item.priority] ?? 3;
            score += priorityScore;
            reasons.push(`Priority ${item.priority} (${priorityScore} pts)`);
            const unmetDeps = item.dependencies.filter(dep => !completedIncrements.includes(dep));
            if (unmetDeps.length === 0 && item.dependencies.length > 0) {
                score += 5;
                reasons.push('All dependencies met (+5 pts)');
            }
            else if (unmetDeps.length > 0) {
                reasons.push(`Blocked by: ${unmetDeps.join(', ')}`);
            }
            if (currentProject && item.project === currentProject) {
                score += 3;
                reasons.push(`Project match: ${currentProject} (+3 pts)`);
            }
            if (item.created) {
                const daysSinceCreated = (Date.now() - new Date(item.created).getTime()) / MS_PER_DAY;
                if (daysSinceCreated < 7) {
                    score += 2;
                    reasons.push('Recently created (+2 pts)');
                }
            }
            return { ...item, score, reason: reasons.join('; ') };
        });
        return ranked.sort((a, b) => b.score - a.score);
    }
    /**
     * Get top N recommendations
     *
     * @param n - Number of recommendations (default: 3)
     * @param currentProject - Current project filter
     * @param completedIncrements - Completed increments
     * @returns Backlog recommendations
     */
    async getTopRecommendations(n = 3, currentProject, completedIncrements = []) {
        const items = await this.scanBacklog();
        const ranked = this.rankItems(items, currentProject, completedIncrements);
        const topItems = ranked.slice(0, n);
        let summary = '';
        if (items.length === 0) {
            summary = 'No items in backlog. Use sw:increment to create new work.';
        }
        else if (topItems.length === 0) {
            summary = 'No actionable items found (all blocked by dependencies).';
        }
        else {
            summary = `Found ${items.length} items in backlog. Top ${topItems.length} recommendations based on priority and dependencies.`;
        }
        return {
            items: topItems,
            totalItems: items.length,
            summary
        };
    }
}
//# sourceMappingURL=backlog-scanner.js.map