/*
Copyright 2024 New Vector Ltd.
Copyright 2023 Suguru Hirahara

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 { viewRoomSummaryByName } from "./utils";
import { isDendrite } from "../../plugins/homeserver/dendrite";
import { getSampleFilePath } from "../../sample-files";
import type { ElementAppPage } from "../../pages/ElementAppPage";

const ROOM_NAME = "Test room";
const NAME = "Alice";

async function uploadFile(app: ElementAppPage, sampleFile: string) {
    // Upload a file from the message composer
    await app.composerUploadFiles("room", getSampleFilePath(sampleFile));
    // Wait until the file is sent
    await expect(app.page.locator(".mx_RoomView_statusArea_expanded")).not.toBeVisible();
    await expect(app.page.locator(".mx_EventTile.mx_EventTile_last").getByRole("status")).toHaveAccessibleName(
        "Your message was sent",
    );
}

test.describe("FilePanel", () => {
    test.use({
        displayName: NAME,
    });

    test.beforeEach(async ({ page, user, app }) => {
        await app.client.createRoom({ name: ROOM_NAME });

        // Open the file panel
        await viewRoomSummaryByName(page, app, ROOM_NAME);
        await page.getByRole("menuitem", { name: "Files" }).click();
        await expect(page.locator(".mx_FilePanel")).toBeVisible();
    });

    test.describe("render", { tag: ["@no-firefox", "@no-webkit"] }, () => {
        test("should render empty state", { tag: "@screenshot" }, async ({ page }) => {
            // Wait until the information about the empty state is rendered
            await expect(page.locator(".mx_EmptyState")).toBeVisible();

            // Take a snapshot of RightPanel - fix https://github.com/vector-im/element-web/issues/25332
            await expect(page.locator(".mx_RightPanel")).toMatchScreenshot("empty.png");
        });

        test("should list tiles on the panel", { tag: "@screenshot" }, async ({ page, app }) => {
            // Upload multiple files
            await uploadFile(app, "riot.png"); // Image
            await uploadFile(app, "1sec.ogg"); // Audio
            await uploadFile(app, "matrix-org-client-versions.json"); // JSON

            const roomViewBody = page.locator(".mx_RoomView_body");
            // Assert that all of the file were uploaded and rendered
            await expect(roomViewBody.locator(".mx_EventTile[data-layout='group']")).toHaveCount(3);

            // Assert that the image exists and has the alt string
            await expect(roomViewBody.locator(".mx_EventTile[data-layout='group'] img[alt='riot.png']")).toBeVisible();

            // Assert that the audio player is rendered
            await expect(roomViewBody.getByRole("region", { name: "Audio player" })).toBeVisible();

            // Assert that the file button exists
            await expect(
                roomViewBody.locator(".mx_EventTile_last[data-layout='group'] .mx_MFileBody", { hasText: ".json" }),
            ).toBeVisible();

            const filePanel = page.locator(".mx_FilePanel");
            // Assert that the file panel is opened inside mx_RightPanel and visible
            await expect(filePanel).toBeVisible();

            const filePanelMessageList = filePanel.locator(".mx_RoomView_MessageList");

            // Assert that data-layout attribute is not applied to file tiles on the panel
            await expect(filePanelMessageList.locator(".mx_EventTile[data-layout]")).not.toBeVisible();

            // Assert that all of the file tiles are rendered
            await expect(filePanelMessageList.locator(".mx_EventTile")).toHaveCount(3);

            // Assert that the download links are rendered
            await expect(filePanelMessageList.locator(".mx_MFileBody")).toHaveCount(3);

            // Assert that the sender of the files is rendered on all of the tiles
            await expect(filePanelMessageList.getByText(NAME)).toHaveCount(3);

            // Detect the image file
            const image = filePanelMessageList.locator(".mx_EventTile_mediaLine.mx_EventTile_image .mx_MImageBody");
            // Assert that the image is specified as thumbnail and has the alt string
            await expect(image.locator("img[class='mx_MImageBody_thumbnail']")).toBeVisible();
            await expect(image.locator("img[alt='riot.png']")).toBeVisible();

            // Detect the audio file
            const audio = filePanelMessageList.getByRole("region", { name: "Audio player" });
            // Assert that the play button is rendered
            await expect(audio.getByRole("button", { name: "Play" })).toBeVisible();

            // Detect the JSON file
            // Assert that the tile is rendered as a button
            const file = filePanelMessageList.locator(".mx_EventTile_mediaLine .mx_MFileBody [role='button']");
            // Assert that the file name is rendered inside the button with ellipsis
            await expect(file.getByText(/matrix.*?\.json/)).toBeVisible();

            // Make the viewport tall enough to display all of the file tiles on FilePanel
            await page.setViewportSize({ width: 800, height: 1000 });

            // In case the panel is scrollable on the resized viewport
            // Assert that the value for flexbox is applied
            await expect(filePanel.locator(".mx_ScrollPanel .mx_RoomView_MessageList")).toHaveCSS(
                "justify-content",
                "flex-end",
            );
            // Assert that all of the file tiles are visible before taking a snapshot
            await expect(filePanelMessageList.locator(".mx_MImageBody")).toBeVisible(); // top
            await expect(filePanelMessageList.locator(".mx_MAudioBody")).toBeVisible(); // middle
            const senderDetails = filePanelMessageList.locator(".mx_EventTile_last .mx_EventTile_senderDetails");
            await expect(senderDetails.locator(".mx_DisambiguatedProfile")).toBeVisible();
            await expect(senderDetails.locator(".mx_MessageTimestamp")).toBeVisible();

            // Take a snapshot of file tiles list on FilePanel
            await expect(filePanelMessageList).toMatchScreenshot("file-tiles-list.png", {
                // Exclude timestamps & flaky seek bar from snapshot
                mask: [page.getByTestId("audio-player-seek")],
                css: `
                    .mx_MessageTimestamp {
                        visibility: hidden;
                    }
                `,
            });
        });

        test("should render the audio player and play the audio file on the panel", async ({ page, app }) => {
            // Upload an image file
            await uploadFile(app, "1sec.ogg");

            const audioBody = page.getByTestId("right-panel").getByRole("region", { name: "Audio player" });

            // Assert that the audio player is rendered
            // Assert that the audio file information is rendered;
            await expect(audioBody.getByText("1sec.ogg")).toBeVisible(); // extension
            await expect(audioBody.getByRole("time")).toHaveText("00:01"); // duration
            await expect(audioBody.getByText("(3.56 KB)")).toBeVisible(); // actual size;

            // Assert that the duration counter is 00:01 before clicking the play button
            await expect(audioBody.getByRole("time")).toHaveText("00:01");

            // Assert that the counter is zero before clicking the play button
            await expect(audioBody.getByRole("timer")).toHaveText("00:00");

            // Click the play button
            await audioBody.getByRole("button", { name: "Play" }).click();

            // Assert that the pause button is rendered
            await expect(audioBody.getByRole("button", { name: "Pause" })).toBeVisible();

            // Assert that the timer is reset when the audio file finished playing
            await expect(audioBody.getByRole("timer")).toHaveText("00:00");

            // Assert that the play button is rendered
            await expect(audioBody.getByRole("button", { name: "Play" })).toBeVisible();
        });

        test("should render file size in kibibytes on a file tile", async ({ page, app }) => {
            const size = "1.12 KB"; // actual file size in kibibytes (1024 bytes)

            // Upload a file
            await uploadFile(app, "matrix-org-client-versions.json");

            const tile = page.locator(".mx_FilePanel .mx_EventTile");
            // Assert that the file size is displayed in kibibytes, not kilobytes (1000 bytes)
            // See: https://github.com/vector-im/element-web/issues/24866
            await expect(tile.locator(".mx_MFileBody [data-type='info']", { hasText: size })).toBeVisible();
        });
    });

    test.describe("download", () => {
        test.skip(isDendrite, "due to a Dendrite sending Content-Disposition inline");

        test("should download an image via the link on the panel", async ({ page, app, context }) => {
            // Upload an image file
            await uploadFile(app, "riot.png");

            // Detect the image file on the panel
            const imageBody = page.locator(
                ".mx_FilePanel .mx_RoomView_MessageList .mx_EventTile_mediaLine.mx_EventTile_image .mx_MImageBody",
            );

            const link = imageBody.locator(".mx_MFileBody a");

            const downloadPromise = page.waitForEvent("download");

            // Click the anchor link (not the image itself)
            await link.click();

            const download = await downloadPromise;
            expect(download.suggestedFilename()).toBe("riot.png");
        });
    });
});
