import { EditorSelection } from "@codemirror/state";
import { EditorView } from "@codemirror/view";
import * as MarkdownStories from "@src/stories/playground/Markdown.stories";
import { composeStories } from "@storybook/react-vite";
import { describe, expect, it } from "vitest";
import {
    mountStory,
    nextFrame,
    preparePreviewEditor,
    scrollEditorTo,
} from "../../_setup/e2e-helpers";
import { md, mdViewer } from "../../_setup/markdown-classes";

const Stories = composeStories(MarkdownStories);

function findLineStart(doc: string, prefix: string): number {
    const idx = doc.indexOf(prefix);
    if (idx < 0) throw new Error(`prefix not found in doc: ${prefix}`);
    return idx;
}

describe("MarkdownEditor / media sizing", () => {
    it("images and videos do not exceed the editor content width", async () => {
        mountStory(<Stories.Editor />);
        const view = await preparePreviewEditor();

        const docStr = view.state.doc.toString();
        const imageLine = findLineStart(docStr, "![SurrealDB logo]");
        const videoLine = findLineStart(docStr, "![Product demo]");

        // Caret away from media lines so replace widgets render.
        view.dispatch({ selection: EditorSelection.cursor(0) });
        await scrollEditorTo(view, imageLine);
        await nextFrame();

        const scroller = view.scrollDOM;
        const scrollerWidth = scroller.clientWidth;
        expect(scrollerWidth).toBeGreaterThan(0);

        let imgs: NodeListOf<HTMLImageElement> = document.querySelectorAll(
            `.cm-editor [data-md-widget="ImageWidget"] img, .cm-editor .${md.image}`,
        );
        for (let i = 0; i < 40 && imgs.length === 0; i++) {
            await nextFrame();
            imgs = document.querySelectorAll(
                `.cm-editor [data-md-widget="ImageWidget"] img, .cm-editor .${md.image}`,
            );
        }

        expect(imgs.length).toBeGreaterThan(0);
        for (const img of Array.from(imgs)) {
            const rect = img.getBoundingClientRect();
            expect(
                rect.width,
                `image ${img.src} width ${rect.width} vs scroller ${scrollerWidth}`,
            ).toBeLessThanOrEqual(scrollerWidth + 2);
        }

        await scrollEditorTo(view, videoLine);
        await nextFrame();

        let videos: NodeListOf<HTMLElement> = document.querySelectorAll(
            `.cm-editor [data-md-widget="VideoWidget"] video, .cm-editor [data-md-widget="VideoWidget"] .${md.image}`,
        );
        for (let i = 0; i < 40 && videos.length === 0; i++) {
            await nextFrame();
            videos = document.querySelectorAll(
                `.cm-editor [data-md-widget="VideoWidget"] video, .cm-editor [data-md-widget="VideoWidget"] .${md.image}`,
            );
        }

        expect(videos.length).toBeGreaterThan(0);
        for (const video of Array.from(videos)) {
            const host =
                video.closest("[data-md-widget='VideoWidget']") ?? video.closest(`.${md.image}`);
            const rect = (host ?? video).getBoundingClientRect();
            expect(
                rect.width,
                `video width ${rect.width} vs scroller ${scrollerWidth}`,
            ).toBeLessThanOrEqual(scrollerWidth + 2);
        }
    });

    it("matches MarkdownViewer image width in the split playground", async () => {
        mountStory(<Stories.SideBySide />);
        const view = await preparePreviewEditor();

        const docStr = view.state.doc.toString();
        const imagePos = findLineStart(docStr, "![SurrealDB logo]");
        view.dispatch({
            selection: EditorSelection.cursor(0),
            effects: EditorView.scrollIntoView(imagePos, { y: "center" }),
        });
        await nextFrame();

        let editorImg: HTMLImageElement | null = null;
        let viewerImg: HTMLImageElement | null = null;
        let editorW = 0;
        let viewerW = 0;

        for (let i = 0; i < 40; i++) {
            editorImg = document.querySelector<HTMLImageElement>(
                `.cm-editor [data-md-widget="ImageWidget"] img`,
            );
            viewerImg = document.querySelector<HTMLImageElement>(
                `.${mdViewer.viewerRoot} img.${md.image}`,
            );
            if (editorImg && viewerImg) {
                editorW = editorImg.getBoundingClientRect().width;
                viewerW = viewerImg.getBoundingClientRect().width;
                if (editorW > 0 && viewerW > 0 && Math.abs(editorW - viewerW) <= 12) {
                    break;
                }
            }
            await nextFrame();
        }

        expect(editorImg).not.toBeNull();
        expect(viewerImg).not.toBeNull();
        // Editor `.cm-line` uses 6px inline padding mirrored in viewer.module.scss.
        expect(Math.abs(editorW - viewerW)).toBeLessThanOrEqual(12);
    });
});
