import { promises as fs } from 'fs';
import path from 'path';
import { getTaskProgressForChange, formatTaskStatus } from '../utils/task-progress.js';
import { readFileSync } from 'fs';
import { join } from 'path';
import { MarkdownParser } from './parsers/markdown-parser.js';
/**
 * Get the most recent modification time of any file in a directory (recursive).
 * Falls back to the directory's own mtime if no files are found.
 */
async function getLastModified(dirPath) {
    let latest = null;
    async function walk(dir) {
        const entries = await fs.readdir(dir, { withFileTypes: true });
        for (const entry of entries) {
            const fullPath = path.join(dir, entry.name);
            if (entry.isDirectory()) {
                await walk(fullPath);
            }
            else {
                const stat = await fs.stat(fullPath);
                if (latest === null || stat.mtime > latest) {
                    latest = stat.mtime;
                }
            }
        }
    }
    await walk(dirPath);
    // If no files found, use the directory's own modification time
    if (latest === null) {
        const dirStat = await fs.stat(dirPath);
        return dirStat.mtime;
    }
    return latest;
}
/**
 * Format a date as relative time (e.g., "2 hours ago", "3 days ago")
 */
function formatRelativeTime(date) {
    const now = new Date();
    const diffMs = now.getTime() - date.getTime();
    const diffSecs = Math.floor(diffMs / 1000);
    const diffMins = Math.floor(diffSecs / 60);
    const diffHours = Math.floor(diffMins / 60);
    const diffDays = Math.floor(diffHours / 24);
    if (diffDays > 30) {
        return date.toLocaleDateString();
    }
    else if (diffDays > 0) {
        return `${diffDays}d ago`;
    }
    else if (diffHours > 0) {
        return `${diffHours}h ago`;
    }
    else if (diffMins > 0) {
        return `${diffMins}m ago`;
    }
    else {
        return 'just now';
    }
}
export class ListCommand {
    async execute(targetPath = '.', mode = 'changes', options = {}) {
        const { sort = 'recent', json = false } = options;
        if (mode === 'changes') {
            const changesDir = path.join(targetPath, 'openspec', 'changes');
            // Check if changes directory exists
            try {
                await fs.access(changesDir);
            }
            catch {
                throw new Error("No OpenSpec changes directory found. Run 'openspec init' first.");
            }
            // Get all directories in changes (excluding archive)
            const entries = await fs.readdir(changesDir, { withFileTypes: true });
            const changeDirs = entries
                .filter(entry => entry.isDirectory() && entry.name !== 'archive')
                .map(entry => entry.name);
            if (changeDirs.length === 0) {
                if (json) {
                    console.log(JSON.stringify({ changes: [] }));
                }
                else {
                    console.log('No active changes found.');
                }
                return;
            }
            // Collect information about each change
            const changes = [];
            for (const changeDir of changeDirs) {
                const progress = await getTaskProgressForChange(changesDir, changeDir);
                const changePath = path.join(changesDir, changeDir);
                const lastModified = await getLastModified(changePath);
                changes.push({
                    name: changeDir,
                    completedTasks: progress.completed,
                    totalTasks: progress.total,
                    lastModified
                });
            }
            // Sort by preference (default: recent first)
            if (sort === 'recent') {
                changes.sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());
            }
            else {
                changes.sort((a, b) => a.name.localeCompare(b.name));
            }
            // JSON output for programmatic use
            if (json) {
                const jsonOutput = changes.map(c => ({
                    name: c.name,
                    completedTasks: c.completedTasks,
                    totalTasks: c.totalTasks,
                    lastModified: c.lastModified.toISOString(),
                    status: c.totalTasks === 0 ? 'no-tasks' : c.completedTasks === c.totalTasks ? 'complete' : 'in-progress'
                }));
                console.log(JSON.stringify({ changes: jsonOutput }, null, 2));
                return;
            }
            // Display results
            console.log('Changes:');
            const padding = '  ';
            const nameWidth = Math.max(...changes.map(c => c.name.length));
            for (const change of changes) {
                const paddedName = change.name.padEnd(nameWidth);
                const status = formatTaskStatus({ total: change.totalTasks, completed: change.completedTasks });
                const timeAgo = formatRelativeTime(change.lastModified);
                console.log(`${padding}${paddedName}     ${status.padEnd(12)}  ${timeAgo}`);
            }
            return;
        }
        // specs mode
        const specsDir = path.join(targetPath, 'openspec', 'specs');
        try {
            await fs.access(specsDir);
        }
        catch {
            console.log('No specs found.');
            return;
        }
        const entries = await fs.readdir(specsDir, { withFileTypes: true });
        const specDirs = entries.filter(e => e.isDirectory()).map(e => e.name);
        if (specDirs.length === 0) {
            console.log('No specs found.');
            return;
        }
        const specs = [];
        for (const id of specDirs) {
            const specPath = join(specsDir, id, 'spec.md');
            try {
                const content = readFileSync(specPath, 'utf-8');
                const parser = new MarkdownParser(content);
                const spec = parser.parseSpec(id);
                specs.push({ id, requirementCount: spec.requirements.length });
            }
            catch {
                // If spec cannot be read or parsed, include with 0 count
                specs.push({ id, requirementCount: 0 });
            }
        }
        specs.sort((a, b) => a.id.localeCompare(b.id));
        console.log('Specs:');
        const padding = '  ';
        const nameWidth = Math.max(...specs.map(s => s.id.length));
        for (const spec of specs) {
            const padded = spec.id.padEnd(nameWidth);
            console.log(`${padding}${padded}     requirements ${spec.requirementCount}`);
        }
    }
}
//# sourceMappingURL=list.js.map