/**
 * CLI Output Formatter
 *
 * Handles formatting output for different modes (human, JSON, quiet).
 */
// ============================================================================
// Human Formatter
// ============================================================================
/**
 * Human-readable output formatter
 */
class HumanFormatter {
    success(result) {
        if (result.message) {
            return result.message;
        }
        if (result.data !== undefined) {
            return formatValue(result.data);
        }
        return '';
    }
    error(result) {
        return `Error: ${result.error}`;
    }
    table(headers, rows) {
        if (rows.length === 0) {
            return 'No results';
        }
        // Calculate column widths
        const widths = headers.map((h, i) => {
            const maxDataWidth = Math.max(...rows.map(r => String(r[i] ?? '').length));
            return Math.max(h.length, maxDataWidth);
        });
        // Build header row
        const headerRow = headers.map((h, i) => h.padEnd(widths[i])).join('  ');
        const separator = widths.map(w => '-'.repeat(w)).join('  ');
        // Build data rows
        const dataRows = rows.map(row => row.map((cell, i) => String(cell ?? '').padEnd(widths[i])).join('  '));
        return [headerRow, separator, ...dataRows].join('\n');
    }
    element(data) {
        const lines = [];
        const maxKeyLength = Math.max(...Object.keys(data).map(k => k.length));
        for (const [key, value] of Object.entries(data)) {
            const paddedKey = key.padEnd(maxKeyLength);
            lines.push(`${paddedKey}  ${formatValue(value)}`);
        }
        return lines.join('\n');
    }
    list(items) {
        if (items.length === 0) {
            return 'No results';
        }
        // Extract common display fields
        const displayFields = ['id', 'title', 'name', 'status', 'type'];
        const headers = displayFields.filter(f => items.some(item => item[f] !== undefined));
        const rows = items.map(item => headers.map(h => item[h]));
        return this.table(headers.map(h => h.toUpperCase()), rows);
    }
    tree(node, prefix = '', isLast = true) {
        const connector = isLast ? '└── ' : '├── ';
        const lines = [prefix + connector + node.label];
        if (node.children) {
            const childPrefix = prefix + (isLast ? '    ' : '│   ');
            for (let i = 0; i < node.children.length; i++) {
                const child = node.children[i];
                const childIsLast = i === node.children.length - 1;
                lines.push(this.tree(child, childPrefix, childIsLast));
            }
        }
        return lines.join('\n');
    }
}
// ============================================================================
// Verbose Formatter
// ============================================================================
/**
 * Verbose output formatter - extends human format with extra details
 */
class VerboseFormatter {
    humanFormatter = new HumanFormatter();
    success(result) {
        // Use human formatter for success, but add more details if available
        const basic = this.humanFormatter.success(result);
        // Add data details if it's an object
        if (result.data && typeof result.data === 'object' && !Array.isArray(result.data)) {
            const details = Object.entries(result.data)
                .map(([k, v]) => `  ${k}: ${JSON.stringify(v)}`)
                .join('\n');
            return `${basic}\n\nDetails:\n${details}`;
        }
        return basic;
    }
    error(result) {
        const lines = [`Error: ${result.error}`];
        // Add exit code
        lines.push(`  Code: ${result.exitCode}`);
        // Add details if available
        if (result.data && typeof result.data === 'object') {
            lines.push(`  Details: ${JSON.stringify(result.data)}`);
        }
        // Add stack trace hint
        lines.push('');
        lines.push('For more details, check the application logs.');
        return lines.join('\n');
    }
    table(headers, rows) {
        return this.humanFormatter.table(headers, rows);
    }
    element(data) {
        return this.humanFormatter.element(data);
    }
    list(items) {
        return this.humanFormatter.list(items);
    }
    tree(data) {
        return this.humanFormatter.tree(data);
    }
}
// ============================================================================
// JSON Formatter
// ============================================================================
/**
 * JSON output formatter
 */
class JsonFormatter {
    success(result) {
        return JSON.stringify({
            success: true,
            data: result.data,
        }, null, 2);
    }
    error(result) {
        return JSON.stringify({
            success: false,
            error: result.error,
            exitCode: result.exitCode,
        }, null, 2);
    }
    table(_headers, rows) {
        return JSON.stringify(rows, null, 2);
    }
    element(data) {
        return JSON.stringify(data, null, 2);
    }
    list(items) {
        return JSON.stringify(items, null, 2);
    }
    tree(data) {
        return JSON.stringify(data, null, 2);
    }
}
// ============================================================================
// Quiet Formatter
// ============================================================================
/**
 * Quiet/minimal output formatter
 */
