/*
Copyright 2025 New Vector Ltd.

SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/

import { type Page } from "@playwright/test";

import { test, expect } from "../../element-web-test";
import { getSampleFilePath, readSampleFileSync } from "../../sample-files";

const screenshotOptions = (page: Page) => ({
    // Hide the jump to bottom button in the timeline to avoid flakiness
    // Exclude timestamp and read marker from snapshot
    css: `
        .mx_MessageTimestamp {
            visibility: hidden;
        }
        .mx_JumpToBottomButton {
            display: none !important;
        }
        .mx_TopUnreadMessagesBar, .mx_MessagePanel_myReadMarker {
            display: none !important;
        }
    `,
});

const IMAGE_FILE = readSampleFileSync("element.png", null);

test.describe("Custom Component API", () => {
    test.use({
        displayName: "Manny",
        config: {
            modules: ["/modules/custom-component-module.js"],
        },
        page: async ({ page }, use) => {
            await page.route("/modules/custom-component-module.js", async (route) => {
                await route.fulfill({ path: getSampleFilePath("custom-component-module.js") });
            });
            await use(page);
        },
        room: async ({ page, app, user, bot }, use) => {
            const roomId = await app.client.createRoom({ name: "TestRoom" });
            await use({ roomId });
        },
    });
    test.describe("basic functionality", () => {
        test(
            "should replace the render method of a textual event",
            { tag: "@screenshot" },
            async ({ page, room, app }) => {
                await app.viewRoomById(room.roomId);
                await app.client.sendMessage(room.roomId, "Simple message");
                await expect(await page.locator(".mx_EventTile_last")).toMatchScreenshot(
                    "custom-component-tile.png",
                    screenshotOptions(page),
                );
            },
        );
        test(
            "should fall through if one module does not render a component",
            { tag: "@screenshot" },
            async ({ page, room, app }) => {
                await app.viewRoomById(room.roomId);
                await app.client.sendMessage(room.roomId, "Fall through here");
                await expect(await page.locator(".mx_EventTile_last")).toMatchScreenshot(
                    "custom-component-tile-fall-through.png",
                    screenshotOptions(page),
                );
            },
        );
        test(
            "should render the original content of a textual event conditionally",
            { tag: "@screenshot" },
            async ({ page, room, app }) => {
                await app.viewRoomById(room.roomId);
                await app.client.sendMessage(room.roomId, "Do not replace me");
                await expect(await page.locator(".mx_EventTile_last")).toMatchScreenshot(
                    "custom-component-tile-original.png",
                    screenshotOptions(page),
                );
            },
        );
        test("should disallow editing when the allowEditingEvent hint is set to false", async ({ page, room, app }) => {
            await app.viewRoomById(room.roomId);
            await app.client.sendMessage(room.roomId, "Do not show edits");
            await page.getByText("Do not show edits").hover();
            await expect(
                await page.getByRole("toolbar", { name: "Message Actions" }).getByRole("button", { name: "Edit" }),
            ).not.toBeVisible();
        });
        test("should disallow downloading media when the allowDownloading hint is set to false", async ({
            page,
            room,
            app,
        }) => {
            await app.viewRoomById(room.roomId);
            await app.viewRoomById(room.roomId);
            const upload = await app.client.uploadContent(IMAGE_FILE, { name: "bad.png", type: "image/png" });
            await app.client.sendEvent(room.roomId, null, "m.room.message", {
                msgtype: "m.image",
                body: "bad.png",
                url: upload.content_uri,
            });

            await app.timeline.scrollToBottom();
            const imgTile = page.locator(".mx_MImageBody").first();
            await expect(imgTile).toBeVisible();
            await imgTile.hover();
            await expect(page.getByRole("button", { name: "Download" })).not.toBeVisible();
            await imgTile.click();
            await expect(page.getByLabel("Image view").getByLabel("Download")).not.toBeVisible();
        });
        test("should allow downloading media when the allowDownloading hint is set to true", async ({
            page,
            room,
            app,
        }) => {
            await app.viewRoomById(room.roomId);
            await app.viewRoomById(room.roomId);
            const upload = await app.client.uploadContent(IMAGE_FILE, { name: "good.png", type: "image/png" });
            await app.client.sendEvent(room.roomId, null, "m.room.message", {
                msgtype: "m.image",
                body: "good.png",
                url: upload.content_uri,
            });

            await app.timeline.scrollToBottom();
            const imgTile = page.locator(".mx_MImageBody").first();
            await expect(imgTile).toBeVisible();
            await imgTile.hover();
            await expect(page.getByRole("button", { name: "Download" })).toBeVisible();
            await imgTile.click();
            await expect(page.getByLabel("Image view").getByLabel("Download")).toBeVisible();
        });
        test(
            "should render the next registered component if the filter function throws",
            { tag: "@screenshot" },
            async ({ page, room, app }) => {
                await app.viewRoomById(room.roomId);
                await app.client.sendMessage(room.roomId, "Crash the filter!");
                await expect(await page.locator(".mx_EventTile_last")).toMatchScreenshot(
                    "custom-component-crash-handle-filter.png",
                    screenshotOptions(page),
                );
            },
        );
        test(
            "should render original component if the render function throws",
            { tag: "@screenshot" },
            async ({ page, room, app }) => {
                await app.viewRoomById(room.roomId);
                await app.client.sendMessage(room.roomId, "Crash the renderer!");
                await expect(await page.locator(".mx_EventTile_last")).toMatchScreenshot(
                    "custom-component-crash-handle-renderer.png",
                    screenshotOptions(page),
                );
            },
        );
    });
});
