/**
 * Output Rendering Module (Module E)
 *
 * SPARC Architecture Reference: §2.6
 *
 * PURPOSE: Format output for display
 *
 * DEPENDENCIES: None
 *
 * FORBIDDEN:
 * - Data Transformation (NO semantic changes)
 * - Data Validation (NO schema checking)
 * - Business Logic (NO conditional processing)
 * - Side Effects (NO logging or metrics)
 */
import { CLIError } from '../errors/index.js';
// ============================================================================
// Output Renderer Implementation
// ============================================================================
export class OutputRenderer {
    /**
     * Render arbitrary data to specified format
     * PASSTHROUGH: No transformation of data semantics
     */
    render(data, options) {
        switch (options.format) {
            case 'json':
                return this.toJson(data, options.pretty ?? false);
            case 'yaml':
                return this.toYaml(data);
            case 'table':
                return this.toTable(data);
            case 'csv':
                return this.toCsv(data);
            case 'text':
            default:
                return this.toText(data);
        }
    }
    /**
     * Render a reference object
     */
    renderReference(ref, options) {
        return this.render(ref, options);
    }
    /**
     * Render orchestration result
     */
    renderResult(result, options) {
        return this.render(result, options);
    }
    /**
     * Render error information
     * Stack traces are excluded by default (only shown when includeStack is true)
     */
    renderError(error, options) {
        // Create a sanitized error object without stack trace (unless verbose/includeStack)
        const sanitizedError = {
            code: error.code,
            category: error.category,
            message: error.message,
        };
        // Include optional fields if present
        if (error.details && Object.keys(error.details).length > 0) {
            sanitizedError['details'] = error.details;
        }
        if (error.correlationId) {
            sanitizedError['correlationId'] = error.correlationId;
        }
        if (error.timestamp) {
            sanitizedError['timestamp'] = error.timestamp;
        }
        if (error.module) {
            sanitizedError['module'] = error.module;
        }
        // Only include stack trace in verbose mode
        if (options.includeStack && error.stack) {
            sanitizedError['stack'] = error.stack;
        }
        if (options.format === 'json') {
            return this.toJson(sanitizedError, options.pretty ?? false);
        }
        // Text format for errors
        let output = `Error [${error.code}]: ${error.message}`;
        if (error.details && Object.keys(error.details).length > 0) {
            output += `\n  Details: ${JSON.stringify(error.details)}`;
        }
        if (error.correlationId) {
            output += `\n  Correlation ID: ${error.correlationId}`;
        }
        return output;
    }
    /**
     * Render a ruvector-rooted execution span tree for CLI output.
     * Shows ruvector as root with downstream cores, repos, and agent spans.
     *
     * GUARDS (no fallback):
     * - Refuses to render if root is not ruvector-service
     * - Refuses to render partial graphs (spans still in 'started' status)
     */
    renderSpanTree(graph, options) {
        // Guard: refuse non-ruvector root
        if (graph.root.service !== 'ruvector-service') {
            throw new CLIError({
                code: 'ECLI-RUVECTOR-005',
                category: 'INTERNAL_ERROR',
                message: `Refusing to render span tree: root is not ruvector-service (actual: ${graph.root.service})`,
                details: { actual_root: graph.root.service },
                module: 'output-renderer',
                recoverable: false,
                exitCode: 141,
            });
        }
        // Guard: refuse partial graphs
        const pendingCount = this.countPendingSpans(graph.root);
        if (pendingCount > 0) {
            throw new CLIError({
                code: 'ECLI-RUVECTOR-004',
                category: 'INTERNAL_ERROR',
                message: `Refusing to render partial graph: ${pendingCount} span(s) still pending`,
                details: { pending_count: pendingCount },
                module: 'output-renderer',
                recoverable: false,
                exitCode: 141,
            });
        }
        if (options.format === 'json') {
            return this.toJson(graph, options.pretty ?? false);
        }
        return this.spanTreeToText(graph.root, 0);
    }
    spanTreeToText(span, depth) {
        const statusIcon = span.status === 'completed' ? '[OK]'
            : span.status === 'failed' ? '[FAIL]'
                : '[...]';
        const timing = span.duration_ms !== undefined ? ` (${span.duration_ms}ms)` : '';
        let line;
        if (depth === 0) {
            // Root node — special formatting
            line = `[ROOT] ${statusIcon} ${span.service}::${span.operation}${timing}`;
            line += '\n' + '='.repeat(line.length);
        }
        else {
            const prefix = '  '.repeat(depth - 1) + '|-- ';
            line = `${prefix}${statusIcon} ${span.service}::${span.operation}${timing}`;
        }
        const lines = [line];
        for (const child of span.children) {
            lines.push(this.spanTreeToText(child, depth + 1));
        }
        return lines.join('\n');
    }
    countPendingSpans(span) {
        let count = span.status === 'started' ? 1 : 0;
        for (const child of span.children) {
            count += this.countPendingSpans(child);
        }
        return count;
    }
    // ============================================================================
    // Format Converters
    // ============================================================================
    toJson(data, pretty) {
        return pretty
            ? JSON.stringify(data, null, 2)
            : JSON.stringify(data);
    }
    toYaml(data) {
        // Simple YAML conversion (no external dependencies per SPARC)
        return this.objectToYaml(data, 0);
    }
    objectToYaml(data, indent) {
        const prefix = '  '.repeat(indent);
        if (data === null || data === undefined) {
            return 'null';
        }
        if (typeof data === 'string') {
            // Quote strings that need it
            if (data.includes('\n') || data.includes(':') || data.includes('#')) {
                return `"${data.replace(/"/g, '\\"')}"`;
            }
            return data;
        }
        if (typeof data === 'number' || typeof data === 'boolean') {
            return String(data);
        }
        if (Array.isArray(data)) {
            if (data.length === 0)
                return '[]';
            return data
                .map(item => `${prefix}- ${this.objectToYaml(item, indent + 1).trimStart()}`)
                .join('\n');
        }
        if (typeof data === 'object') {
            const obj = data;
            const keys = Object.keys(obj);
            if (keys.length === 0)
                return '{}';
            return keys
                .map(key => {
                const value = obj[key];
                const valueStr = this.objectToYaml(value, indent + 1);
                if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
                    return `${prefix}${key}:\n${valueStr}`;
                }
                return `${prefix}${key}: ${valueStr}`;
            })
                .join('\n');
        }
        return String(data);
    }
    toTable(data) {
        if (!data || typeof data !== 'object') {
            return String(data);
        }
        // Handle array of objects
        if (Array.isArray(data)) {
            if (data.length === 0)
                return '(empty)';
            const firstItem = data[0];
            if (typeof firstItem !== 'object' || firstItem === null) {
                return data.map(String).join('\n');
            }
            const headers = Object.keys(firstItem);
            const rows = data.map(item => {
                const obj = item;
                return headers.map(h => String(obj[h] ?? '')).join('\t');
            });
            return [headers.join('\t'), ...rows].join('\n');
        }
        // Handle single object
        const obj = data;
        return Object.entries(obj)
            .map(([key, value]) => `${key}: ${this.formatValue(value)}`)
            .join('\n');
    }
    toCsv(data) {
        if (!Array.isArray(data)) {
            return this.toTable(data);
        }
        if (data.length === 0)
            return '';
        const firstItem = data[0];
        if (typeof firstItem !== 'object' || firstItem === null) {
            return data.map(item => this.escapeCsv(String(item))).join('\n');
        }
        const headers = Object.keys(firstItem);
        const headerRow = headers.map(h => this.escapeCsv(h)).join(',');
        const rows = data.map(item => {
            const obj = item;
            return headers.map(h => this.escapeCsv(String(obj[h] ?? ''))).join(',');
        });
        return [headerRow, ...rows].join('\n');
    }
    escapeCsv(value) {
        if (value.includes(',') || value.includes('"') || value.includes('\n')) {
            return `"${value.replace(/"/g, '""')}"`;
        }
        return value;
    }
    toText(data) {
        if (typeof data === 'string')
            return data;
        if (data === null || data === undefined)
            return '';
        if (typeof data === 'object') {
            return JSON.stringify(data, null, 2);
        }
        return String(data);
    }
    formatValue(value) {
        if (value === null || value === undefined)
            return '';
        if (typeof value === 'object')
            return JSON.stringify(value);
        return String(value);
    }
}
// ============================================================================
// Factory Function
// ============================================================================
export function createOutputRenderer() {
    return new OutputRenderer();
}
//# sourceMappingURL=output-renderer.js.map