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 {
    getEditorView,
    mountStory,
    nextFrame,
    page,
    readDoc,
    userEvent,
} from "../../_setup/e2e-helpers";
import { md, mdViewer } from "../../_setup/markdown-classes";

const Stories = composeStories(MarkdownStories);

async function getMode(): Promise<string | null> {
    const editor = document.querySelector<HTMLElement>(".cm-editor");
    return editor?.getAttribute("data-markdown-mode") ?? null;
}

describe("MarkdownEditor / modes", () => {
    it("toggling the SegmentedControl swaps mode without remounting the editor", async () => {
        mountStory(<Stories.Editor />);
        await getEditorView();

        const initialEditor = document.querySelector(".cm-editor");
        expect(initialEditor).not.toBeNull();
        expect(await getMode()).toBe("preview");

        // Mantine's SegmentedControl hides the underlying radio inputs and
        // styles a sibling label, so we click the visible label text.
        await userEvent.click(page.getByText("Source", { exact: true }));
        await nextFrame();
        expect(await getMode()).toBe("source");
        expect(document.querySelector(".cm-editor")).toBe(initialEditor);

        await userEvent.click(page.getByText("Preview", { exact: true }));
        await nextFrame();
        expect(await getMode()).toBe("preview");
        expect(document.querySelector(".cm-editor")).toBe(initialEditor);
    });

    it("standalone MarkdownViewer is not a CodeMirror surface", async () => {
        mountStory(<Stories.Viewer />);
        await nextFrame();
        await nextFrame();

        expect(document.querySelector(`.${mdViewer.viewerRoot}`)).not.toBeNull();
        expect(document.querySelector(".cm-editor")).toBeNull();
        expect(document.querySelector(".cm-content")).toBeNull();
    });

    it("source mode renders raw markdown markers (no hidden marks)", async () => {
        mountStory(<Stories.Editor />);
        await getEditorView();
        await userEvent.click(page.getByText("Source", { exact: true }));
        await nextFrame();
        await getEditorView();

        await nextFrame();
        const hidden = document.querySelectorAll(`.${md.markHidden}`);
        expect(hidden.length).toBe(0);

        const text = document.querySelector(".cm-content")?.textContent ?? "";
        expect(text).toContain("# Welcome to the live preview editor");
        expect(text).toContain("**bold**");
    });

    it("keeps edits when cycling Source then Preview after a document change", async () => {
        mountStory(<Stories.Editor />);
        const view = await getEditorView();

        const token = `\nuniq-e2e-${Date.now()}`;
        view.dispatch({
            selection: EditorSelection.cursor(view.state.doc.length),
            changes: { from: view.state.doc.length, insert: token },
        });
        await nextFrame();

        await userEvent.click(page.getByText("Source", { exact: true }));
        await nextFrame();
        expect((await readDoc()).includes(token.trim())).toBe(true);

        await userEvent.click(page.getByText("Preview", { exact: true }));
        await nextFrame();

        expect((await readDoc()).includes(token.trim())).toBe(true);
        expect(document.querySelector(".cm-content")?.getAttribute("aria-readonly")).not.toBe(
            "true",
        );
    });
});
