/*
Copyright 2024 New Vector Ltd.
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.

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 { test, expect } from "../../element-web-test";
import { SettingLevel } from "../../../src/settings/SettingLevel";
import { getSampleFilePath } from "../../sample-files";

const CtrlOrMeta = process.platform === "darwin" ? "Meta" : "Control";

test.describe("Composer", () => {
    test.use({
        displayName: "Janet",
        botCreateOpts: {
            displayName: "Bob",
        },
    });

    test.use({
        room: async ({ app, user }, use) => {
            const roomId = await app.client.createRoom({ name: "Composing Room" });
            await app.viewRoomByName("Composing Room");
            await use({ roomId });
        },
    });

    test.beforeEach(async ({ room }) => {}); // trigger room fixture

    test.describe("CIDER", () => {
        test("sends a message when you click send or press Enter", async ({ page }) => {
            const composer = page.getByRole("textbox", { name: "Send an unencrypted message…" });

            // Type a message
            await composer.pressSequentially("my message 0");
            // It has not been sent yet
            await expect(page.locator(".mx_EventTile_body", { hasText: "my message 0" })).not.toBeVisible();

            // Click send
            await page.getByRole("button", { name: "Send message" }).click();
            // It has been sent
            await expect(
                page.locator(".mx_EventTile_last .mx_EventTile_body", { hasText: "my message 0" }),
            ).toBeVisible();

            // Type another and press Enter afterward
            await composer.pressSequentially("my message 1");
            await composer.press("Enter");
            // It was sent
            await expect(
                page.locator(".mx_EventTile_last .mx_EventTile_body", { hasText: "my message 1" }),
            ).toBeVisible();
        });

        test("can write formatted text", async ({ page }) => {
            const composer = page.getByRole("textbox", { name: "Send an unencrypted message…" });

            await composer.pressSequentially("my bold");
            await composer.press(`${CtrlOrMeta}+KeyB`);
            await composer.pressSequentially(" message");
            await page.getByRole("button", { name: "Send message" }).click();
            // Note: both "bold" and "message" are bold, which is probably surprising
            await expect(page.locator(".mx_EventTile_body strong", { hasText: "bold message" })).toBeVisible();
        });

        test("should allow user to input emoji via graphical picker", async ({ page, app }) => {
            await app.getComposer(false).getByRole("button", { name: "Emoji" }).click();

            await page.getByTestId("mx_EmojiPicker").locator(".mx_EmojiPicker_item", { hasText: "😇" }).click();

            await page.locator(".mx_ContextualMenu_background").click(); // Close emoji picker
            await page.getByRole("textbox", { name: "Send an unencrypted message…" }).press("Enter"); // Send message

            await expect(page.locator(".mx_EventTile_body", { hasText: "😇" })).toBeVisible();
        });

        test.describe("render emoji picker with larger viewport height", async () => {
            test.use({ viewport: { width: 1280, height: 720 } });
            test("render emoji picker", { tag: "@screenshot" }, async ({ page, app }) => {
                await app.getComposer(false).getByRole("button", { name: "Emoji" }).click();
                // Mask the background of the screenshot to avoid failing the test just because some
                // other component have changed its rendering.
                await expect(page.getByTestId("mx_EmojiPicker")).toMatchScreenshot("emoji-picker.png", {
                    css: `
                        .mx_ContextualMenu_background {
                            background-color: magenta !important;
                        }
                    `,
                });
            });
        });

        test.describe("render emoji picker with small viewport height", async () => {
            test.use({ viewport: { width: 1280, height: 360 } });
            test("render emoji picker", { tag: "@screenshot" }, async ({ page, app }) => {
                await app.getComposer(false).getByRole("button", { name: "Emoji" }).click();
                // Mask the background of the screenshot to avoid failing the test just because some
                // other component have changed its rendering.
                await expect(page.getByTestId("mx_EmojiPicker")).toMatchScreenshot("emoji-picker-small.png", {
                    css: `
                        .mx_ContextualMenu_background {
                            background-color: magenta !important;
                        }
                    `,
                });
            });
        });

        test("should have focus lock in emoji picker", async ({ page, app }) => {
            const emojiButton = app.getComposer(false).getByRole("button", { name: "Emoji" });

            // Open emoji picker by clicking the button
            await emojiButton.click();

            // Wait for emoji picker to be visible
            const emojiPicker = page.getByTestId("mx_EmojiPicker");
            await expect(emojiPicker).toBeVisible();

            // Get initial focused element (should be search input)
            const searchInput = emojiPicker.getByRole("textbox", { name: "Search" });
            await expect(searchInput).toBeFocused();

            // Try to tab multiple times - focus should stay within emoji picker
            await page.keyboard.press("Tab");
            await page.keyboard.press("Tab");
            await page.keyboard.press("Tab");
            await page.keyboard.press("Tab");
            await page.keyboard.press("Tab");

            // Verify we're still within the emoji picker (not back to composer)
            const focusedElement = await page.evaluate(() => document.activeElement?.closest(".mx_EmojiPicker"));
            expect(focusedElement).not.toBeNull();

            // Close with Escape key
            await page.keyboard.press("Escape");

            // Verify emoji picker is closed
            await expect(emojiPicker).not.toBeVisible();

            // Verify focus returns to emoji button
            await expect(emojiButton).toBeFocused();
        });

        test.describe("when Control+Enter is required to send", () => {
            test.beforeEach(async ({ app }) => {
                await app.settings.setValue("MessageComposerInput.ctrlEnterToSend", null, SettingLevel.ACCOUNT, true);
            });

            test("only sends when you press Control+Enter", async ({ page }) => {
                const composer = page.getByRole("textbox", { name: "Send an unencrypted message…" });
                // Type a message and press Enter
                await composer.pressSequentially("my message 3");
                await composer.press("Enter");
                // It has not been sent yet
                await expect(page.locator(".mx_EventTile_body", { hasText: "my message 3" })).not.toBeVisible();

                // Press Control+Enter
                await composer.press(`${CtrlOrMeta}+Enter`);
                // It was sent
                await expect(
                    page.locator(".mx_EventTile_last .mx_EventTile_body", { hasText: "my message 3" }),
                ).toBeVisible();
            });
        });

        test("can send mention", { tag: "@screenshot" }, async ({ page, bot, app }) => {
            // Set up a private room so we have another user to mention
            await app.client.createRoom({
                is_direct: true,
                invite: [bot.credentials.userId],
            });
            await app.viewRoomByName("Bob");

            const composer = page.getByRole("textbox", { name: "Send an unencrypted message…" });
            await composer.pressSequentially("@bob");

            // Note that we include the user ID here as the room tile is also an 'option' role
            // with text 'Bob'
            await page.getByRole("option", { name: `Bob ${bot.credentials.userId}` }).click();
            await expect(composer.getByText("Bob")).toBeVisible();
            await expect(composer).toMatchScreenshot("mention.png");
            await composer.press("Enter");
            await expect(page.locator(".mx_EventTile_body", { hasText: "Bob" })).toBeVisible();
        });

        test("renders emoji autocomplete", { tag: "@screenshot" }, async ({ page }) => {
            const composer = page.getByRole("textbox", { name: "Send an unencrypted message…" });

            // Type ":+1" to trigger emoji autocomplete
            await composer.pressSequentially(":+1");

            // Wait for autocomplete to appear
            const autocomplete = page.locator("#mx_Autocomplete");
            await expect(autocomplete).toBeVisible();

            // Take a screenshot of the autocomplete
            await expect(autocomplete).toMatchScreenshot("emoji-autocomplete.png");
        });

        test("can paste a file", async ({ page, bot, app }) => {
            // Set up a private room so we have another user to mention
            await app.client.createRoom({
                is_direct: true,
                invite: [bot.credentials.userId],
            });
            await app.viewRoomByName("Bob");
            await app.composerDragAndPasteFile("room", getSampleFilePath("riot.png"), "image/png");
            await expect(page.locator(".mx_MImageBody")).toBeVisible();
        });
    });
});
