/**
 * ProgressTracker - Real-time progress tracking with ETA calculation
 *
 * Features:
 * - ASCII progress bar (30 characters)
 * - Linear ETA extrapolation
 * - Elapsed time tracking
 * - Project-level status display (✅ ❌ ⏳)
 * - Configurable update frequency
 *
 * @module core/progress/progress-tracker
 */
import { consoleLogger } from '../../utils/logger.js';
/**
 * Format seconds into human-readable time string
 */
function formatTime(seconds) {
    if (seconds < 60) {
        return `${seconds}s`;
    }
    const minutes = Math.floor(seconds / 60);
    const remainingSeconds = seconds % 60;
    return `${minutes}m ${remainingSeconds}s`;
}
/**
 * ProgressTracker - Real-time progress tracking with ASCII progress bar and ETA
 *
 * @example
 * ```typescript
 * const tracker = new ProgressTracker({
 *   total: 127,
 *   label: 'Importing projects',
 *   showEta: true,
 *   updateFrequency: 5
 * });
 *
 * for (const project of projects) {
 *   await importProject(project);
 *   tracker.update(project.key, 'success');
 * }
 *
 * tracker.finish(120, 5, 2);
 * ```
 */
export class ProgressTracker {
    constructor(options) {
        this.items = new Map();
        this.lastUpdateCount = 0;
        this.total = options.total;
        this.label = options.label;
        this.showEta = options.showEta ?? true;
        this.updateFrequency = options.updateFrequency ?? 5;
        this.logger = options.logger ?? consoleLogger;
        this.startTime = Date.now();
    }
    /**
     * Update progress for a specific item
     *
     * @param itemName Item identifier (e.g., project key)
     * @param status Item status (pending, success, error)
     */
    update(itemName, status) {
        // Track item
        this.items.set(itemName, {
            name: itemName,
            status,
            timestamp: Date.now(),
        });
        const completed = this.getCompletedCount();
        // Only render if we've processed enough items since last update
        if (completed - this.lastUpdateCount >= this.updateFrequency ||
            completed === this.total) {
            this.render();
            this.lastUpdateCount = completed;
        }
    }
    /**
     * Finish progress tracking and show final summary
     *
     * @param succeeded Count of successful items
     * @param failed Count of failed items
     * @param skipped Count of skipped items
     */
    finish(succeeded, failed, skipped, options) {
        this.render();
        const totalTimeSec = Math.floor((Date.now() - this.startTime) / 1000);
        console.log('');
        console.log(`✅ Import Complete!`);
        console.log('');
        console.log(`Imported: ${succeeded} projects`);
        if (failed > 0) {
            const errorLogHint = options?.errorLogFile ? ` (see ${options.errorLogFile})` : '';
            console.log(`Failed: ${failed} projects${errorLogHint}`);
            if (options?.showErrorDetails) {
                const failedItems = this.getItemsByStatus('error').slice(0, 5);
                for (const item of failedItems) {
                    console.log(`  ❌ ${item.name}`);
                }
                if (failed > 5) {
                    console.log(`  ... and ${failed - 5} more errors`);
                }
            }
        }
        if (skipped > 0) {
            console.log(`Skipped: ${skipped} projects`);
        }
        console.log('');
        console.log(`Total time: ${formatTime(totalTimeSec)}`);
        console.log('');
    }
    /**
     * Render progress bar to console
     */
    render() {
        const completed = this.getCompletedCount();
        const percentage = Math.round((completed / this.total) * 100);
        const progressBar = this.renderProgressBar(percentage);
        const elapsed = this.getElapsedTime();
        const eta = this.showEta ? this.getEta() : '';
        // Clear current line and print progress
        process.stdout.write('\r'); // Carriage return to beginning of line
        process.stdout.write(`${this.label}... ${completed}/${this.total} (${percentage}%) ${progressBar} [${elapsed}${eta}]`);
    }
    /**
     * Render ASCII progress bar
     *
     * @param percentage Completion percentage (0-100)
     * @returns ASCII progress bar (30 characters)
     *
     * @example
     * renderProgressBar(0)   => "[>                             ]"
     * renderProgressBar(37)  => "[==========>                   ]"
     * renderProgressBar(100) => "[==============================]"
     */
    renderProgressBar(percentage) {
        const barLength = 30;
        const filledLength = Math.round((percentage / 100) * barLength);
        if (filledLength === 0) {
            return `[>${' '.repeat(barLength - 1)}]`;
        }
        const filled = '='.repeat(filledLength - 1) + '>';
        const empty = ' '.repeat(barLength - filledLength);
        return `[${filled}${empty}]`;
    }
    /**
     * Get elapsed time in human-readable format
     *
     * @returns Elapsed time string (e.g., "2m 34s elapsed", "45s elapsed")
     */
    getElapsedTime() {
        const elapsedSeconds = Math.floor((Date.now() - this.startTime) / 1000);
        return `${formatTime(elapsedSeconds)} elapsed`;
    }
    /**
     * Get estimated time remaining (ETA) using linear extrapolation
     *
     * Formula: ETA = (total - completed) * (elapsed / completed)
     *
     * @returns ETA string (e.g., ", ~2m remaining", ", ~45s remaining")
     */
    getEta() {
        const completed = this.getCompletedCount();
        if (completed === 0) {
            return ', ~? remaining';
        }
        if (completed === this.total) {
            return ', ~0s remaining';
        }
        const elapsedMs = Date.now() - this.startTime;
        const etaSeconds = Math.floor(((this.total - completed) * elapsedMs) / completed / 1000);
        return `, ~${formatTime(etaSeconds)} remaining`;
    }
    /**
     * Get count of completed items (success + error)
     */
    getCompletedCount() {
        return Array.from(this.items.values()).filter(item => item.status === 'success' || item.status === 'error').length;
    }
    /**
     * Get items by status
     */
    getItemsByStatus(status) {
        return Array.from(this.items.values()).filter(item => item.status === status);
    }
    /**
     * Get current progress statistics
     */
    getStats() {
        const completed = this.getCompletedCount();
        const succeeded = this.getItemsByStatus('success').length;
        const failed = this.getItemsByStatus('error').length;
        const pending = this.getItemsByStatus('pending').length;
        const percentage = Math.round((completed / this.total) * 100);
        const elapsedMs = Date.now() - this.startTime;
        let etaMs = null;
        if (completed > 0 && completed < this.total) {
            const avgTimePerItem = elapsedMs / completed;
            const remaining = this.total - completed;
            etaMs = remaining * avgTimePerItem;
        }
        return {
            total: this.total,
            completed,
            succeeded,
            failed,
            pending,
            percentage,
            elapsedMs,
            etaMs,
        };
    }
}
//# sourceMappingURL=progress-tracker.js.map