import { EditorSelection } from "@codemirror/state";
import * as MarkdownStories from "@src/stories/playground/Markdown.stories";
import { composeStories } from "@storybook/react-vite";
import { describe, expect, it } from "vitest";
import {
    focusAndType,
    getEditorView,
    mountStory,
    nextFrame,
    readDoc,
} from "../../_setup/e2e-helpers";

const Stories = composeStories(MarkdownStories);

describe("MarkdownEditor / edits", () => {
    it("reflects typed characters in the underlying document", async () => {
        mountStory(<Stories.Editor />);
        const view = await getEditorView();

        view.dispatch({ selection: EditorSelection.cursor(view.state.doc.length) });
        view.focus();

        await focusAndType("\nfresh content");
        await nextFrame();

        const doc = await readDoc();
        expect(doc.endsWith("\nfresh content")).toBe(true);
    });

    it("mirrors the source preview block in the story when the document changes", async () => {
        mountStory(<Stories.Editor />);
        const view = await getEditorView();

        view.dispatch({
            selection: EditorSelection.cursor(view.state.doc.length),
            changes: { from: view.state.doc.length, insert: "\n\nMirror this line." },
        });
        await nextFrame();
        await nextFrame();

        const previewBlock = document.querySelector<HTMLPreElement>("pre");
        expect(previewBlock).not.toBeNull();
        expect(previewBlock?.textContent ?? "").toContain("Mirror this line.");
    });

    it("expands triple backtick into a fenced block with caret on the empty content line", async () => {
        mountStory(<Stories.Editor />);
        const view = await getEditorView();

        view.focus();
        const insertAt = view.state.doc.length;
        view.dispatch({
            changes: { from: insertAt, insert: "\n\n``" },
            selection: EditorSelection.cursor(insertAt + 4),
        });
        await nextFrame();

        await focusAndType("`");
        await nextFrame();

        const doc = await readDoc();
        expect(doc.endsWith("\n\n```\n\n```")).toBe(true);

        const main = view.state.selection.main;
        expect(main.empty).toBe(true);
        const contentLine = view.state.doc.lineAt(main.head);
        expect(contentLine.text).toBe("");
        expect(main.head).toBe(contentLine.from);
        expect(view.state.doc.line(contentLine.number - 1).text).toBe("```");
        expect(view.state.doc.line(contentLine.number + 1).text).toBe("```");
    });

    it("wraps a non-empty selection with backticks when ` is typed", async () => {
        mountStory(<Stories.Editor />);
        const view = await getEditorView();

        const target = "First item";
        const start = view.state.doc.toString().indexOf(target);
        expect(start).toBeGreaterThan(-1);

        view.focus();
        view.dispatch({ selection: EditorSelection.range(start, start + target.length) });
        await nextFrame();

        await focusAndType("`");
        await nextFrame();

        const doc = await readDoc();
        expect(doc).toContain("`First item`");
    });

    it("auto-continues a bullet list when Enter is pressed at the end of a list item", async () => {
        mountStory(<Stories.Editor />);
        const view = await getEditorView();

        const target = "- First item";
        const start = view.state.doc.toString().indexOf(target);
        expect(start).toBeGreaterThan(-1);
        const eol = start + target.length;

        view.focus();
        view.dispatch({ selection: EditorSelection.cursor(eol) });
        await nextFrame();

        await focusAndType("{Enter}continued");
        await nextFrame();

        const doc = await readDoc();
        expect(doc).toContain("- First item\n- continued\n- Second item");
    });
});
