import { EditorSelection, EditorState } from "@codemirror/state";
import { markdownTableFormatter, tableFormatSpec } from "@src/lib/markdown/editor/table-format";
import { describe, expect, it } from "vitest";

function stateWith(doc: string, caret: number): EditorState {
    return EditorState.create({
        doc,
        selection: EditorSelection.cursor(caret),
        extensions: [markdownTableFormatter()],
    });
}

describe("tableFormatSpec", () => {
    it("aligns columns and pads cells with trailing spaces", () => {
        const doc = "| a | b |\n| - | - |\n| longvalue | y |";
        const state = stateWith(doc, doc.indexOf("longvalue"));
        const spec = tableFormatSpec(state);
        expect(spec).not.toBeNull();
        const next = state.update(spec ?? {}).state;
        expect(next.doc.toString()).toBe(
            ["| a         | b   |", "| --------- | --- |", "| longvalue | y   |"].join("\n"),
        );
    });

    it("returns null for an already-formatted table", () => {
        const doc = ["| a         | b   |", "| --------- | --- |", "| longvalue | y   |"].join(
            "\n",
        );
        const state = stateWith(doc, doc.indexOf("longvalue"));
        expect(tableFormatSpec(state)).toBeNull();
    });

    it("returns null when the caret is not in a table", () => {
        const doc = "just a paragraph with a | pipe";
        const state = stateWith(doc, 3);
        expect(tableFormatSpec(state)).toBeNull();
    });

    it("keeps the caret within the edited cell after formatting", () => {
        const doc = "| a | b |\n| - | - |\n| longvalue | y |";
        // Caret right after "long" inside the body cell.
        const caret = doc.indexOf("longvalue") + 4;
        const state = stateWith(doc, caret);
        const spec = tableFormatSpec(state);
        const next = state.update(spec ?? {}).state;
        const formatted = next.doc.toString();
        const cellStart = formatted.indexOf("longvalue");
        expect(next.selection.main.head).toBe(cellStart + 4);
    });
});

describe("markdownTableFormatter", () => {
    it("reformats the table after an edit inside it", () => {
        const doc = "| a | b |\n| - | - |\n| x | y |";
        const state = EditorState.create({
            doc,
            selection: EditorSelection.cursor(doc.indexOf("x") + 1),
            extensions: [markdownTableFormatter()],
        });
        // Type "tra" after "x" → "xtra", which widens column 1.
        const tr = state.update({
            changes: { from: doc.indexOf("x") + 1, insert: "tra" },
            selection: EditorSelection.cursor(doc.indexOf("x") + 4),
        });
        const next = tr.state;
        expect(next.doc.toString()).toContain("| xtra | y   |");
    });

    it("does not loop on its own formatting transaction", () => {
        const doc = "| a | b |\n| - | - |\n| x | y |";
        const state = EditorState.create({
            doc,
            selection: EditorSelection.cursor(doc.indexOf("x") + 1),
            extensions: [markdownTableFormatter()],
        });
        const tr = state.update({
            changes: { from: doc.indexOf("x") + 1, insert: "tra" },
        });
        // A single user edit produces a stable, idempotent result.
        const again = tr.state.update({ selection: EditorSelection.cursor(0) });
        expect(again.state.doc.toString()).toBe(tr.state.doc.toString());
    });
});
