const DEFAULT_LIST_COUNT = 10;
const DEFAULT_MAX_DIFF_CHARS = 6000;
export function parseChangeSetSelector(input) {
    const trimmed = input.trim();
    if (!trimmed)
        return { type: "list", count: DEFAULT_LIST_COUNT };
    const lastMatch = trimmed.match(/^last(?:\s+(\d+))?$/iu);
    if (lastMatch) {
        const count = lastMatch[1] ? Number(lastMatch[1]) : 1;
        if (!Number.isInteger(count) || count <= 0)
            throw new Error("Expected /changes last N with N greater than zero.");
        return { type: "last", count };
    }
    const rangeMatch = trimmed.match(/^(\d+)\.\.(\d+)$/u);
    if (rangeMatch) {
        const from = Number(rangeMatch[1]);
        const to = Number(rangeMatch[2]);
        if (from > to)
            throw new Error("Change-set ranges must be ascending, for example /changes 3..7.");
        return { type: "range", from, to };
    }
    const id = Number(trimmed);
    if (Number.isInteger(id) && id > 0)
        return { type: "id", id };
    throw new Error("Usage: /changes, /changes <id>, /changes A..B, or /changes last N.");
}
export function resolveChangeSetSelection(allChangeSets, selector) {
    const activeChangeSets = [...allChangeSets].filter((changeSet) => changeSet.status !== "reverted");
    const sorted = activeChangeSets.sort((a, b) => a.id - b.id);
    let changeSets;
    if (selector.type === "list") {
        changeSets = sorted.slice(-selector.count).reverse();
    }
    else if (selector.type === "last") {
        changeSets = sorted.slice(-selector.count).reverse();
    }
    else if (selector.type === "id") {
        changeSets = sorted.filter((changeSet) => changeSet.id === selector.id);
    }
    else {
        changeSets = sorted.filter((changeSet) => changeSet.id >= selector.from && changeSet.id <= selector.to);
    }
    if (!changeSets.length) {
        throw new Error("No matching change sets found.");
    }
    return { selector, changeSets };
}
export async function renderChangeSetSelection(selection, snapshotService, options = {}) {
    if (selection.selector.type === "list")
        return renderChangeSetList(selection.changeSets);
    const sections = [];
    for (const changeSet of selection.changeSets) {
        sections.push(await renderChangeSetDetail(changeSet, snapshotService, options));
    }
    return sections.join("\n\n");
}
function renderChangeSetList(changeSets) {
    const lines = ["Change sets", ""];
    for (const changeSet of changeSets) {
        lines.push(`#${changeSet.id} ${formatSummary(changeSet)} ${formatTimestamp(changeSet.createdAt)} ${formatFilePreview(changeSet.files)}`);
    }
    lines.push("", "Inspect: /changes <id>   Revert: /revert <id>");
    return lines.join("\n");
}
async function renderChangeSetDetail(changeSet, snapshotService, options) {
    const maxDiffChars = options.maxDiffChars ?? DEFAULT_MAX_DIFF_CHARS;
    const lines = [`Change set #${changeSet.id}`, "", `Summary: ${formatSummary(changeSet)}`, ""];
    for (const status of ["created", "modified", "deleted", "skipped"]) {
        const files = changeSet.files.filter((file) => file.status === status);
        if (!files.length)
            continue;
        lines.push(`${formatStatusTitle(status)}:`);
        for (const file of files)
            lines.push(`- ${file.path}${file.skippedReason ? ` (${file.skippedReason})` : ""}`);
        lines.push("");
    }
    const diff = await renderChangeSetDiff(changeSet.files, snapshotService, maxDiffChars);
    if (diff)
        lines.push("Diff", "", diff);
    lines.push("Commands:", `/revert ${changeSet.id}`);
    return lines.join("\n").trimEnd();
}
async function renderChangeSetDiff(files, snapshotService, maxChars) {
    const chunks = [];
    let usedChars = 0;
    for (const file of files) {
        if (file.status === "skipped")
            continue;
        const chunk = await renderFileDiff(file, snapshotService);
        if (!chunk)
            continue;
        if (usedChars + chunk.length > maxChars) {
            chunks.push(`... diff truncated at ${maxChars} characters ...`);
            break;
        }
        chunks.push(chunk);
        usedChars += chunk.length;
    }
    return chunks.join("\n\n");
}
async function renderFileDiff(file, snapshotService) {
    if (file.status === "created" && file.afterHash) {
        return renderAddedFile(file.path, await snapshotService.readText(file.afterHash));
    }
    if (file.status === "deleted" && file.beforeHash) {
        return renderDeletedFile(file.path, await snapshotService.readText(file.beforeHash));
    }
    if (file.status === "modified" && file.beforeHash && file.afterHash) {
        return renderModifiedFile(file.path, await snapshotService.readText(file.beforeHash), await snapshotService.readText(file.afterHash));
    }
    return undefined;
}
function renderAddedFile(path, content) {
    return [`--- /dev/null`, `+++ ${path}`, ...content.split(/\r?\n/u).map((line) => `+${line}`)].join("\n");
}
function renderDeletedFile(path, content) {
    return [`--- ${path}`, `+++ /dev/null`, ...content.split(/\r?\n/u).map((line) => `-${line}`)].join("\n");
}
function renderModifiedFile(path, before, after) {
    const beforeLines = before.split(/\r?\n/u);
    const afterLines = after.split(/\r?\n/u);
    const lines = [`--- ${path}`, `+++ ${path}`];
    const maxLines = Math.max(beforeLines.length, afterLines.length);
    for (let index = 0; index < maxLines; index++) {
        const beforeLine = beforeLines[index];
        const afterLine = afterLines[index];
        if (beforeLine === afterLine) {
            if (beforeLine !== undefined)
                lines.push(` ${beforeLine}`);
            continue;
        }
        if (beforeLine !== undefined)
            lines.push(`-${beforeLine}`);
        if (afterLine !== undefined)
            lines.push(`+${afterLine}`);
    }
    return lines.join("\n");
}
function formatSummary(changeSet) {
    return [
        changeSet.summary.created > 0 ? `+${changeSet.summary.created}` : undefined,
        changeSet.summary.modified > 0 ? `~${changeSet.summary.modified}` : undefined,
        changeSet.summary.deleted > 0 ? `-${changeSet.summary.deleted}` : undefined,
        changeSet.summary.skipped > 0 ? `skipped ${changeSet.summary.skipped}` : undefined,
    ]
        .filter(Boolean)
        .join(" ");
}
function formatTimestamp(timestamp) {
    return new Date(timestamp).toISOString();
}
function formatFilePreview(files) {
    const preview = files
        .slice(0, 3)
        .map((file) => file.path)
        .join(", ");
    const suffix = files.length > 3 ? `, +${files.length - 3} more` : "";
    return preview ? `(${preview}${suffix})` : "";
}
function formatStatusTitle(status) {
    return status[0]?.toUpperCase() + status.slice(1);
}
//# sourceMappingURL=change-history.js.map