/**
 * RCASD-INDEX.json population and querying.
 *
 * Scans .cleo/rcasd/ and legacy .cleo/rcsd/ directories to build a master index of all RCASD
 * pipeline artifacts. Provides lookup functions for task-anchored artifacts,
 * specs, reports, and pipeline state.
 *
 * @task T4801
 * @epic T4798
 * @ref schemas/rcasd-index.schema.json (compat: rcsd-index.schema.json)
 */
import { existsSync, mkdirSync, readdirSync } from 'node:fs';
import { join } from 'node:path';
import { getCleoDirAbsolute } from '../paths.js';
import { readJsonFile, writeJsonFileAtomic } from '../store/file-utils.js';
// =============================================================================
// INDEX POPULATION
// =============================================================================
/**
 * Scan .cleo/rcasd/ and legacy .cleo/rcsd/ directories and build the RCASD index.
 *
 * Reads all _manifest.json files and any spec/report markdown files to
 * produce a complete index.
 *
 * @param cwd - Working directory
 * @returns Populated RcasdIndex
 * @task T4801
 */
export function buildIndex(cwd) {
    const cleoDir = getCleoDirAbsolute(cwd);
    const lifecycleDirs = [join(cleoDir, 'rcasd'), join(cleoDir, 'rcsd')];
    const seenTaskIds = new Set();
    const taskAnchored = {};
    const specs = [];
    const reports = [];
    let activeResearch = 0;
    let pendingConsensus = 0;
    let lastCompleted;
    for (const lifecycleDir of lifecycleDirs) {
        if (!existsSync(lifecycleDir)) {
            continue;
        }
        const entries = readdirSync(lifecycleDir, { withFileTypes: true });
        for (const entry of entries) {
            if (!entry.isDirectory() || !entry.name.startsWith('T') || seenTaskIds.has(entry.name)) {
                continue;
            }
            const taskId = entry.name;
            seenTaskIds.add(taskId);
            const taskDir = join(lifecycleDir, taskId);
            // Read _manifest.json if it exists
            const manifestPath = join(taskDir, '_manifest.json');
            let manifest = null;
            if (existsSync(manifestPath)) {
                try {
                    manifest = readJsonFile(manifestPath);
                }
                catch {
                    // Skip corrupted manifests
                    continue;
                }
            }
            // Determine pipeline stage and status
            const { currentStage, status } = determinePipelineState(manifest);
            // Track research/consensus counts
            if (manifest?.stages) {
                const researchStatus = manifest.stages['research']?.status;
                const consensusStatus = manifest.stages['consensus']?.status;
                if (researchStatus && researchStatus !== 'completed' && researchStatus !== 'skipped') {
                    activeResearch++;
                }
                if (researchStatus === 'completed' &&
                    (!consensusStatus || consensusStatus === 'not_started' || consensusStatus === 'pending')) {
                    pendingConsensus++;
                }
                // Track last completed
                for (const [, stageData] of Object.entries(manifest.stages)) {
                    if (stageData.status === 'completed' && stageData.completedAt) {
                        if (!lastCompleted || stageData.completedAt > lastCompleted.completedAt) {
                            lastCompleted = {
                                taskId,
                                stage: currentStage,
                                completedAt: stageData.completedAt,
                            };
                        }
                    }
                }
            }
            // Scan for spec and report files
            if (!existsSync(taskDir))
                continue;
            const files = readdirSync(taskDir).filter((f) => f.endsWith('.md'));
            const specFile = files.find((f) => f.includes('-SPEC.md') || f.includes('_spec'));
            const reportFile = files.find((f) => f.includes('-REPORT.md') || f.includes('_report') || f.includes('_research'));
            // Derive short name from directory name
            const shortName = deriveShortName(taskId, manifest?.title);
            taskAnchored[taskId] = {
                shortName,
                directory: taskId,
                spec: specFile,
                report: reportFile,
                status,
                pipelineStage: currentStage,
                updatedAt: new Date().toISOString(),
            };
            // Add spec entry if found
            if (specFile) {
                specs.push({
                    file: specFile,
                    version: '1.0.0',
                    status: status === 'completed' ? 'ACTIVE' : 'DRAFT',
                    domain: shortName,
                    taskId,
                    lastUpdated: new Date().toISOString(),
                });
            }
            // Add report entry if found
            if (reportFile) {
                const relatedSpec = specFile || `${shortName}-SPEC.md`;
                reports.push({
                    file: reportFile,
                    relatedSpec,
                    taskId,
                    progress: status === 'completed' ? '100%' : 'Varies',
                    lastUpdated: new Date().toISOString(),
                });
            }
        }
    }
    const now = new Date().toISOString();
    return {
        $schema: 'https://cleo-dev.com/schemas/v1/rcasd-index.schema.json',
        _meta: {
            version: '1.0.0',
            lastUpdated: now,
            totals: {
                tasks: Object.keys(taskAnchored).length,
                specs: specs.length,
                reports: reports.length,
                activeResearch,
                pendingConsensus,
            },
        },
        authorities: {},
        taskAnchored,
        specs,
        reports,
        pipeline: {
            activeOperations: [],
            queuedTasks: [],
            lastCompleted,
        },
        recentChanges: [],
    };
}
/**
 * Write RCASD-INDEX.json to disk.
 *
 * @param index - The index to write
 * @param cwd - Working directory
 * @task T4801
 */
