import { matchesKey } from '@mariozechner/pi-tui';
import { truncateToWidth } from "./renderers.js";
const ANSI_RE = /\x1b\[[0-9;]*m/g;
function visibleWidth(text) {
    return text.replace(ANSI_RE, '').length;
}
function padRight(text, width) {
    if (width <= 0)
        return '';
    const truncated = truncateToWidth(text, width);
    const remaining = width - visibleWidth(truncated);
    return remaining > 0 ? truncated + ' '.repeat(remaining) : truncated;
}
function hLine(width) {
    return width > 0 ? '─'.repeat(width) : '';
}
function buildTopBorder(width, title) {
    if (width <= 0)
        return '';
    if (width === 1)
        return '╭';
    if (width === 2)
        return '╭╮';
    const inner = width - 2;
    const titleText = ` ${title} `;
    if (titleText.length >= inner) {
        return `╭${hLine(inner)}╮`;
    }
    const left = Math.floor((inner - titleText.length) / 2);
    const right = inner - titleText.length - left;
    return `╭${hLine(left)}${titleText}${hLine(right)}╮`;
}
function buildBottomBorder(width) {
    if (width <= 0)
        return '';
    if (width === 1)
        return '╰';
    if (width === 2)
        return '╰╯';
    return `╰${hLine(width - 2)}╯`;
}
function parsePrintableChar(data) {
    if (!data || data.length !== 1)
        return null;
    const code = data.charCodeAt(0);
    if (code < 32 || code === 127)
        return null;
    return data;
}
export class ChainEditorOverlay {
    focused = true;
    theme;
    done;
    agentByName;
    availableAgents;
    mode = 'sequential';
    screen = 'compose';
    steps;
    selectedStepIndex = 0;
    statusMessage = '';
    maxVisibleItems = 6;
    pickerIndex = 0;
    pickerFilter = '';
    editBuffer = '';
    editCursor = 0;
    previousTask = '';
    disposed = false;
    constructor(theme, agents, done, initialAgents = []) {
        this.theme = theme;
        this.done = done;
        this.availableAgents = [...agents];
        this.agentByName = new Map(agents.map((agent) => [agent.name, agent]));
        this.steps = this.buildInitialSteps(initialAgents);
    }
    handleInput(data) {
        if (this.disposed)
            return;
        if (this.screen === 'picker') {
            this.handlePickerInput(data);
            return;
        }
        if (this.screen === 'edit') {
            this.handleEditInput(data);
            return;
        }
        this.handleComposeInput(data);
    }
    render(width) {
        const safeWidth = Math.max(4, width);
        const termHeight = process.stdout.rows || 40;
        // Match overlay maxHeight of 95%, leave margin for overlay chrome
        const maxLines = Math.max(10, Math.floor(termHeight * 0.95) - 2);
        const lines = this.screen === 'picker'
            ? this.renderPickerScreen(safeWidth, maxLines)
            : this.renderComposeScreen(safeWidth, maxLines);
        return lines.map((line) => truncateToWidth(line, safeWidth));
    }
    invalidate() {
        // Stateless rendering; no cache invalidation required.
    }
    dispose() {
        this.disposed = true;
    }
    buildInitialSteps(initialAgents) {
        const names = initialAgents
            .map((name) => name.trim())
            .filter((name) => name.length > 0)
            .filter((name) => this.agentByName.has(name));
        return names.map((agent, index) => ({
            agent,
            task: index === 0 ? '' : '(from previous step)',
        }));
    }
    handleComposeInput(data) {
        if (matchesKey(data, 'escape')) {
            this.close(undefined);
            return;
        }
        if (matchesKey(data, 'up')) {
            if (this.steps.length > 0) {
                this.selectedStepIndex =
                    (this.selectedStepIndex - 1 + this.steps.length) % this.steps.length;
                this.statusMessage = '';
            }
            this.invalidate();
            return;
        }
        if (matchesKey(data, 'down')) {
            if (this.steps.length > 0) {
                this.selectedStepIndex = (this.selectedStepIndex + 1) % this.steps.length;
                this.statusMessage = '';
            }
            this.invalidate();
            return;
        }
        if (matchesKey(data, 'a') || data.toLowerCase() === 'a') {
            this.screen = 'picker';
            this.pickerFilter = '';
            this.pickerIndex = 0;
            this.statusMessage = '';
            this.invalidate();
            return;
        }
        if (matchesKey(data, 'd') || matchesKey(data, 'delete') || data.toLowerCase() === 'd') {
            if (this.steps.length > 0) {
                this.steps.splice(this.selectedStepIndex, 1);
                if (this.selectedStepIndex >= this.steps.length) {
                    this.selectedStepIndex = Math.max(0, this.steps.length - 1);
                }
                this.statusMessage = '';
                this.invalidate();
            }
            return;
        }
        if (matchesKey(data, 'e') || data.toLowerCase() === 'e') {
            const selected = this.steps[this.selectedStepIndex];
            if (!selected)
                return;
            this.previousTask = selected.task;
            this.editBuffer = selected.task;
            this.editCursor = this.editBuffer.length;
            this.screen = 'edit';
            this.statusMessage = '';
            this.invalidate();
            return;
        }
        if (matchesKey(data, 'p') || data.toLowerCase() === 'p') {
            this.mode = this.mode === 'sequential' ? 'parallel' : 'sequential';
            this.statusMessage = '';
            this.invalidate();
            return;
        }
        if (matchesKey(data, 'enter')) {
            if (this.steps.length < 2) {
                this.statusMessage = 'Need at least 2 steps to run.';
                this.invalidate();
                return;
            }
            this.close({
                mode: this.mode,
                steps: this.steps.map((step) => ({
                    agent: step.agent,
                    task: step.task,
                })),
            });
        }
    }
    handlePickerInput(data) {
        if (matchesKey(data, 'escape')) {
            this.screen = 'compose';
            this.invalidate();
            return;
        }
        const filtered = this.getFilteredAgents();
        if (matchesKey(data, 'up')) {
            if (filtered.length > 0) {
                this.pickerIndex = (this.pickerIndex - 1 + filtered.length) % filtered.length;
            }
            this.invalidate();
            return;
        }
        if (matchesKey(data, 'down')) {
            if (filtered.length > 0) {
                this.pickerIndex = (this.pickerIndex + 1) % filtered.length;
            }
            this.invalidate();
            return;
        }
        if (matchesKey(data, 'backspace')) {
            if (this.pickerFilter.length > 0) {
                this.pickerFilter = this.pickerFilter.slice(0, -1);
                this.pickerIndex = 0;
                this.invalidate();
            }
            return;
        }
        if (matchesKey(data, 'enter')) {
            const selected = filtered[this.pickerIndex];
            if (!selected)
                return;
            this.steps.push({
                agent: selected.name,
                task: this.steps.length === 0 ? '' : '(from previous step)',
            });
            this.selectedStepIndex = this.steps.length - 1;
            this.screen = 'compose';
            this.statusMessage = '';
            this.invalidate();
            return;
        }
        const char = parsePrintableChar(data);
        if (char) {
            this.pickerFilter += char;
            this.pickerIndex = 0;
            this.invalidate();
        }
    }
    handleEditInput(data) {
        const selected = this.steps[this.selectedStepIndex];
        if (!selected) {
            this.screen = 'compose';
            return;
        }
        if (matchesKey(data, 'escape')) {
            selected.task = this.previousTask;
            this.screen = 'compose';
            this.invalidate();
            return;
        }
        if (matchesKey(data, 'enter')) {
            selected.task = this.editBuffer;
            this.screen = 'compose';
            this.invalidate();
            return;
        }
        if (matchesKey(data, 'left')) {
            this.editCursor = Math.max(0, this.editCursor - 1);
            this.invalidate();
            return;
        }
        if (matchesKey(data, 'right')) {
            this.editCursor = Math.min(this.editBuffer.length, this.editCursor + 1);
            this.invalidate();
            return;
        }
        if (matchesKey(data, 'home')) {
            this.editCursor = 0;
            this.invalidate();
            return;
        }
        if (matchesKey(data, 'end')) {
            this.editCursor = this.editBuffer.length;
            this.invalidate();
            return;
        }
        if (matchesKey(data, 'backspace')) {
            if (this.editCursor > 0) {
                this.editBuffer =
                    this.editBuffer.slice(0, this.editCursor - 1) +
                        this.editBuffer.slice(this.editCursor);
                this.editCursor -= 1;
                selected.task = this.editBuffer;
                this.invalidate();
            }
            return;
        }
        if (matchesKey(data, 'delete')) {
            if (this.editCursor < this.editBuffer.length) {
                this.editBuffer =
                    this.editBuffer.slice(0, this.editCursor) +
                        this.editBuffer.slice(this.editCursor + 1);
                selected.task = this.editBuffer;
                this.invalidate();
            }
            return;
        }
        const char = parsePrintableChar(data);
        if (char) {
            this.editBuffer =
                this.editBuffer.slice(0, this.editCursor) +
                    char +
                    this.editBuffer.slice(this.editCursor);
            this.editCursor += char.length;
            selected.task = this.editBuffer;
            this.invalidate();
        }
    }
    getFilteredAgents() {
        const query = this.pickerFilter.trim().toLowerCase();
        if (!query)
            return this.availableAgents;
        return this.availableAgents.filter((agent) => {
            const haystack = `${agent.name} ${agent.description}`.toLowerCase();
            return haystack.includes(query);
        });
    }
    renderComposeScreen(width, maxLines) {
        const inner = Math.max(0, width - 2);
        // Fixed header (always rendered)
        const chainSummary = this.steps.length > 0 ? this.steps.map((step) => step.agent).join(' → ') : '(empty)';
        const header = [
            buildTopBorder(width, 'Chain Editor'),
            this.contentLine('', inner),
            this.contentLine(this.theme.fg('text', `  Chain: ${chainSummary}`), inner),
            this.contentLine(this.theme.fg('muted', `  Mode: ${this.mode}`), inner),
            this.contentLine('', inner),
        ];
        // Fixed footer (always rendered)
        const hintRun = this.steps.length >= 2 ? '[Enter] Run' : '[Enter] Run (needs 2+ steps)';
        const footer = [
            this.contentLine(this.theme.fg('dim', `  [↑↓] Navigate  [e] Edit task  [d] Remove`), inner),
            this.contentLine(this.theme.fg('dim', `  [a] Add step  [p] Toggle mode  ${hintRun}  [Esc] Cancel`), inner),
            buildBottomBorder(width),
        ];
        // Available lines for scrollable content area
        const contentBudget = Math.max(4, maxLines - header.length - footer.length);
        if (this.steps.length === 0) {
            const content = [
                this.contentLine(this.theme.fg('muted', '  No steps yet. Press [a] to add an agent step.'), inner),
                this.contentLine('', inner),
            ];
            return [...header, ...content, ...footer];
        }
        // Each step takes 3 lines (name+model, task, empty) or 4 lines (with edit hint)
        const LINES_PER_STEP = 3;
        // Reserve 2 lines for possible scroll indicators + status message
        const scrollReserve = this.statusMessage ? 3 : 2;
        const maxSteps = Math.max(1, Math.floor((contentBudget - scrollReserve) / LINES_PER_STEP));
        const windowSize = Math.min(maxSteps, this.steps.length);
        const [startIdx, endIdx] = this.getStepVisibleRange(windowSize);
        const content = [];
        if (startIdx > 0) {
            content.push(this.contentLine(this.theme.fg('dim', `  ↑ ${startIdx} more above`), inner));
        }
        for (let i = startIdx; i < endIdx; i++) {
            const step = this.steps[i];
            const selected = i === this.selectedStepIndex;
            const marker = selected ? this.theme.fg('accent', '►') : ' ';
            const agent = this.agentByName.get(step.agent);
            const model = agent?.model ? this.theme.fg('dim', ` [${agent.model}]`) : '';
            content.push(this.contentLine(`${marker} ${this.theme.bold(`Step ${i + 1}: ${step.agent}`)}${model}`, inner));
            if (this.screen === 'edit' && selected) {
                const displayTask = this.editBuffer.slice(0, this.editCursor) +
                    this.theme.fg('accent', '│') +
                    this.editBuffer.slice(this.editCursor);
                content.push(this.contentLine(this.theme.fg('text', `  task: ${displayTask}`), inner));
                content.push(this.contentLine(this.theme.fg('dim', '  editing: [Enter] Save  [Esc] Cancel  [←→] Move cursor'), inner));
            }
            else {
                const task = step.task || this.theme.fg('muted', '(empty)');
                content.push(this.contentLine(this.theme.fg('text', `  task: ${task}`), inner));
            }
            content.push(this.contentLine('', inner));
        }
        if (endIdx < this.steps.length) {
            content.push(this.contentLine(this.theme.fg('dim', `  ↓ ${this.steps.length - endIdx} more below`), inner));
        }
        if (this.statusMessage) {
            content.push(this.contentLine(this.theme.fg('warning', `  ${this.statusMessage}`), inner));
        }
        return [...header, ...content, ...footer];
    }
    renderPickerScreen(width, maxLines) {
        const inner = Math.max(0, width - 2);
        const filtered = this.getFilteredAgents();
        if (this.pickerIndex >= filtered.length) {
            this.pickerIndex = Math.max(0, filtered.length - 1);
        }
        // Fixed header (always rendered)
        const header = [
            buildTopBorder(width, 'Add Agent Step'),
            this.contentLine('', inner),
            this.contentLine(this.theme.fg('text', `  Filter: ${this.pickerFilter || '(type to filter)'}`), inner),
            this.contentLine('', inner),
        ];
        // Fixed footer (always rendered)
        const footer = [
            this.contentLine(this.theme.fg('dim', '  [↑↓] Navigate  [Enter] Select  [Esc] Back  [Backspace] Filter'), inner),
            buildBottomBorder(width),
        ];
        // Available lines for scrollable content area
        const contentBudget = Math.max(4, maxLines - header.length - footer.length);
        if (filtered.length === 0) {
            const content = [
                this.contentLine(this.theme.fg('muted', '  No agents match filter.'), inner),
                this.contentLine('', inner),
            ];
            return [...header, ...content, ...footer];
        }
        // Each agent takes 3 lines: name, description, empty line
        const LINES_PER_AGENT = 3;
        // Reserve 2 lines for possible scroll indicators
        const scrollReserve = 2;
        const maxAgents = Math.max(1, Math.floor((contentBudget - scrollReserve) / LINES_PER_AGENT));
        const windowSize = Math.min(maxAgents, filtered.length);
        const [startIdx, endIdx] = this.getPickerVisibleRange(filtered.length, windowSize);
        const content = [];
        if (startIdx > 0) {
            content.push(this.contentLine(this.theme.fg('dim', `  ↑ ${startIdx} more above`), inner));
        }
        for (let i = startIdx; i < endIdx; i++) {
            const agent = filtered[i];
            const selected = i === this.pickerIndex;
            const marker = selected ? this.theme.fg('accent', '► ') : '  ';
            const model = agent.model ? this.theme.fg('dim', ` [${agent.model}]`) : '';
            content.push(this.contentLine(`${marker}${this.theme.bold(agent.name)}${model}`, inner));
            content.push(this.contentLine(this.theme.fg('muted', `   ${agent.description || ''}`), inner));
            content.push(this.contentLine('', inner));
        }
        if (endIdx < filtered.length) {
            content.push(this.contentLine(this.theme.fg('dim', `  ↓ ${filtered.length - endIdx} more below`), inner));
        }
        return [...header, ...content, ...footer];
    }
    contentLine(content, innerWidth) {
        return `│${padRight(content, innerWidth)}│`;
    }
    getStepVisibleRange(windowSize) {
        const count = this.steps.length;
        const ws = windowSize ?? this.maxVisibleItems;
        if (count <= ws)
            return [0, count];
        const half = Math.floor(ws / 2);
        let start = Math.max(0, this.selectedStepIndex - half);
        let end = start + ws;
        if (end > count) {
            end = count;
            start = Math.max(0, end - ws);
        }
        return [start, end];
    }
    getPickerVisibleRange(count, windowSize) {
        const ws = windowSize ?? this.maxVisibleItems;
        if (count <= ws)
            return [0, count];
        const half = Math.floor(ws / 2);
        let start = Math.max(0, this.pickerIndex - half);
        let end = start + ws;
        if (end > count) {
            end = count;
            start = Math.max(0, end - ws);
        }
        return [start, end];
    }
    close(result) {
        if (this.disposed)
            return;
        this.disposed = true;
        this.done(result);
    }
}
//# sourceMappingURL=chain-preview.js.map