import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { resolve } from 'node:path';
import { highlight } from 'cli-highlight';
import { Box, Text } from 'ink';
import ToolMessage from '../../components/tool-message.js';
import { getColors } from '../../config/index.js';
import { DEFAULT_TERMINAL_COLUMNS } from '../../constants.js';
import { truncateAnsi } from '../../utils/ansi-truncate.js';
import { formatError } from '../../utils/error-formatter.js';
import { getCachedFileContent } from '../../utils/file-cache.js';
import { normalizeIndentation } from '../../utils/indentation-normalizer.js';
import { areLinesSimlar, computeInlineDiff } from '../../utils/inline-diff.js';
import { getLanguageFromExtension } from '../../utils/programming-language-helper.js';
/** Truncate a plain line to fit terminal width */
const truncateLine = (line, maxWidth) => {
    if (line.length <= maxWidth)
        return line;
    return line.slice(0, maxWidth - 1) + '…';
};
export async function formatStringReplacePreview(args, result, colors) {
    const themeColors = colors || getColors();
    const { path, old_str, new_str } = args;
    const terminalWidth = process.stdout.columns || DEFAULT_TERMINAL_COLUMNS;
    const lineNumPrefixWidth = 8;
    const availableWidth = Math.max(terminalWidth - lineNumPrefixWidth - 2, 20);
    const isResult = result !== undefined;
    try {
        const absPath = resolve(path);
        const cached = await getCachedFileContent(absPath);
        const fileContent = cached.content;
        const ext = path.split('.').pop()?.toLowerCase() ?? '';
        const language = getLanguageFromExtension(ext);
        // Preview mode - validate old_str exists and is unique
        if (!isResult) {
            const occurrences = fileContent.split(old_str).length - 1;
            if (occurrences === 0) {
                return (_jsx(ToolMessage, { message: _jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { color: themeColors.tool, children: "\u2692 string_replace" }), _jsxs(Box, { children: [_jsx(Text, { color: themeColors.secondary, children: "Path: " }), _jsx(Text, { wrap: "truncate-end", color: themeColors.primary, children: path })] }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(Text, { color: themeColors.error, children: "\u2717 Error: Content not found in file. The file may have changed since you last read it." }) })] }), hideBox: true }));
            }
            if (occurrences > 1) {
                return (_jsx(ToolMessage, { message: _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: themeColors.tool, children: "\u2692 string_replace" }), _jsxs(Box, { children: [_jsx(Text, { color: themeColors.secondary, children: "Path: " }), _jsx(Text, { wrap: "truncate-end", color: themeColors.primary, children: path })] }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { color: themeColors.error, children: ["\u2717 Error: Found ", occurrences, " matches"] }), _jsx(Text, { color: themeColors.secondary, children: "Add more surrounding context to make the match unique." })] })] }), hideBox: true }));
            }
        }
        // Find location of the match in the file
        const searchStr = isResult ? new_str : old_str;
        const matchIndex = fileContent.indexOf(searchStr);
        const beforeContent = fileContent.substring(0, matchIndex);
        const beforeLines = beforeContent.split('\n');
        const startLine = beforeLines.length;
        const oldStrLines = old_str.split('\n');
        const newStrLines = new_str.split('\n');
        const contentLines = isResult ? newStrLines : oldStrLines;
        const endLine = startLine + contentLines.length - 1;
        const allLines = fileContent.split('\n');
        const contextLines = 3;
        const showStart = Math.max(0, startLine - 1 - contextLines);
        const showEnd = Math.min(allLines.length - 1, endLine - 1 + contextLines);
        // Collect all lines for normalization
        const linesToNormalize = [];
        for (let i = showStart; i < startLine - 1; i++) {
            linesToNormalize.push(allLines[i] || '');
        }
        for (let i = 0; i < oldStrLines.length; i++) {
            linesToNormalize.push(oldStrLines[i] || '');
        }
        if (isResult) {
            for (let i = 0; i < newStrLines.length; i++) {
                linesToNormalize.push(allLines[startLine - 1 + i] || '');
            }
        }
        else {
            for (let i = 0; i < newStrLines.length; i++) {
                linesToNormalize.push(newStrLines[i] || '');
            }
        }
        const contextAfterStart = isResult
            ? startLine - 1 + newStrLines.length
            : endLine;
        for (let i = contextAfterStart; i <= showEnd; i++) {
            linesToNormalize.push(allLines[i] || '');
        }
        const normalizedLines = normalizeIndentation(linesToNormalize);
        // Split normalized lines back into sections
        let lineIndex = 0;
        const contextBeforeCount = startLine - 1 - showStart;
        const normalizedContextBefore = normalizedLines.slice(lineIndex, lineIndex + contextBeforeCount);
        lineIndex += contextBeforeCount;
        const normalizedOldLines = normalizedLines.slice(lineIndex, lineIndex + oldStrLines.length);
        lineIndex += oldStrLines.length;
        const normalizedNewLines = normalizedLines.slice(lineIndex, lineIndex + newStrLines.length);
        lineIndex += newStrLines.length;
        const normalizedContextAfter = normalizedLines.slice(lineIndex);
        // Render context before
        const contextBefore = normalizedContextBefore.map((line, i) => {
            const actualLineNum = showStart + i;
            const lineNumStr = String(actualLineNum + 1).padStart(4, ' ');
            let displayLine;
            try {
                displayLine = truncateAnsi(highlight(line, { language, theme: 'default' }), availableWidth);
            }
            catch {
                displayLine = truncateLine(line, availableWidth);
            }
            return (_jsxs(Box, { children: [_jsxs(Text, { color: themeColors.secondary, children: [lineNumStr, " "] }), _jsx(Text, { wrap: "truncate-end", children: displayLine })] }, `before-${i}`));
        });
        // Build unified diff
        const diffLines = [];
        let oldIdx = 0;
        let newIdx = 0;
        let diffKey = 0;
        while (oldIdx < normalizedOldLines.length ||
            newIdx < normalizedNewLines.length) {
            const oldLine = oldIdx < normalizedOldLines.length ? normalizedOldLines[oldIdx] : null;
            const newLine = newIdx < normalizedNewLines.length ? normalizedNewLines[newIdx] : null;
            if (oldLine !== null && newLine !== null && oldLine === newLine) {
                const lineNumStr = String(startLine + oldIdx).padStart(4, ' ');
                diffLines.push(_jsxs(Box, { children: [_jsxs(Text, { color: themeColors.secondary, children: [lineNumStr, " "] }), _jsx(Text, { wrap: "truncate-end", children: truncateLine(oldLine, availableWidth) })] }, `diff-${diffKey++}`));
                oldIdx++;
                newIdx++;
            }
            else if (oldLine !== null &&
                newLine !== null &&
                areLinesSimlar(oldLine, newLine)) {
                const truncatedOldLine = truncateLine(oldLine, availableWidth);
                const truncatedNewLine = truncateLine(newLine, availableWidth);
                const segments = computeInlineDiff(truncatedOldLine, truncatedNewLine);
                const lineNumStr = String(startLine + oldIdx).padStart(4, ' ');
                const oldParts = segments
                    .filter(seg => seg.type === 'unchanged' || seg.type === 'removed')
                    .map((seg, s) => (_jsx(Text, { bold: seg.type === 'removed', underline: seg.type === 'removed', children: seg.text }, `old-seg-${s}`)));
                diffLines.push(_jsxs(Box, { children: [_jsxs(Text, { backgroundColor: themeColors.diffRemoved, color: themeColors.diffRemovedText, children: [lineNumStr, " -"] }), _jsx(Text, { wrap: "truncate-end", backgroundColor: themeColors.diffRemoved, color: themeColors.diffRemovedText, children: oldParts })] }, `diff-${diffKey++}`));
                const newParts = segments
                    .filter(seg => seg.type === 'unchanged' || seg.type === 'added')
                    .map((seg, s) => (_jsx(Text, { bold: seg.type === 'added', underline: seg.type === 'added', children: seg.text }, `new-seg-${s}`)));
                diffLines.push(_jsxs(Box, { children: [_jsxs(Text, { backgroundColor: themeColors.diffAdded, color: themeColors.diffAddedText, children: [lineNumStr, " +"] }), _jsx(Text, { wrap: "truncate-end", backgroundColor: themeColors.diffAdded, color: themeColors.diffAddedText, children: newParts })] }, `diff-${diffKey++}`));
                oldIdx++;
                newIdx++;
            }
            else if (oldLine !== null) {
                const lineNumStr = String(startLine + oldIdx).padStart(4, ' ');
                diffLines.push(_jsxs(Box, { children: [_jsxs(Text, { backgroundColor: themeColors.diffRemoved, color: themeColors.diffRemovedText, children: [lineNumStr, " -"] }), _jsx(Text, { wrap: "truncate-end", backgroundColor: themeColors.diffRemoved, color: themeColors.diffRemovedText, children: truncateLine(oldLine, availableWidth) })] }, `diff-${diffKey++}`));
                oldIdx++;
            }
            else if (newLine !== null) {
                const lineNumStr = String(startLine + newIdx).padStart(4, ' ');
                diffLines.push(_jsxs(Box, { children: [_jsxs(Text, { backgroundColor: themeColors.diffAdded, color: themeColors.diffAddedText, children: [lineNumStr, " +"] }), _jsx(Text, { wrap: "truncate-end", backgroundColor: themeColors.diffAdded, color: themeColors.diffAddedText, children: truncateLine(newLine, availableWidth) })] }, `diff-${diffKey++}`));
                newIdx++;
            }
        }
        // Render context after
        const lineDelta = newStrLines.length - oldStrLines.length;
        const contextAfter = normalizedContextAfter.map((line, i) => {
            const actualLineNum = endLine + i;
            const lineNumStr = String(actualLineNum + lineDelta + 1).padStart(4, ' ');
            let displayLine;
            try {
                displayLine = truncateAnsi(highlight(line, { language, theme: 'default' }), availableWidth);
            }
            catch {
                displayLine = truncateLine(line, availableWidth);
            }
            return (_jsxs(Box, { children: [_jsxs(Text, { color: themeColors.secondary, children: [lineNumStr, " "] }), _jsx(Text, { wrap: "truncate-end", children: displayLine })] }, `after-${i}`));
        });
        const rangeDesc = startLine === endLine
            ? `line ${startLine}`
            : `lines ${startLine}-${endLine}`;
        return (_jsx(ToolMessage, { message: _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: themeColors.tool, children: "\u2692 string_replace" }), _jsxs(Box, { children: [_jsx(Text, { color: themeColors.secondary, children: "Path: " }), _jsx(Text, { wrap: "truncate-end", color: themeColors.primary, children: path })] }), _jsxs(Box, { children: [_jsx(Text, { color: themeColors.secondary, children: "Location: " }), _jsx(Text, { color: themeColors.text, children: rangeDesc })] }), _jsxs(Box, { flexDirection: "column", marginTop: 1, marginBottom: 1, children: [_jsxs(Text, { color: themeColors.success, children: [isResult ? '✓ Replace completed' : '✓ Replacing', ' ', oldStrLines.length, " line", oldStrLines.length > 1 ? 's' : '', ' ', "with ", newStrLines.length, " line", newStrLines.length > 1 ? 's' : ''] }), _jsxs(Box, { flexDirection: "column", children: [contextBefore, diffLines, contextAfter] })] })] }), hideBox: true }));
    }
    catch (error) {
        return (_jsx(ToolMessage, { message: _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: themeColors.tool, children: "\u2692 string_replace" }), _jsxs(Box, { children: [_jsx(Text, { color: themeColors.secondary, children: "Path: " }), _jsx(Text, { wrap: "truncate-end", color: themeColors.primary, children: path })] }), _jsxs(Box, { children: [_jsx(Text, { color: themeColors.error, children: "Error: " }), _jsx(Text, { color: themeColors.error, children: formatError(error) })] })] }), hideBox: true }));
    }
}
//# sourceMappingURL=string-replace-preview.js.map