/**
 * Multi-Step Progress Component
 *
 * Shows step-by-step progress with visual indicators, progress bar,
 * and time estimation. Designed for multi-step operations.
 */
import React, { useState, useEffect, useMemo } from "react";
import { Box, Text } from "ink";
/**
 * Spinner frames for running state
 */
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
/**
 * Format duration in human-readable format
 */
function formatDuration(ms) {
    if (ms < 1000)
        return `${ms}ms`;
    if (ms < 60000)
        return `${(ms / 1000).toFixed(1)}s`;
    const mins = Math.floor(ms / 60000);
    const secs = Math.floor((ms % 60000) / 1000);
    return `${mins}m ${secs}s`;
}
/**
 * Status icons and colors
 */
const STATUS_CONFIG = {
    pending: { icon: "○", color: "gray", textLabel: "[PENDING]" },
    running: { icon: "●", color: "cyan", textLabel: "[RUNNING]" },
    completed: { icon: "✓", color: "green", textLabel: "[DONE]" },
    failed: { icon: "✗", color: "red", textLabel: "[FAILED]" },
    skipped: { icon: "⊘", color: "yellow", textLabel: "[SKIPPED]" },
};
/**
 * Multi-Step Progress Component
 */
export function MultiStepProgress({ steps, title, showProgressBar = true, showTimeEstimate = true, showStepDetails = false, compact = false, width = 50, }) {
    const [spinnerFrame, setSpinnerFrame] = useState(0);
    const [elapsedTime, setElapsedTime] = useState(0);
    // Calculate progress stats
    const stats = useMemo(() => {
        const total = steps.length;
        const completed = steps.filter((s) => s.status === "completed").length;
        const failed = steps.filter((s) => s.status === "failed").length;
        const skipped = steps.filter((s) => s.status === "skipped").length;
        const running = steps.find((s) => s.status === "running");
        const currentIndex = steps.findIndex((s) => s.status === "running");
        // Calculate total duration from completed steps
        const totalDuration = steps
            .filter((s) => s.duration !== undefined)
            .reduce((sum, s) => sum + (s.duration || 0), 0);
        // Estimate remaining time based on average
        const avgDuration = completed > 0 ? totalDuration / completed : 0;
        const remainingSteps = total - completed - failed - skipped;
        const estimatedRemaining = avgDuration * remainingSteps;
        const progress = total > 0 ? ((completed + failed + skipped) / total) * 100 : 0;
        return {
            total,
            completed,
            failed,
            skipped,
            running,
            currentIndex,
            progress,
            totalDuration,
            estimatedRemaining,
        };
    }, [steps]);
    // Animate spinner for running state
    useEffect(() => {
        if (!stats.running)
            return;
        const interval = setInterval(() => {
            setSpinnerFrame((prev) => (prev + 1) % SPINNER_FRAMES.length);
        }, 80);
        return () => clearInterval(interval);
    }, [stats.running]);
    // Track elapsed time
    useEffect(() => {
        if (!stats.running)
            return;
        const interval = setInterval(() => {
            setElapsedTime((prev) => prev + 100);
        }, 100);
        return () => clearInterval(interval);
    }, [stats.running]);
    // Reset elapsed time when step changes
    useEffect(() => {
        setElapsedTime(0);
    }, [stats.currentIndex]);
    // Render progress bar
    const renderProgressBar = () => {
        const barWidth = width - 10; // Leave room for percentage
        const filledWidth = Math.round((stats.progress / 100) * barWidth);
        const emptyWidth = barWidth - filledWidth;
        const filled = "█".repeat(filledWidth);
        const empty = "░".repeat(emptyWidth);
        return (React.createElement(Box, { marginY: 1 },
            React.createElement(Text, { color: "green" }, filled),
            React.createElement(Text, { dimColor: true }, empty),
            React.createElement(Text, null,
                " ",
                stats.progress.toFixed(0),
                "%")));
    };
    // Render step icon with animation
    const getStepIcon = (status) => {
        if (status === "running") {
            return SPINNER_FRAMES[spinnerFrame];
        }
        return STATUS_CONFIG[status].icon;
    };
    // Compact view - single line progress
    if (compact) {
        return (React.createElement(Box, { flexDirection: "column" },
            React.createElement(Box, null,
                React.createElement(Text, { color: "cyan" },
                    getStepIcon(stats.running ? "running" : "pending"),
                    " "),
                React.createElement(Text, null,
                    "Step ",
                    stats.currentIndex + 1,
                    "/",
                    stats.total,
                    ":",
                    " "),
                React.createElement(Text, { bold: true }, stats.running?.name || "Waiting..."),
                showTimeEstimate && elapsedTime > 0 && (React.createElement(Text, { dimColor: true },
                    " (",
                    formatDuration(elapsedTime),
                    ")"))),
            showProgressBar && (React.createElement(Box, { marginLeft: 2 },
                React.createElement(Text, { color: "green" }, "█".repeat(Math.round((stats.progress / 100) * 20))),
                React.createElement(Text, { dimColor: true }, "░".repeat(20 - Math.round((stats.progress / 100) * 20))),
                React.createElement(Text, null,
                    " ",
                    stats.progress.toFixed(0),
                    "%")))));
    }
    return (React.createElement(Box, { flexDirection: "column" },
        title && (React.createElement(Box, { marginBottom: 1 },
            React.createElement(Text, { bold: true, color: "white" }, title),
            React.createElement(Text, { dimColor: true },
                " ",
                "(",
                stats.completed + stats.failed + stats.skipped,
                "/",
                stats.total,
                ")"))),
        showProgressBar && renderProgressBar(),
        React.createElement(Box, { flexDirection: "column" }, steps.map((step, _index) => {
            const config = STATUS_CONFIG[step.status];
            const icon = getStepIcon(step.status);
            const isRunning = step.status === "running";
            return (React.createElement(Box, { key: step.id, flexDirection: "column" },
                React.createElement(Box, null,
                    React.createElement(Text, { color: config.color },
                        icon,
                        " "),
                    React.createElement(Text, { color: config.color, dimColor: step.status === "pending" }, step.name),
                    step.duration !== undefined && step.status === "completed" && (React.createElement(Text, { dimColor: true },
                        " (",
                        formatDuration(step.duration),
                        ")")),
                    isRunning && elapsedTime > 0 && (React.createElement(Text, { dimColor: true },
                        " (",
                        formatDuration(elapsedTime),
                        ")")),
                    React.createElement(Text, { dimColor: true },
                        " ",
                        config.textLabel)),
                showStepDetails && (React.createElement(React.Fragment, null,
                    step.description && isRunning && (React.createElement(Box, { marginLeft: 2 },
                        React.createElement(Text, { dimColor: true },
                            "\u23BF ",
                            step.description))),
                    step.error && step.status === "failed" && (React.createElement(Box, { marginLeft: 2 },
                        React.createElement(Text, { color: "red" },
                            "\u23BF Error: ",
                            step.error)))))));
        })),
        showTimeEstimate && stats.estimatedRemaining > 0 && (React.createElement(Box, { marginTop: 1 },
            React.createElement(Text, { dimColor: true },
                "Estimated remaining: ",
                formatDuration(stats.estimatedRemaining)))),
        stats.progress === 100 && (React.createElement(Box, { marginTop: 1 },
            React.createElement(Text, { color: "green" }, "\u2713 "),
            React.createElement(Text, null,
                "Completed ",
                stats.completed,
                " step",
                stats.completed !== 1 ? "s" : ""),
            stats.failed > 0 && (React.createElement(Text, { color: "red" },
                " \u2022 ",
                stats.failed,
                " failed")),
            stats.skipped > 0 && (React.createElement(Text, { color: "yellow" },
                " \u2022 ",
                stats.skipped,
                " skipped")),
            React.createElement(Text, { dimColor: true },
                " \u2022 Total: ",
                formatDuration(stats.totalDuration))))));
}
/**
 * Hook to manage step progress state
 */
