import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/**
 * FileWritesPanel — live per-file activity from the inner agent.
 *
 * Renders one row per file the inner agent's Edit / Write / MultiEdit /
 * NotebookEdit tools have touched during the run. Rows are populated by
 * `WizardStore.recordFileChangePlanned` / `recordFileChangeApplied`,
 * which `inner-lifecycle.ts` calls from PreToolUse / PostToolUse via the
 * abstract `WizardUI` interface.
 *
 * UX:
 *   ⠸  CREATE   src/amplitude.ts            generating...
 *   ✓  CREATE   src/events.ts               42 lines    1.2s
 *   ✓  MODIFY   app/layout.tsx              edited      0.3s
 *   ✗  MODIFY   .env.local                  failed
 *
 * Color tokens (see styles.ts):
 *   - CREATE → success (emerald)
 *   - MODIFY → warning (amber)
 *   - DELETE → error (red)
 *   - in-progress rows lean on Colors.active (violet) for the spinner
 *
 * Hidden when there are no rows so RunScreen stays compact during the
 * planning phase before any file write has fired.
 */
import { Box, Text } from 'ink';
import { useMemo } from 'react';
import path from 'path';
import { Colors, Icons } from '../styles.js';
import { BrailleSpinner } from './BrailleSpinner.js';
const OP_LABELS = {
    create: 'CREATE',
    modify: 'MODIFY',
    delete: 'DELETE',
};
const OP_COLORS = {
    create: Colors.success,
    modify: Colors.warning,
    delete: Colors.error,
};
/** Right-pad to a fixed width with ASCII spaces. */
const padRight = (s, width) => s.length >= width ? s : s + ' '.repeat(width - s.length);
const formatDuration = (ms) => {
    if (ms < 1000)
        return `${ms}ms`;
    return `${(ms / 1000).toFixed(1)}s`;
};
/**
 * Relativize an absolute path against the install dir for display. Falls
 * back to the basename if the path lives outside the project (rare, but
 * possible — skill installs sometimes touch tmp dirs).
 */
const displayPath = (raw, installDir) => {
    if (installDir &&
        raw.startsWith(installDir) &&
        (raw.length === installDir.length || raw[installDir.length] === '/')) {
        const rel = path.relative(installDir, raw);
        return rel === '' ? path.basename(raw) : rel;
    }
    return raw.startsWith('/') ? path.basename(raw) : raw;
};
export const FileWritesPanel = ({ entries, installDir, spinnerFrame, now = Date.now(), maxVisible = 8, }) => {
    // Slice to the most recent rows. The store also caps at MAX_FILE_WRITES,
    // but RunScreen has limited vertical real estate — long-running runs
    // would otherwise push the rest of the dashboard off-screen.
    const visible = useMemo(() => entries.length > maxVisible
        ? entries.slice(entries.length - maxVisible)
        : entries, [entries, maxVisible]);
    if (visible.length === 0)
        return null;
    const completedCount = entries.filter((e) => e.status === 'applied').length;
    const totalCount = entries.length;
    const inProgressCount = entries.filter((e) => e.status === 'planned').length;
    return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: Colors.heading, bold: true, children: "Files" }), _jsxs(Text, { color: Colors.subtle, children: [" ", Icons.dot, " "] }), _jsx(Text, { color: Colors.muted, children: inProgressCount > 0
                            ? `${completedCount}/${totalCount} written`
                            : `${totalCount} written` })] }), visible.map((entry) => (_jsx(FileWriteRow, { entry: entry, installDir: installDir, spinnerFrame: spinnerFrame, now: now }, `${entry.startedAt}-${entry.path}`)))] }));
};
const FileWriteRow = ({ entry, installDir, spinnerFrame, now, }) => {
    const { operation, status } = entry;
    const opLabel = padRight(OP_LABELS[operation], 6);
    const opColor = OP_COLORS[operation];
    const display = displayPath(entry.path, installDir);
    let icon;
    if (status === 'applied') {
        icon = _jsx(Text, { color: Colors.success, children: Icons.checkmark });
    }
    else if (status === 'failed') {
        icon = _jsx(Text, { color: Colors.error, children: Icons.cross });
    }
    else {
        icon = _jsx(BrailleSpinner, { color: Colors.active, frame: spinnerFrame });
    }
    // Trailing detail column. While the row is still planned we show
    // "generating…" with elapsed seconds so a stuck write is visible
    // (instead of a dead spinner). On apply we show bytes/lines + total
    // duration so the user can see throughput.
    let detail;
    if (status === 'applied') {
        const dur = entry.completedAt !== undefined
            ? formatDuration(entry.completedAt - entry.startedAt)
            : '';
        const sizeHint = typeof entry.bytes === 'number'
            ? `${entry.bytes.toLocaleString()} bytes`
            : 'edited';
        detail = dur ? `${sizeHint}  ${dur}` : sizeHint;
    }
    else if (status === 'failed') {
        detail = 'failed';
    }
    else {
        const elapsed = now - entry.startedAt;
        detail = elapsed >= 1000 ? `generating… ${formatDuration(elapsed)}` : 'generating…';
    }
    return (_jsxs(Box, { children: [_jsx(Text, { children: " " }), icon, _jsx(Text, { children: " " }), _jsx(Text, { color: opColor, bold: true, children: opLabel }), _jsx(Text, { children: " " }), _jsx(Text, { color: Colors.body, wrap: "truncate-end", children: display }), _jsxs(Text, { color: Colors.subtle, children: [" ", Icons.dot, " "] }), _jsx(Text, { color: Colors.muted, children: detail })] }));
};
