/**
 * coverage-audit.ts
 *
 * Pure computation functions for cross-referencing the product feature registry
 * (config/features) against actual task files (tasks/*.yaml)
 * to produce a documentation coverage audit.
 *
 * Phase 3c of the Scenario Matrix implementation.
 *
 * @see docs/archive/exec-plans/scenario-matrix-implementation/phase-3-gap-analysis.md
 */
import { ConsoleLogger } from "../adapters/loggers/index.js";
import { tryLoadConfigFile } from "./compiler/config-loader.js";
import { FeatureRegistrySchema } from "./schemas.js";
import { resolveMappings } from "./resolve-mappings.js";
// ---------------------------------------------------------------------------
// Core logic (exported for testing)
// ---------------------------------------------------------------------------
/**
 * Count unique document slugs referenced across all tasks.
 */
export function countReferencedDocs(rootDir) {
    const mappings = resolveMappings(rootDir);
    const allSlugs = new Set();
    for (const config of Object.values(mappings.feature_areas)) {
        for (const task of config.tasks) {
            for (const doc of task.canonical_docs) {
                allSlugs.add(doc.slug);
            }
        }
    }
    const slugs = [...allSlugs].sort();
    return { slugs, total: slugs.length };
}
/**
 * Count actual tasks per area from task YAML files.
 */
export function countTasksByArea(rootDir) {
    const mappings = resolveMappings(rootDir);
    const counts = {};
    for (const [area, config] of Object.entries(mappings.feature_areas)) {
        counts[area] = config.tasks.length;
    }
    return counts;
}
/**
 * Format coverage audit for console output.
 */
export function formatCoverageConsole(report) {
    const lines = [];
    lines.push("═══════════════════════════════════════════════════════════════");
    lines.push(" DOCUMENTATION COVERAGE AUDIT");
    lines.push("═══════════════════════════════════════════════════════════════");
    lines.push("");
    lines.push(`Coverage: ${report.covered.length}/${report.totalFeatures} features (${report.coveragePercent}%)`);
    lines.push("");
    // Covered features
    if (report.covered.length > 0) {
        lines.push("COVERED FEATURES:");
        for (const f of report.covered) {
            const taskLabel = f.actualTaskCount === 1 ? "1 task" : `${f.actualTaskCount} tasks`;
            const sections = f.sections.join(", ");
            lines.push(`  ✅ ${f.id.padEnd(20)} ${taskLabel.padEnd(10)} ${f.priority.padEnd(10)} ${sections}`);
        }
        lines.push("");
    }
    // Uncovered features
    if (report.uncovered.length > 0) {
        lines.push("UNCOVERED FEATURES (by priority):");
        for (const f of report.uncovered) {
            const sections = f.sections.join(", ");
            lines.push(`  ❌ ${f.id.padEnd(20)} ${f.priority.padEnd(10)} ${sections}`);
        }
        lines.push("");
    }
    return lines.join("\n");
}
/**
 * Format coverage audit as markdown.
 */
export function formatCoverageMarkdown(report) {
    const lines = [];
    lines.push("### 📊 Documentation Coverage Audit");
    lines.push("");
    lines.push(`**Coverage: ${report.covered.length}/${report.totalFeatures} features (${report.coveragePercent}%)**`);
    lines.push("");
    if (report.covered.length > 0) {
        lines.push("#### Covered Features");
        lines.push("");
        lines.push("| Feature | Tasks | Priority | Sections |");
        lines.push("|---------|-------|----------|----------|");
        for (const f of report.covered) {
            lines.push(`| ✅ ${f.name} | ${f.actualTaskCount} | ${f.priority} | ${f.sections.join(", ")} |`);
        }
        lines.push("");
    }
    if (report.uncovered.length > 0) {
        lines.push("#### Uncovered Features");
        lines.push("");
        lines.push("| Feature | Priority | Sections |");
        lines.push("|---------|----------|----------|");
        for (const f of report.uncovered) {
            lines.push(`| ❌ ${f.name} | ${f.priority} | ${f.sections.join(", ")} |`);
        }
        lines.push("");
    }
    return lines.join("\n");
}
// ---------------------------------------------------------------------------
// Formatting
// ---------------------------------------------------------------------------
/**
 * Load and validate the feature registry.
 *
 * Resolution order:
 *   1. config/features file (user overrides) — if non-empty, wins
 *   2. Registry features (preset-provided) — fallback when config is empty
 *   3. null — no features available
 */
export function loadFeatureRegistry(rootDir, options) {
    const log = options?.logger ?? new ConsoleLogger();
    // Priority 1: config file (user overrides)
    const loaded = tryLoadConfigFile("features", rootDir);
    if (loaded) {
        // Check for intentionally empty config (stub for preset override).
        // The Zod schema requires .min(1), so an empty features array would
        // fail validation — but that's the expected state when the preset
        // provides features and the config file is just an override point.
        const raw = loaded.data;
        if (Array.isArray(raw?.features) && raw.features.length === 0) {
            // Empty config — fall through to registry
        }
        else {
            const result = FeatureRegistrySchema.safeParse(loaded.data);
            if (!result.success) {
                log.error("❌ config/features validation failed:");
                for (const issue of result.error.issues) {
                    log.error(`  ${issue.path.join(".")}: ${issue.message}`);
                }
                return null;
            }
            return result.data.features;
        }
    }
    // Priority 2: registry (preset-provided features)
    const registryFeatures = options?.registry?.getFeatureDefs();
    if (registryFeatures && registryFeatures.features.length > 0) {
        return registryFeatures.features;
    }
    return null;
}
/**
 * Run the coverage audit and produce a structured report.
 */
export function runCoverageAudit(rootDir, options) {
    const features = loadFeatureRegistry(rootDir, {
        registry: options?.registry,
    });
    if (!features)
        return null;
    const taskCounts = countTasksByArea(rootDir);
    const totalFeatures = features.length;
    const covered = [];
    const uncovered = [];
    for (const feature of features) {
        if (feature.status === "covered" && feature.area) {
            const actualTaskCount = taskCounts[feature.area] ?? 0;
            covered.push({ ...feature, actualTaskCount });
        }
        else if (feature.status === "uncovered" || feature.status === "planned") {
            uncovered.push(feature);
        }
    }
    // Sort uncovered by priority
    const priorityOrder = { critical: 0, high: 1, low: 3, medium: 2 };
    uncovered.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
    const coveredCount = covered.length;
    const coveragePercent = totalFeatures > 0 ? (coveredCount / totalFeatures) * 100 : 0;
    return {
        coveragePercent: Math.round(coveragePercent * 10) / 10,
        covered,
        generatedAt: new Date().toISOString(),
        totalFeatures,
        uncovered,
    };
}