export function useStepProgress(initialSteps) {
    const [steps, setSteps] = useState(initialSteps.map((s) => ({ ...s, status: "pending" })));
    const [startTimes, setStartTimes] = useState(new Map());
    const startStep = (stepId) => {
        setStartTimes((prev) => new Map(prev).set(stepId, Date.now()));
        setSteps((prev) => prev.map((s) => s.id === stepId ? { ...s, status: "running" } : s));
    };
    const completeStep = (stepId) => {
        const startTime = startTimes.get(stepId);
        const duration = startTime ? Date.now() - startTime : undefined;
        setSteps((prev) => prev.map((s) => s.id === stepId
            ? { ...s, status: "completed", duration }
            : s));
    };
    const failStep = (stepId, error) => {
        const startTime = startTimes.get(stepId);
        const duration = startTime ? Date.now() - startTime : undefined;
        setSteps((prev) => prev.map((s) => s.id === stepId
            ? { ...s, status: "failed", duration, error }
            : s));
    };
    const skipStep = (stepId) => {
        setSteps((prev) => prev.map((s) => s.id === stepId ? { ...s, status: "skipped" } : s));
    };
    const resetSteps = () => {
        setSteps(initialSteps.map((s) => ({ ...s, status: "pending" })));
        setStartTimes(new Map());
    };
    return {
        steps,
        startStep,
        completeStep,
        failStep,
        skipStep,
        resetSteps,
    };
}
export default MultiStepProgress;
//# sourceMappingURL=MultiStepProgress.js.map