/**
 * Phase 4 Command
 *
 * Generates Architecture Decision Records (ADRs) and Domain-Driven Design
 * models (DDDs) from the Phase 2 research dossier and Phase 3 SPARC + TDD
 * output:
 *   - Prior Phase Loading (research dossier + SPARC + TDD from disk)
 *   - ADR Generation (citing SPARC sections + dossier items)
 *   - DDD Model Generation (bounded contexts, aggregates, ubiquitous language)
 *
 * Usage:
 *   agentics phase4 --trace <TRACE_ID>
 *   agentics phase4 --latest
 *
 * Error codes:
 *   ECLI-P4-001  Missing Phase 2 or Phase 3 artifacts
 *   ECLI-P4-005  No runs found (--latest with empty runs dir)
 *   ECLI-P4-006  Phase 4 output directory already exists (append-only)
 */
import * as fs from 'node:fs';
import * as path from 'node:path';
import { CLIError } from '../errors/index.js';
import { executePhase4ADRPipeline } from '../pipeline/phase4-adrs/index.js';
// ============================================================================
// Constants
// ============================================================================
const RUNS_DIR_NAME = '.agentics/runs';
const PHASE2_SUBDIR = 'phase2';
const PHASE3_SUBDIR = 'phase3';
const PHASE4_SUBDIR = 'phase4';
const DIR_MODE = 0o700;
// ============================================================================
// Command Executor
// ============================================================================
/**
 * Execute the `agentics phase4` command.
 *
 * Resolves the Phase 1 run directory (via --trace or --latest), validates
 * that Phase 2 and Phase 3 exist and Phase 4 does not, constructs the
 * Phase4ADRContext, and delegates to the pipeline coordinator.
 */
export async function executePhase4Command(options) {
    const homeDir = process.env['HOME'] ?? '/tmp';
    const runsDir = path.join(homeDir, RUNS_DIR_NAME);
    // ── Resolve Phase 1 run directory ──
    const runDir = options.trace
        ? path.join(runsDir, options.trace)
        : options.latest
            ? findLatestRunDir(runsDir)
            : null;
    if (!runDir) {
        throw new CLIError({
            code: 'ECLI-P4-001',
            category: 'INPUT_ERROR',
            message: 'No trace ID specified. Use --trace <TRACE_ID> or --latest.',
            module: 'phase4',
            recoverable: false,
            exitCode: 1,
        });
    }
    // ── Validate run directory exists ──
    if (!fs.existsSync(runDir)) {
        throw new CLIError({
            code: 'ECLI-P4-001',
            category: 'INPUT_ERROR',
            message: `Run directory not found: ${runDir}`,
            details: { runDir },
            module: 'phase4',
            recoverable: false,
            exitCode: 1,
        });
    }
    // ── Validate Phase 2 subdirectory exists ──
    const phase2Dir = path.join(runDir, PHASE2_SUBDIR);
    if (!fs.existsSync(phase2Dir)) {
        throw new CLIError({
            code: 'ECLI-P4-001',
            category: 'INPUT_ERROR',
            message: `Phase 2 output not found. Run 'agentics phase2' first: ${phase2Dir}`,
            details: { phase2Dir },
            module: 'phase4',
            recoverable: false,
            exitCode: 1,
        });
    }
    // ── Validate Phase 3 subdirectory exists with SPARC output ──
    const phase3Dir = path.join(runDir, PHASE3_SUBDIR);
    if (!fs.existsSync(phase3Dir)) {
        throw new CLIError({
            code: 'ECLI-P4-001',
            category: 'INPUT_ERROR',
            message: `Phase 3 output not found. Run 'agentics phase3' first: ${phase3Dir}`,
            details: { phase3Dir },
            module: 'phase4',
            recoverable: false,
            exitCode: 1,
        });
    }
    // Accept SPARC from Phase 3 or fall back to Phase 2
    const sparcCombinedPath = path.join(phase3Dir, 'sparc', 'sparc-combined.json');
    const phase2SparcFallback = path.join(phase2Dir, 'sparc', 'sparc-combined.json');
    if (!fs.existsSync(sparcCombinedPath) && fs.existsSync(phase2SparcFallback)) {
        // Phase 3 didn't produce SPARC (timeout/failure) — copy Phase 2 SPARC so Phase 4 can proceed
        console.error('  [RECOVER] Phase 3 SPARC not found — using Phase 2 SPARC as fallback');
        const phase3SparcDir = path.join(phase3Dir, 'sparc');
        fs.mkdirSync(phase3SparcDir, { recursive: true });
        fs.copyFileSync(phase2SparcFallback, sparcCombinedPath);
    }
    if (!fs.existsSync(sparcCombinedPath)) {
        throw new CLIError({
            code: 'ECLI-P4-001',
            category: 'INPUT_ERROR',
            message: `SPARC output not found in Phase 3 or Phase 2`,
            details: { sparcCombinedPath, phase2SparcFallback },
            module: 'phase4',
            recoverable: false,
            exitCode: 1,
        });
    }
    // ── Append-only guard: Phase 4 must not already have completed output ──
    // Allow re-running if the directory exists but has no ADR index (failed prior run)
    const phase4Dir = path.join(runDir, PHASE4_SUBDIR);
    const phase4AdrIndex = path.join(phase4Dir, 'adrs', 'adr-index.json');
    if (fs.existsSync(phase4Dir) && fs.existsSync(phase4AdrIndex)) {
        throw new CLIError({
            code: 'ECLI-P4-006',
            category: 'INPUT_ERROR',
            message: `Phase 4 already exists for this run. Output is append-only: ${phase4Dir}`,
            details: { phase4Dir },
            module: 'phase4',
            recoverable: false,
            exitCode: 1,
        });
    }
    // ── Create Phase 4 output directory ──
    fs.mkdirSync(phase4Dir, { recursive: true, mode: DIR_MODE });
    // ── Derive trace ID from directory name ──
    const traceId = path.basename(runDir);
    // ── Read scenario query from Phase 1 manifest ──
    let scenarioQuery = '';
    try {
        const manifestPath = path.join(runDir, 'manifest.json');
        if (fs.existsSync(manifestPath)) {
            const raw = fs.readFileSync(manifestPath, 'utf-8');
            const manifest = JSON.parse(raw);
            scenarioQuery = manifest.query ?? '';
        }
    }
    catch {
        // Best-effort; the pipeline will proceed without it
    }
    // ── Construct Phase4ADRContext ──
    const context = {
        traceId,
        phase2Dir,
        phase3Dir,
        phase4Dir,
        scenarioQuery,
        stageOutputs: new Map(),
        telemetrySpans: [],
    };
    // ── Execute pipeline ──
    const result = await executePhase4ADRPipeline(context);
    return result;
}
// ============================================================================
// Formatting
// ============================================================================
/**
 * Format Phase 4 result for human-readable display.
 */
