/**
 * TUI rendering for the task tool.
 *
 * Pure display functions - no I/O, no subprocess knowledge.
 * Takes typed data, returns pi-tui Components.
 */
import { Text } from '../../../../tui/index.js';
import { ABORT_UI_LABEL_INLINE } from '../../../abort-labels.js';
// ---------------------------------------------------------------------------
// Formatting helpers (pure functions, exported for testing)
// ---------------------------------------------------------------------------
export function formatTokens(count) {
    if (count < 1_000)
        return count.toString();
    // Upper bound is 9_950 so that toFixed(1) never rounds up to "10.0k"
    // (9_949 -> "9.9k", 9_950 would -> "10.0k" which collides with the rounded tier).
    if (count < 9_950)
        return `${(count / 1_000).toFixed(1)}k`;
    if (count < 1_000_000)
        return `${Math.round(count / 1_000)}k`;
    return `${(count / 1_000_000).toFixed(1)}M`;
}
/**
 * Format usage stats for display.
 *
 * When contextWindow is provided, shows "X% context used" instead of $cost
 * (appropriate for credit-based models like shortcut-opus where cost is meaningless).
 */
export function formatUsage(usage, model, contextWindow, toolCallCount) {
    const parts = [];
    if (toolCallCount)
        parts.push(`${toolCallCount} step${toolCallCount > 1 ? 's' : ''}`);
    if (usage.input)
        parts.push(`↑${formatTokens(usage.input)}`);
    if (usage.output)
        parts.push(`↓${formatTokens(usage.output)}`);
    // Show context % instead of cost when we have a context window
    if (contextWindow && contextWindow > 0 && usage.lastTurnInput > 0) {
        const percent = (usage.lastTurnInput / contextWindow) * 100;
        parts.push(`${percent.toFixed(1)}% context used`);
    }
    else if (!contextWindow && usage.cost) {
        // Fallback: show cost only when no context window info
        parts.push(`$${usage.cost.toFixed(4)}`);
    }
    if (model)
        parts.push(model);
    return parts.join(' ');
}
const MAX_TOOL_DESC = 50;
function truncate(s, max) {
    return s.length > max ? s.slice(0, max) + '...' : s;
}
/**
 * Format a tool call for display in the task result.
 *
 * bash and execute_code show a short description (up to 50 chars).
 * read/write/edit are omitted (returns null) - not useful in task context.
 */
export function formatToolCall(tc) {
    switch (tc.name) {
        case 'bash': {
            const desc = tc.args.description;
            if (desc)
                return `$ ${truncate(desc, MAX_TOOL_DESC)}`;
            const cmd = tc.args.command ?? '...';
            return `$ ${truncate(cmd, MAX_TOOL_DESC)}`;
        }
        case 'execute_code': {
            const desc = tc.args.description ?? '';
            return `𝕏 ${truncate(desc || '(code)', MAX_TOOL_DESC)}`;
        }
        case 'read':
        case 'write':
        case 'edit':
            return null;
        default: {
            const argsStr = JSON.stringify(tc.args);
            return `${tc.name} ${truncate(argsStr, MAX_TOOL_DESC)}`;
        }
    }
}
/** Resolve the icon, label, and color for a completed (non-running) task state. */
function taskStatus(isError, isAborted, theme) {
    if (isError)
        return { icon: theme.fg('error', '✗'), word: 'failed', color: 'error' };
    if (isAborted)
        return { icon: theme.fg('warning', '⊘'), word: ABORT_UI_LABEL_INLINE, color: 'warning' };
    return { icon: theme.fg('success', '✓'), word: 'done', color: 'success' };
}
/** Max length for the query preview in the header */
const MAX_QUERY_PREVIEW = 120;
/**
 * Render the task tool call header (shown while running / before result).
 *
 * Collapsed: name + truncated single-line query
 * Expanded: name + full query
 */
export function renderTaskCall(data, expanded, theme) {
    const title = theme.fg('toolTitle', theme.bold(data.name || 'task'));
    if (data.query) {
        if (expanded) {
            return new Text(`${title}\n${theme.fg('text', data.query)}`, 0, 0);
        }
        // Collapsed: first line, truncated
        const firstLine = data.query.split('\n')[0] ?? '';
        const preview = firstLine.length > MAX_QUERY_PREVIEW
            ? firstLine.slice(0, MAX_QUERY_PREVIEW) + '...'
            : firstLine;
        return new Text(`${title}\n${theme.fg('text', preview)}`, 0, 0);
    }
    return new Text(title, 0, 0);
}
/**
 * Get the "currently doing" description from the latest tool call.
 * Walks backwards to find the last displayable tool call.
 */
export function getCurrentActivity(toolCalls) {
    for (let i = toolCalls.length - 1; i >= 0; i--) {
        const formatted = formatToolCall(toolCalls[i]);
        if (formatted)
            return formatted;
    }
    return null;
}
/**
 * Render the task tool result.
 *
 * Collapsed: compact 1-line status - "currently doing: X" or "✓ done"
 * Expanded: full details - all tool calls, output text, usage stats
 */