export function writeIndex(index, cwd) {
    const cleoDir = getCleoDirAbsolute(cwd);
    if (!existsSync(cleoDir)) {
        mkdirSync(cleoDir, { recursive: true });
    }
    const indexPath = join(cleoDir, 'RCASD-INDEX.json');
    writeJsonFileAtomic(indexPath, index);
}
/**
 * Read RCASD-INDEX.json from disk.
 *
 * @param cwd - Working directory
 * @returns The index or null if not found
 * @task T4801
 */
export function readIndex(cwd) {
    const cleoDir = getCleoDirAbsolute(cwd);
    const indexPath = join(cleoDir, 'RCASD-INDEX.json');
    if (!existsSync(indexPath)) {
        return null;
    }
    try {
        return readJsonFile(indexPath);
    }
    catch {
        return null;
    }
}
/**
 * Rebuild and write the index from current disk state.
 *
 * @param cwd - Working directory
 * @returns The rebuilt index
 * @task T4801
 */
export function rebuildIndex(cwd) {
    const index = buildIndex(cwd);
    writeIndex(index, cwd);
    return index;
}
// =============================================================================
// QUERYING
// =============================================================================
/**
 * Get task anchor by task ID.
 *
 * @param taskId - The task ID to look up
 * @param cwd - Working directory
 * @returns TaskAnchor or null
 * @task T4801
 */
export function getTaskAnchor(taskId, cwd) {
    const index = readIndex(cwd);
    return index?.taskAnchored[taskId] ?? null;
}
/**
 * Find tasks by pipeline stage.
 *
 * @param stage - The pipeline stage to filter by
 * @param cwd - Working directory
 * @returns Array of [taskId, anchor] pairs
 * @task T4801
 */
export function findByStage(stage, cwd) {
    const index = readIndex(cwd);
    if (!index)
        return [];
    return Object.entries(index.taskAnchored).filter(([, anchor]) => anchor.pipelineStage === stage);
}
/**
 * Find tasks by status.
 *
 * @param status - The status to filter by
 * @param cwd - Working directory
 * @returns Array of [taskId, anchor] pairs
 * @task T4801
 */
export function findByStatus(status, cwd) {
    const index = readIndex(cwd);
    if (!index)
        return [];
    return Object.entries(index.taskAnchored).filter(([, anchor]) => anchor.status === status);
}
/**
 * Get index summary statistics.
 *
 * @param cwd - Working directory
 * @returns Index totals or null
 * @task T4801
 */
export function getIndexTotals(cwd) {
    const index = readIndex(cwd);
    return index?._meta.totals ?? null;
}
// =============================================================================
// INTERNAL HELPERS
// =============================================================================
/**
 * Determine the current pipeline stage and status from a manifest.
 * @task T4801
 */
function determinePipelineState(manifest) {
    if (!manifest?.stages) {
        return { currentStage: 'initialized', status: 'active' };
    }
    // Walk stages in reverse order to find the most advanced completed stage
    const stageOrder = [
        'release',
        'testing',
        'validation',
        'implementation',
        'decomposition',
        'specification',
        'consensus',
        'research',
    ];
    let mostAdvancedCompleted = null;
    const hasFailed = false;
    let hasBlocked = false;
    for (const stageName of stageOrder) {
        const stageData = manifest.stages[stageName];
        if (!stageData)
            continue;
        if (stageData.status === 'completed') {
            if (!mostAdvancedCompleted) {
                mostAdvancedCompleted = stageName;
            }
        }
        if (stageData.status === 'blocked')
            hasBlocked = true;
    }
    // Determine current stage
    const currentStage = mostAdvancedCompleted || 'research';
    // Determine status
    let status;
    if (hasFailed) {
        status = 'failed';
    }
    else if (hasBlocked) {
        status = 'paused';
    }
    else if (mostAdvancedCompleted === 'release') {
        status = 'completed';
    }
    else {
        status = 'active';
    }
    return { currentStage, status };
}
/**
 * Derive a short kebab-case name from task ID and optional title.
 * @task T4801
 */
function deriveShortName(taskId, title) {
    if (title) {
        return title
            .toLowerCase()
            .replace(/[^a-z0-9\s-]/g, '')
            .replace(/\s+/g, '-')
            .slice(0, 25);
    }
    return taskId.toLowerCase();
}
//# sourceMappingURL=rcasd-index.js.map