export function formatPhase4ForDisplay(result) {
    const lines = [];
    lines.push('');
    lines.push('Phase 4: ADRs + DDDs');
    lines.push('='.repeat(50));
    lines.push('');
    lines.push(`  Trace ID:    ${result.traceId}`);
    lines.push(`  Output:      ${result.phase4Dir}`);
    lines.push(`  Duration:    ${result.totalTiming}ms`);
    lines.push(`  Stages:      ${result.stageResults.length}`);
    lines.push(`  Artifacts:   ${result.manifest.artifacts.length} files`);
    lines.push(`  Success:     ${result.manifest.success}`);
    lines.push('');
    // Stage summary
    lines.push('  Stages:');
    for (const stage of result.stageResults) {
        const icon = stage.status === 'completed' ? '[OK]' : '[FAIL]';
        lines.push(`    ${icon} ${stage.stage} (${stage.timing}ms)`);
    }
    lines.push('');
    // Check for failures
    const failed = result.stageResults.find(s => s.status === 'failed');
    if (failed) {
        lines.push(`  ERROR: Pipeline failed at stage "${failed.stage}"`);
        const failData = failed.data;
        if (failData && typeof failData === 'object' && 'error' in failData) {
            lines.push(`         ${failData['error']}`);
        }
        lines.push('');
    }
    return lines.join('\n');
}
// ============================================================================
// Helpers
// ============================================================================
/**
 * Find the most recent Phase 1 run directory that has both Phase 2 and
 * Phase 3 subdirectories, with the Phase 3 sparc-combined.json present.
 */
function findLatestRunDir(runsDir) {
    if (!fs.existsSync(runsDir)) {
        throw new CLIError({
            code: 'ECLI-P4-005',
            category: 'INPUT_ERROR',
            message: `Runs directory not found: ${runsDir}`,
            details: { runsDir },
            module: 'phase4',
            recoverable: false,
            exitCode: 1,
        });
    }
    let latestDir = '';
    let latestTime = '';
    const entries = fs.readdirSync(runsDir, { withFileTypes: true });
    for (const entry of entries) {
        if (!entry.isDirectory())
            continue;
        const runPath = path.join(runsDir, entry.name);
        const phase2Path = path.join(runPath, PHASE2_SUBDIR);
        const phase3Path = path.join(runPath, PHASE3_SUBDIR);
        const sparcPath = path.join(phase3Path, 'sparc', 'sparc-combined.json');
        // Only consider runs that have Phase 2 output, Phase 3 output, and SPARC
        if (!fs.existsSync(phase2Path))
            continue;
        if (!fs.existsSync(phase3Path))
            continue;
        if (!fs.existsSync(sparcPath))
            continue;
        const manifestPath = path.join(runPath, 'manifest.json');
        if (!fs.existsSync(manifestPath))
            continue;
        try {
            const raw = fs.readFileSync(manifestPath, 'utf-8');
            const manifest = JSON.parse(raw);
            const createdAt = manifest.created_at ?? '';
            if (createdAt > latestTime) {
                latestTime = createdAt;
                latestDir = runPath;
            }
        }
        catch {
            // Skip unreadable manifests
        }
    }
    if (!latestDir) {
        throw new CLIError({
            code: 'ECLI-P4-005',
            category: 'INPUT_ERROR',
            message: 'No Phase 3 runs found. Run Phase 1, Phase 2, and Phase 3 first.',
            details: { runsDir },
            module: 'phase4',
            recoverable: false,
            exitCode: 1,
        });
    }
    return latestDir;
}
//# sourceMappingURL=phase4.js.map