export function renderTaskResult(data, expanded, theme) {
    // Guard against deserialized data missing toolCalls (e.g. from session resume)
    const toolCalls = data.toolCalls ?? [];
    const icon = data.isRunning
        ? ' '
        : data.isError
            ? theme.fg('error', '✗')
            : theme.fg('success', '✓');
    // -- Collapsed view: compact single-line status ---------------------
    if (!expanded) {
        if (data.isRunning) {
            const activity = getCurrentActivity(toolCalls);
            const activityStr = activity
                ? `${theme.fg('muted', 'currently doing:')} ${theme.fg('toolOutput', activity)}`
                : theme.fg('muted', 'starting...');
            const tcCount = toolCalls.length;
            const usageStr = formatUsage(data.usage, undefined, data.contextWindow, tcCount);
            const footer = usageStr ? `\n${theme.fg('dim', usageStr)}` : '';
            return new Text(`${activityStr}${footer}`, 0, 0);
        }
        // Completed (error, aborted, or done)
        const s = taskStatus(data.isError, data.isAborted, theme);
        const errMsg = data.isError && data.errorMessage
            ? ` ${theme.fg('error', truncate(data.errorMessage, 80))}`
            : '';
        const tcCount = toolCalls.length;
        const usageStr = formatUsage(data.usage, undefined, data.contextWindow, tcCount);
        const usageSuffix = usageStr ? `  ${theme.fg('dim', usageStr)}` : '';
        return new Text(`${icon} ${theme.fg(s.color, s.word)}${errMsg}${usageSuffix}`, 0, 0);
    }
    // -- Expanded view: full details ------------------------------------
    const s = data.isRunning ? undefined : taskStatus(data.isError, data.isAborted, theme);
    const statusWord = data.isRunning ? 'running...' : s.word;
    let text = `${icon} ${theme.fg('accent', statusWord)}`;
    // Error message
    if (data.isError && data.errorMessage) {
        text += `\n${theme.fg('error', data.errorMessage)}`;
    }
    // All tool calls
    if (toolCalls.length > 0) {
        for (const tc of toolCalls) {
            const formatted = formatToolCall(tc);
            if (formatted) {
                text += `\n${theme.fg('muted', '→ ')}${theme.fg('toolOutput', formatted)}`;
            }
        }
    }
    // Final output text
    if (data.text) {
        text += `\n\n${theme.fg('toolOutput', data.text)}`;
    }
    else if (!data.isRunning && !data.isError) {
        text += `\n${theme.fg('muted', '(no output)')}`;
    }
    // Usage
    const tcCount = toolCalls.length;
    const usageStr = formatUsage(data.usage, data.model, data.contextWindow, tcCount);
    if (usageStr) {
        text += `\n${theme.fg('dim', usageStr)}`;
    }
    return new Text(text, 0, 0);
}
// ---------------------------------------------------------------------------
// Grouped rendering (for renderGroup - multiple concurrent tasks as one tree)
// ---------------------------------------------------------------------------
// Tree drawing characters - all prefixes are 3 chars wide for alignment
const BRANCH_MID = '├─ ';
const BRANCH_END = '└─ ';
const PIPE_MID = '│  ';
const PIPE_END = '   ';
const MAX_GROUP_QUERY_PREVIEW = 100;
const MAX_EXPANDED_TOOL_CALLS = 15;
/**
 * Render a group of concurrent task calls as a single tree.
 *
 * Pure function - takes member snapshots from the framework, returns a Component.
 *
 *   ✓ General Subagent
 *   ├─ Revenue Sheet: Analyze Q3 revenue data...
 *   │     ✓ done  18 steps  claude-opus-4-8
 *   ├─ Income Statement: Build income statement...
 *   │     Step 3: >_ Running calculations  claude-opus-4-8
 *   └─ Format Summary: Apply formatting...
 *         ⏳ starting...
 */