class QuietFormatter {
    success(result) {
        // In quiet mode, only output IDs or minimal data
        if (result.data === undefined)
            return '';
        if (typeof result.data === 'string') {
            return result.data;
        }
        if (Array.isArray(result.data)) {
            return result.data
                .map(item => {
                if (typeof item === 'object' && item !== null && 'id' in item) {
                    return item.id;
                }
                return String(item);
            })
                .join('\n');
        }
        if (typeof result.data === 'object' && result.data !== null && 'id' in result.data) {
            return result.data.id;
        }
        return '';
    }
    error(result) {
        return result.error ?? 'Error';
    }
    table(_headers, rows) {
        // Output first column (typically ID)
        return rows.map(row => String(row[0] ?? '')).join('\n');
    }
    element(data) {
        // Output ID if present
        if (data.id) {
            return String(data.id);
        }
        return '';
    }
    list(items) {
        return items
            .map(item => item.id ?? '')
            .filter(Boolean)
            .join('\n');
    }
    tree(data) {
        // Flatten tree to labels only
        const flatten = (node) => {
            const result = [node.label];
            if (node.children) {
                for (const child of node.children) {
                    result.push(...flatten(child));
                }
            }
            return result;
        };
        return flatten(data).join('\n');
    }
}
// ============================================================================
// Formatter Factory
// ============================================================================
const formatters = {
    human: new HumanFormatter(),
    verbose: new VerboseFormatter(),
    json: new JsonFormatter(),
    quiet: new QuietFormatter(),
};
/**
 * Gets the appropriate formatter for the output mode
 */
export function getFormatter(mode) {
    return formatters[mode];
}
/**
 * Determines output mode from global options
 */
export function getOutputMode(options) {
    if (options.json)
        return 'json';
    if (options.quiet)
        return 'quiet';
    if (options.verbose)
        return 'verbose';
    return 'human';
}
// ============================================================================
// Helpers
// ============================================================================
/**
 * Formats a value for human display
 */
function formatValue(value) {
    if (value === null || value === undefined) {
        return '-';
    }
    if (typeof value === 'boolean') {
        return value ? 'yes' : 'no';
    }
    if (Array.isArray(value)) {
        if (value.length === 0)
            return '-';
        return value.join(', ');
    }
    if (typeof value === 'object') {
        return JSON.stringify(value);
    }
    return String(value);
}
// ============================================================================
// Status Styling
// ============================================================================
/**
 * Status display characters (can be extended with colors)
 */
export const STATUS_ICONS = {
    open: '○',
    in_progress: '◐',
    blocked: '●',
    deferred: '◌',
    closed: '✓',
    tombstone: '×',
    draft: '○',
    active: '◐',
    completed: '✓',
    abandoned: '×',
    pending: '○',
    running: '◐',
    paused: '◌',
    failed: '×',
};
/**
 * Gets the status icon for a status value
 */
export function getStatusIcon(status) {
    return STATUS_ICONS[status] ?? '?';
}
// ============================================================================
// Event Formatting
// ============================================================================
/**
 * Event type display names
 */
export const EVENT_TYPE_DISPLAY = {
    created: 'Created',
    updated: 'Updated',
    closed: 'Closed',
    reopened: 'Reopened',
    deleted: 'Deleted',
    dependency_added: 'Dependency Added',
    dependency_removed: 'Dependency Removed',
    tag_added: 'Tag Added',
    tag_removed: 'Tag Removed',
    member_added: 'Member Added',
    member_removed: 'Member Removed',
};
/**
 * Event type icons
 */
export const EVENT_TYPE_ICONS = {
    created: '+',
    updated: '~',
    closed: '✓',
    reopened: '↺',
    deleted: '×',
    dependency_added: '→',
    dependency_removed: '←',
    tag_added: '#',
    tag_removed: '#',
    member_added: '+',
    member_removed: '-',
};
/**
 * Formats a single event for human display
 */
