import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
/**
 * DissolveTransition — Column-sweep inspired by TTE's Sweep effect.
 *
 * Uses a SequenceEaser (in_out_circ) to activate columns with eased pacing.
 * Each activated column cycles through shade characters (░▒▓█) independently.
 *
 * Out phase: columns sweep, building up shade chars until solid █ (covers old content).
 * In phase: columns sweep in reverse, dissolving █ back through shades to empty (reveals new content).
 */
import { Box, Text } from 'ink';
import { useState, useEffect, useRef } from 'react';
/** Shade characters in build-up order (light → solid). */
const SHADES = ['░', '▒', '▓', '█'];
/** How many ticks each shade character displays before advancing. */
const TICKS_PER_SHADE = 1;
/** Total ticks a column needs to complete its shade cycle. */
const SHADE_CYCLE_TICKS = SHADES.length * TICKS_PER_SHADE;
/**
 * Decide whether a transition should animate at all in the current
 * environment. Returning false makes the component an instant
 * pass-through — the user sees the new screen immediately with no
 * column-sweep overlay.
 *
 * Animation costs scale with terminal area (each tick rebuilds
 * `width × height` characters as JS strings then runs them through
 * Yoga). On slow terminals, narrow windows, or CI runs the dissolve
 * is the dominant "the wizard feels slow" sensation, so we skip it.
 *
 * Honors `AMPLITUDE_WIZARD_NO_TRANSITIONS=1` for users who want it
 * off regardless of capability heuristics.
 */
