// @vitest-environment happy-dom

import { beforeEach, describe, expect, it, vi } from "vitest";
import { readFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";

vi.mock("../app/i18n.js", () => ({
    t: (_key, fallback) => fallback,
}));

vi.mock("../app/toast.js", () => ({
    comfyToast: vi.fn(),
}));

vi.mock("../app/events.js", () => ({
    EVENTS: {},
}));

vi.mock("../api/endpoints.js", () => ({
    buildViewURL: vi.fn(() => "http://example.com/media.mp4"),
    buildAssetViewURL: vi.fn(() => "http://example.com/media.mp4"),
}));

vi.mock("../features/viewer/videoSync.js", () => ({
    installFollowerVideoSync: vi.fn(() => ({ abort: vi.fn() })),
}));

vi.mock("../features/viewer/model3dRenderer.js", () => ({
    isModel3DInteractionTarget: vi.fn(() => false),
    createModel3DMediaElement: vi.fn(() => {
        const el = document.createElement("div");
        el.className = "mjr-model3d-host";
        return el;
    }),
    MODEL3D_EXTS: new Set(),
}));

const __dirname = dirname(fileURLToPath(import.meta.url));
const themeCss = readFileSync(resolve(__dirname, "../theme/theme-comfy.css"), "utf8");

function injectThemeCss() {
    const style = document.createElement("style");
    style.setAttribute("data-test-theme", "mjr");
    style.textContent = themeCss;
    document.head.appendChild(style);
}

function makeGenInfoFragment() {
    const frag = document.createDocumentFragment();
    const row = document.createElement("div");
    row.dataset.field = "prompt";
    const strong = document.createElement("strong");
    strong.textContent = "Prompt: ";
    row.appendChild(strong);
    row.appendChild(document.createTextNode("test"));
    frag.appendChild(row);
    return frag;
}

describe("floating viewer geninfo sidebar", () => {
    beforeEach(() => {
        document.head.innerHTML = "";
        document.body.innerHTML = "";
        injectThemeCss();
    });

    it("renders video geninfo in the side panel instead of over the player", async () => {
        const { FloatingViewer } = await import("../features/viewer/FloatingViewer.js");

        const viewer = new FloatingViewer();
        viewer._contentEl = document.createElement("div");
        viewer._genSidebarEl = document.createElement("aside");
        document.body.appendChild(viewer._contentEl);
        document.body.appendChild(viewer._genSidebarEl);
        viewer._mediaA = {
            filename: "clip.mp4",
            url: "http://example.com/clip.mp4",
            generation_time_ms: 12_300,
        };
        viewer._buildGenInfoDOM = vi.fn(() => makeGenInfoFragment());

        viewer._renderSimple();
        viewer._renderGenInfoSidebar();

        const player = viewer._contentEl.querySelector(".mjr-mfv-player-host");
        const overlay = viewer._contentEl.querySelector(".mjr-mfv-geninfo");
        const sidebar = viewer._genSidebarEl.querySelector(".mjr-mfv-gen-sidebar-body");

        expect(player).toBeTruthy();
        expect(overlay).toBeFalsy();
        expect(sidebar).toBeTruthy();
        expect(sidebar.textContent).toContain("Prompt: test");
        expect(viewer._genSidebarEl.querySelector(".mjr-mfv-gen-time-badge")?.textContent).toContain(
            "12.3s",
        );
    });

    it("keeps legacy geninfo overlay nodes hidden by default", () => {
        const wrap = document.createElement("div");
        wrap.className = "mjr-mfv-simple-container";
        wrap.style.position = "relative";
        wrap.style.width = "480px";
        wrap.style.height = "320px";

        const player = document.createElement("div");
        player.className = "mjr-mfv-simple-player";
        const controls = document.createElement("div");
        controls.className = "mjr-mfv-simple-player-controls";
        player.appendChild(controls);

        const overlay = document.createElement("div");
        overlay.className = "mjr-mfv-geninfo";
        overlay.appendChild(makeGenInfoFragment());

        wrap.appendChild(player);
        wrap.appendChild(overlay);
        document.body.appendChild(wrap);

        const styles = getComputedStyle(overlay);

        expect(styles.display).toBe("none");
    });

    it("only shows asset A geninfo after returning to simple mode", async () => {
        const { FloatingViewer, MFV_MODES } = await import("../features/viewer/FloatingViewer.js");

        const viewer = new FloatingViewer();
        viewer._genSidebarEl = document.createElement("aside");
        viewer._mediaA = { filename: "a.png", metadata_raw: { prompt: "asset A prompt", seed: 1 } };
        viewer._mediaB = { filename: "b.png", metadata_raw: { prompt: "asset B prompt", seed: 2 } };
        document.body.appendChild(viewer._genSidebarEl);

        viewer._mode = MFV_MODES.AB;
        viewer._renderGenInfoSidebar();
        expect(viewer._genSidebarEl.textContent).toContain("Asset A");
        expect(viewer._genSidebarEl.textContent).toContain("Asset B");

        viewer._mode = MFV_MODES.SIMPLE;
        viewer._renderGenInfoSidebar();
        expect(viewer._genSidebarEl.textContent).toContain("Current Asset");
        expect(viewer._genSidebarEl.textContent).toContain("asset A prompt");
        expect(viewer._genSidebarEl.textContent).not.toContain("Asset B");
        expect(viewer._genSidebarEl.textContent).not.toContain("asset B prompt");
    });

    it("marks bottom geninfo cards for the compact wrapped layout", async () => {
        const { FloatingViewer } = await import("../features/viewer/FloatingViewer.js");

        const viewer = new FloatingViewer();
        const wrapper = document.createElement("div");
        wrapper.className = "mjr-mfv-content-wrapper";
        wrapper.setAttribute("data-sidebar-pos", "bottom");
        viewer._contentWrapper = wrapper;
        viewer._genSidebarEl = document.createElement("aside");
        wrapper.appendChild(viewer._genSidebarEl);
        document.body.appendChild(wrapper);
        viewer._mediaA = {
            filename: "image.png",
            generation_time_ms: 2600,
            metadata_raw: {
                prompt: "bee wearing a white space suit",
                model: "emie-image-turbo-nf4p4",
                clip: "minicfual-3.3b",
                vae: "flux2.vae",
                sampler: "euler",
                steps: 8,
                cfg: 1,
                scheduler: "simple",
            },
        };

        viewer._renderGenInfoSidebar();

        expect(viewer._genSidebarEl.classList.contains("open")).toBe(true);
        expect(viewer._genSidebarEl.querySelector(".mjr-mfv-gen-time-badge")).toBeTruthy();
        expect(viewer._genSidebarEl.querySelector(".mjr-mfv-gen-card--positive-prompt")).toBeTruthy();
        expect(viewer._genSidebarEl.querySelector(".mjr-mfv-gen-card--model-and-lora")).toBeTruthy();
        expect(viewer._genSidebarEl.querySelector(".mjr-mfv-gen-card--sampling")).toBeTruthy();
        const body = viewer._genSidebarEl.querySelector(".mjr-mfv-gen-sidebar-body");
        expect(getComputedStyle(body).overflowX).toBe("hidden");
        expect(getComputedStyle(body).overflowY).toBe("visible");
        expect(getComputedStyle(body).display).toBe("block");
    });

    it("renders ideogram excerpt blocks like the grid panel generation details", async () => {
        const { FloatingViewer } = await import("../features/viewer/FloatingViewer.js");

        const viewer = new FloatingViewer();
        viewer._genSidebarEl = document.createElement("aside");
        document.body.appendChild(viewer._genSidebarEl);
        viewer._mediaA = {
            filename: "ideogram.png",
            metadata_raw: {
                workflow: "i2i",
                ideogram: {
                    high_level_description: "A refined campaign image about skin aging.",
                    background: "warm sunset gradient background",
                    elements: [
                        {
                            description: "A graceful woman in side profile.",
                            x: 0.49,
                            y: 0.391,
                            w: 0.274,
                            h: 0.497,
                        },
                    ],
                    payload: { prompt: "json prompt" },
                },
            },
        };

        viewer._renderGenInfoSidebar();

        expect(viewer._genSidebarEl.querySelector(".mjr-mfv-gen-card--high-level-description")).toBeTruthy();
        expect(viewer._genSidebarEl.querySelector(".mjr-mfv-gen-card--background")).toBeTruthy();
        expect(viewer._genSidebarEl.querySelector(".mjr-mfv-gen-card--element-1")).toBeTruthy();
        expect(viewer._genSidebarEl.querySelector(".mjr-mfv-gen-card--json-sent-to-text-encoder")).toBeTruthy();
    });

    it("hydrates MFV metadata when the current prompt is only a file path", async () => {
        const { ensureViewerMetadataAsset } = await import("../features/viewer/genInfo.js");
        const getAssetMetadata = vi.fn(() =>
            Promise.resolve({
                ok: true,
                data: {
                    metadata_raw: {
                        geninfo: {
                            positive: { value: "real LTX prompt" },
                            seed: { value: 0 },
                        },
                    },
                },
            }),
        );

        const asset = {
            id: 10650,
            filename: "ltx_director_00010_.mp4",
            type: "video",
            prompt: "D:/____COMFY_OUTPUTS/video/ltx_director_00010_.mp4",
            metadata_raw: {
                prompt: "D:/____COMFY_OUTPUTS/video/ltx_director_00010_.mp4",
                workflow: { nodes: [] },
            },
        };

        const hydrated = await ensureViewerMetadataAsset(asset, {
            getAssetMetadata,
            getFileMetadataScoped: vi.fn(),
            metadataCache: null,
        });

        expect(getAssetMetadata).toHaveBeenCalledWith(10650, {});
        expect(hydrated.metadata_raw.geninfo.positive.value).toBe("real LTX prompt");
        expect(hydrated.metadata_raw.geninfo.seed.value).toBe(0);
    });
});