export function formatEvent(event) {
    const icon = EVENT_TYPE_ICONS[event.eventType] ?? '?';
    const type = EVENT_TYPE_DISPLAY[event.eventType] ?? event.eventType;
    const time = formatTimestamp(event.createdAt);
    const actor = event.actor.replace(/^.*:/, ''); // Remove prefix (e.g., "user:alice" -> "alice")
    return `${icon} ${type} by ${actor} at ${time}`;
}
/**
 * Formats a timestamp for display
 */
export function formatTimestamp(timestamp) {
    try {
        const date = new Date(timestamp);
        const now = new Date();
        const diff = now.getTime() - date.getTime();
        // Within last minute
        if (diff < 60_000) {
            return 'just now';
        }
        // Within last hour
        if (diff < 3_600_000) {
            const mins = Math.floor(diff / 60_000);
            return `${mins}m ago`;
        }
        // Within last day
        if (diff < 86_400_000) {
            const hours = Math.floor(diff / 3_600_000);
            return `${hours}h ago`;
        }
        // Within last week
        if (diff < 604_800_000) {
            const days = Math.floor(diff / 86_400_000);
            return `${days}d ago`;
        }
        // Otherwise show date
        return date.toLocaleDateString();
    }
    catch {
        return timestamp;
    }
}
/**
 * Formats events as a timeline
 */
export function formatTimeline(events) {
    if (events.length === 0) {
        return 'No events';
    }
    const lines = [];
    for (const event of events) {
        lines.push(formatEvent(event));
    }
    return lines.join('\n');
}
/**
 * Formats events as a detailed table
 */
export function formatEventsTable(events) {
    if (events.length === 0) {
        return 'No events';
    }
    const headers = ['TIME', 'TYPE', 'ACTOR', 'CHANGES'];
    const rows = events.map(event => {
        const time = formatTimestamp(event.createdAt);
        const type = EVENT_TYPE_DISPLAY[event.eventType] ?? event.eventType;
        const actor = event.actor.replace(/^.*:/, '');
        const changes = formatChanges(event);
        return [time, type, actor, changes];
    });
    // Calculate column widths
    const widths = headers.map((h, i) => {
        const maxDataWidth = Math.max(...rows.map(r => String(r[i] ?? '').length));
        return Math.max(h.length, maxDataWidth);
    });
    // Build output
    const headerRow = headers.map((h, i) => h.padEnd(widths[i])).join('  ');
    const separator = widths.map(w => '-'.repeat(w)).join('  ');
    const dataRows = rows.map(row => row.map((cell, i) => String(cell ?? '').padEnd(widths[i])).join('  '));
    return [headerRow, separator, ...dataRows].join('\n');
}
/**
 * Formats the changes from an event
 */
function formatChanges(event) {
    if (event.eventType === 'created') {
        return 'New element';
    }
    if (event.eventType === 'deleted') {
        return 'Element deleted';
    }
    if (event.eventType === 'closed') {
        return 'Status → closed';
    }
    if (event.eventType === 'reopened') {
        return 'Status → open';
    }
    if (event.eventType === 'dependency_added' || event.eventType === 'dependency_removed') {
        const depInfo = event.newValue ?? event.oldValue;
        if (depInfo && typeof depInfo === 'object') {
            const blockerId = depInfo.blockerId;
            const depType = depInfo.type;
            if (blockerId) {
                return `${depType ?? 'dep'}: ${blockerId}`;
            }
        }
        return event.eventType === 'dependency_added' ? 'Added' : 'Removed';
    }
    // For updates, show changed fields
    if (event.oldValue && event.newValue) {
        const changes = [];
        for (const key of Object.keys(event.newValue)) {
            if (key === 'updatedAt' || key === 'createdAt' || key === 'id' || key === 'type')
                continue;
            const oldVal = event.oldValue[key];
            const newVal = event.newValue[key];
            if (JSON.stringify(oldVal) !== JSON.stringify(newVal)) {
                const oldStr = oldVal === undefined || oldVal === null ? '-' : String(oldVal);
                const newStr = newVal === undefined || newVal === null ? '-' : String(newVal);
                if (oldStr.length > 20) {
                    changes.push(`${key} changed`);
                }
                else {
                    changes.push(`${key}: ${oldStr} → ${newStr}`);
                }
            }
        }
        if (changes.length > 0) {
            return changes.slice(0, 2).join(', ') + (changes.length > 2 ? '...' : '');
        }
    }
    return '-';
}
//# sourceMappingURL=formatter.js.map