export function renderTaskGroup(members, options, theme) {
    const { expanded } = options;
    const lines = [];
    // Only use a type-specific header when the whole batch is homogeneous.
    // Mixed parallel task batches should render generically rather than lying
    // about every child being the first child's type.
    const anyPartial = members.some((m) => m.isPartial);
    const declaredTypes = new Set(members
        .map((member) => {
        const args = (member.args ?? undefined);
        return typeof args?.subagent_type === 'string' ? args.subagent_type : undefined;
    })
        .filter((value) => Boolean(value)));
    const agentType = declaredTypes.size === 1
        ? [...declaredTypes][0]
        : declaredTypes.size === 0 && !anyPartial
            ? 'general'
            : undefined;
    const agentLabel = agentType
        ? formatAgentGroupLabel(agentType, members.length)
        : `${members.length} Agent${members.length > 1 ? 's' : ''}`;
    const showHeader = expanded || !anyPartial;
    if (showHeader) {
        // Header icon: all done or any error; no icon when still running
        const allDone = members.every((m) => !m.isPartial);
        const headerIcon = groupHeaderIcon(members, theme);
        const header = allDone
            ? `${headerIcon} ${theme.fg('toolTitle', theme.bold(agentLabel))}`
            : theme.fg('toolTitle', theme.bold(agentLabel));
        lines.push(header);
    }
    // Tree entries
    for (let i = 0; i < members.length; i++) {
        const member = members[i];
        const isLast = i === members.length - 1;
        const branch = isLast ? BRANCH_END : BRANCH_MID;
        const cont = isLast ? PIPE_END : PIPE_MID;
        const args = (member.args ?? {});
        const name = args.name || `Agent ${i + 1}`;
        const query = args.query;
        const details = member.result?.details;
        const isRunning = member.isPartial;
        const isError = member.result?.isError ?? false;
        if (expanded) {
            lines.push(...renderGroupEntryExpanded(name, query, details, isRunning, isError, branch, cont, theme));
        }
        else {
            lines.push(...renderGroupEntryCollapsed(name, query, details, isRunning, isError, branch, cont, theme));
        }
    }
    return new Text(lines.join('\n'), 0, 0);
}
function groupHeaderIcon(members, theme) {
    const allDone = members.every((m) => !m.isPartial);
    if (!allDone)
        return ' ';
    if (members.some((m) => m.result?.isError))
        return theme.fg('error', '✗');
    return theme.fg('success', '✓');
}
function renderGroupEntryCollapsed(name, _query, details, isRunning, isError, branch, cont, theme) {
    const line1 = `${theme.fg('dim', branch)}${theme.fg('accent', name)}`;
    const prefix = theme.fg('dim', cont);
    if (isRunning) {
        const toolCalls = details?.toolCalls ?? [];
        const stepCount = toolCalls.length;
        const activity = details ? getCurrentActivity(toolCalls) : null;
        let status;
        if (activity) {
            status = `${theme.fg('muted', `Step ${stepCount}:`)} ${theme.fg('toolOutput', activity)}`;
        }
        else if (stepCount > 0) {
            status = `${theme.fg('muted', `Step ${stepCount}: thinking...`)}`;
        }
        else {
            status = theme.fg('muted', 'starting...');
        }
        return [line1, `${prefix}  ${status}`];
    }
    const s = taskStatus(isError, details?.isAborted, theme);
    const steps = details?.toolCalls?.length ?? 0;
    const stepStr = steps > 0 ? `  ${theme.fg('dim', `${steps} step${steps > 1 ? 's' : ''}`)}` : '';
    return [line1, `${prefix}${s.icon} ${theme.fg(s.color, s.word)}${stepStr}`];
}
function renderGroupEntryExpanded(name, query, details, isRunning, isError, branch, cont, theme) {
    const c = theme.fg('dim', cont);
    const lines = [];
    lines.push(`${theme.fg('dim', branch)}${theme.fg('accent', name)}`);
    if (query)
        lines.push(`${c}${groupQueryPreview(query, theme)}`);
    const usageStr = details
        ? formatUsage(details.usage, details.model, details.contextWindow, details.toolCalls?.length ?? 0)
        : '';
    const usageSuffix = usageStr ? `  ${theme.fg('dim', usageStr)}` : '';
    if (isRunning) {
        lines.push(`${c}${theme.fg('accent', 'running...')}${usageSuffix}`);
    }
    else {
        const s = taskStatus(isError, details?.isAborted, theme);
        lines.push(`${c}${s.icon} ${theme.fg('accent', s.word)}${usageSuffix}`);
    }
    if (details) {
        if (isError && details.errorMessage) {
            lines.push(`${c}${theme.fg('error', details.errorMessage)}`);
        }
        const allToolCalls = details.toolCalls ?? [];
        const displayCalls = allToolCalls.slice(-MAX_EXPANDED_TOOL_CALLS);
        if (allToolCalls.length > MAX_EXPANDED_TOOL_CALLS) {
            lines.push(`${c}${theme.fg('muted', `... (${allToolCalls.length - MAX_EXPANDED_TOOL_CALLS} earlier steps)`)}`);
        }
        for (const tc of displayCalls) {
            const formatted = formatToolCall(tc);
            if (formatted) {
                lines.push(`${c}${theme.fg('muted', '→ ')}${theme.fg('toolOutput', formatted)}`);
            }
        }
        if (details.text && !isRunning) {
            const outputLines = details.text.split('\n');
            const maxLines = 5;
            for (const line of outputLines.slice(0, maxLines)) {
                lines.push(`${c}${theme.fg('toolOutput', line)}`);
            }
            if (outputLines.length > maxLines) {
                lines.push(`${c}${theme.fg('muted', `... (${outputLines.length - maxLines} more lines)`)}`);
            }
        }
    }
    return lines;
}
function groupQueryPreview(query, theme) {
    const firstLine = query.split('\n')[0] ?? '';
    const preview = firstLine.length > MAX_GROUP_QUERY_PREVIEW
        ? firstLine.slice(0, MAX_GROUP_QUERY_PREVIEW) + '...'
        : firstLine;
    return theme.fg('text', preview);
}
export function formatAgentTypeLabel(agentType) {
    return agentType.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()) + ' Subagent';
}
function formatAgentGroupLabel(agentType, count) {
    const name = agentType.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
    return `${count} ${name} Agent${count > 1 ? 's' : ''}`;
}
//# sourceMappingURL=task.js.map