function shouldAnimateTransition(width, height) {
    if (process.env.AMPLITUDE_WIZARD_NO_TRANSITIONS === '1')
        return false;
    if (process.env.CI === '1' || process.env.CI === 'true')
        return false;
    if (!process.stdout.isTTY)
        return false;
    const term = process.env.TERM ?? '';
    if (term === '' || term === 'dumb' || term === 'xterm-mono')
        return false;
    // Tiny terminals can't render the sweep meaningfully — the column count
    // is too low for the easing to look like motion. Threshold matches the
    // narrowest commonly-supported width the rest of the TUI plans for.
    if (width < 60 || height < 10)
        return false;
    return true;
}
function easeInOutCirc(t) {
    if (t < 0.5) {
        return (1 - Math.sqrt(1 - 4 * t * t)) / 2;
    }
    return (Math.sqrt(1 - (2 * t - 2) ** 2) + 1) / 2;
}
var TransitionPhase;
(function (TransitionPhase) {
    TransitionPhase["Idle"] = "idle";
    TransitionPhase["Out"] = "out";
    TransitionPhase["In"] = "in";
})(TransitionPhase || (TransitionPhase = {}));
export const DissolveTransition = ({ transitionKey, width, height, children, direction = 'left', duration = 12, }) => {
    // Decide once per render whether to animate at all. When the env
    // doesn't support a smooth sweep, skip every state-machine path and
    // render `children` directly — there's nothing for the user to see
    // anyway, and the per-tick render cost on slow terminals is the
    // primary "feels slow" sensation. Re-evaluated on each render so a
    // resize from too-narrow to wide-enough lights the animation back up
    // without a remount.
    const animate = shouldAnimateTransition(width, height);
    const [phase, setPhase] = useState(TransitionPhase.Idle);
    const [tick, setTick] = useState(0);
    const [activeDir, setActiveDir] = useState(direction);
    const prevKey = useRef(transitionKey);
    const pendingChildren = useRef(children);
    const [displayChildren, setDisplayChildren] = useState(children);
    // Track when each column was activated (tick number), -1 means not yet.
    const columnActivationTick = useRef([]);
    useEffect(() => {
        if (!animate) {
            // Pass-through mode: any new children become the display directly,
            // and any stuck phase/tick state from a prior animated run is
            // reset so a switch back to animated mode starts clean.
            prevKey.current = transitionKey;
            pendingChildren.current = children;
            setDisplayChildren(children);
            if (phase !== TransitionPhase.Idle) {
                setPhase(TransitionPhase.Idle);
                setTick(0);
            }
            return;
        }
        if (transitionKey !== prevKey.current) {
            prevKey.current = transitionKey;
            pendingChildren.current = children;
            setActiveDir(direction);
            setPhase(TransitionPhase.Out);
            setTick(0);
            columnActivationTick.current = new Array(width).fill(-1);
        }
        else if (phase === TransitionPhase.Idle) {
            setDisplayChildren(children);
        }
    }, [transitionKey, children, width, height, phase, direction, animate]);
    useEffect(() => {
        if (phase === TransitionPhase.Idle)
            return;
        const timer = setInterval(() => {
            setTick((prev) => prev + 1);
        }, duration);
        return () => clearInterval(timer);
    }, [phase, duration]);
    // Easer steps = width / 3: ~3 columns activate per tick for a fast sweep.
    const easerSteps = Math.max(Math.ceil(width / 3), 10);
    // A phase ends when the easer has completed AND all columns have finished their shade cycle.
    const maxTicks = easerSteps + SHADE_CYCLE_TICKS;
    useEffect(() => {
        if (phase === TransitionPhase.Idle)
            return;
        if (tick >= maxTicks) {
            if (phase === TransitionPhase.Out) {
                setDisplayChildren(pendingChildren.current);
                setPhase(TransitionPhase.In);
                setTick(0);
                columnActivationTick.current = new Array(width).fill(-1);
            }
            else {
                setPhase(TransitionPhase.Idle);
            }
        }
    }, [tick, phase, maxTicks, width]);
    if (!animate || phase === TransitionPhase.Idle) {
        return _jsx(_Fragment, { children: displayChildren });
    }
    // --- SequenceEaser logic ---
    // Map current tick to easer progress (0..1), apply easing,
    // then determine how many columns should be activated.
    const easerProgress = Math.min(tick / easerSteps, 1);
    const easedValue = easeInOutCirc(easerProgress);
    const activatedCount = Math.floor(easedValue * width);
    // Build column order based on direction.
    // "left" means sweep moves left-to-right; "right" means right-to-left.
    // TTE's COLUMN_RIGHT_TO_LEFT activates rightmost first.
    const columnOrder = [];
    if (activeDir === 'left') {
        for (let c = width - 1; c >= 0; c--)
            columnOrder.push(c);
    }
    else {
        for (let c = 0; c < width; c++)
            columnOrder.push(c);
    }
    // Activate columns that should be active but aren't yet.
    for (let i = 0; i < activatedCount && i < columnOrder.length; i++) {
        const col = columnOrder[i];
        if (columnActivationTick.current[col] === -1) {
            columnActivationTick.current[col] = tick;
        }
    }
    // --- Render frame ---
    const rows = [];
    for (let r = 0; r < height; r++) {
        let row = '';
        for (let c = 0; c < width; c++) {
            const activatedAt = columnActivationTick.current[c];
            let char;
            if (activatedAt === -1) {
                // Not yet activated
                char = phase === TransitionPhase.Out ? ' ' : '█';
            }
            else {
                // Column is activated — determine shade based on ticks since activation
                const age = tick - activatedAt;
                const shadeIndex = Math.min(Math.floor(age / TICKS_PER_SHADE), SHADES.length - 1);
                if (phase === TransitionPhase.Out) {
                    // Building up: ░ → ▒ → ▓ → █
                    char = SHADES[shadeIndex];
                }
                else {
                    // Dissolving: █ → ▓ → ▒ → ░ → space
                    if (shadeIndex >= SHADES.length - 1 && age >= SHADE_CYCLE_TICKS) {
                        char = ' ';
                    }
                    else {
                        char = SHADES[SHADES.length - 1 - shadeIndex];
                    }
                }
            }
            row += char;
        }
        rows.push(row);
    }
    return (_jsx(Box, { flexDirection: "column", flexGrow: 1, children: rows.map((row, i) => (_jsx(Text, { dimColor: true, children: row }, i))) }));
};
