/*
 * 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 React from "react";
import { mocked } from "jest-mock";
import { render, screen } from "jest-matrix-react";
import userEvent from "@testing-library/user-event";

import {
    type RoomListItemMenuViewState,
    useRoomListItemMenuViewModel,
} from "../../../../../../src/components/viewmodels/roomlist/RoomListItemMenuViewModel";
import type { MatrixClient, Room } from "matrix-js-sdk/src/matrix";
import { mkRoom, stubClient } from "../../../../../test-utils";
import { RoomListItemMenuView } from "../../../../../../src/components/views/rooms/RoomListPanel/RoomListItemMenuView";
import { RoomNotifState } from "../../../../../../src/RoomNotifs";

jest.mock("../../../../../../src/components/viewmodels/roomlist/RoomListItemMenuViewModel", () => ({
    useRoomListItemMenuViewModel: jest.fn(),
}));

describe("<RoomListItemMenuView />", () => {
    const defaultValue: RoomListItemMenuViewState = {
        showMoreOptionsMenu: true,
        showNotificationMenu: true,
        isFavourite: true,
        isLowPriority: true,
        canInvite: true,
        canMarkAsUnread: true,
        canMarkAsRead: true,
        canCopyRoomLink: true,
        isNotificationAllMessage: true,
        isNotificationMentionOnly: true,
        isNotificationAllMessageLoud: true,
        isNotificationMute: true,
        copyRoomLink: jest.fn(),
        markAsUnread: jest.fn(),
        markAsRead: jest.fn(),
        leaveRoom: jest.fn(),
        toggleLowPriority: jest.fn(),
        toggleFavorite: jest.fn(),
        invite: jest.fn(),
        setRoomNotifState: jest.fn(),
    };

    let matrixClient: MatrixClient;
    let room: Room;

    beforeEach(() => {
        mocked(useRoomListItemMenuViewModel).mockReturnValue(defaultValue);
        matrixClient = stubClient();
        room = mkRoom(matrixClient, "room1");
    });

    function renderMenu() {
        return render(<RoomListItemMenuView room={room} />);
    }

    it("should render the more options menu", () => {
        const { asFragment } = renderMenu();
        expect(screen.getByRole("button", { name: "More Options" })).toBeInTheDocument();
        expect(asFragment()).toMatchSnapshot();
    });

    it("should render the notification options menu", () => {
        const { asFragment } = renderMenu();
        expect(screen.getByRole("button", { name: "Notification options" })).toBeInTheDocument();
        expect(asFragment()).toMatchSnapshot();
    });

    it("should not render the more options menu when showMoreOptionsMenu is false", () => {
        mocked(useRoomListItemMenuViewModel).mockReturnValue({ ...defaultValue, showMoreOptionsMenu: false });
        renderMenu();
        expect(screen.queryByRole("button", { name: "More Options" })).toBeNull();
    });

    it("should not render the notification options menu when showNotificationMenu is false", () => {
        mocked(useRoomListItemMenuViewModel).mockReturnValue({ ...defaultValue, showNotificationMenu: false });
        renderMenu();
        expect(screen.queryByRole("button", { name: "Notification options" })).toBeNull();
    });

    it("should display all the buttons and have the actions linked for the more options menu", async () => {
        const user = userEvent.setup();
        renderMenu();

        const openMenu = screen.getByRole("button", { name: "More Options" });
        await user.click(openMenu);

        await user.click(screen.getByRole("menuitem", { name: "Mark as read" }));
        expect(defaultValue.markAsRead).toHaveBeenCalled();

        await user.click(openMenu);
        await user.click(screen.getByRole("menuitem", { name: "Mark as unread" }));
        expect(defaultValue.markAsUnread).toHaveBeenCalled();

        await user.click(openMenu);
        await user.click(screen.getByRole("menuitemcheckbox", { name: "Favourited" }));
        expect(defaultValue.toggleFavorite).toHaveBeenCalled();

        await user.click(openMenu);
        await user.click(screen.getByRole("menuitemcheckbox", { name: "Low priority" }));
        expect(defaultValue.toggleLowPriority).toHaveBeenCalled();

        await user.click(openMenu);
        await user.click(screen.getByRole("menuitem", { name: "Invite" }));
        expect(defaultValue.invite).toHaveBeenCalled();

        await user.click(openMenu);
        await user.click(screen.getByRole("menuitem", { name: "Copy room link" }));
        expect(defaultValue.copyRoomLink).toHaveBeenCalled();

        await user.click(openMenu);
        await user.click(screen.getByRole("menuitem", { name: "Leave room" }));
        expect(defaultValue.leaveRoom).toHaveBeenCalled();
    });

    it("should display all the buttons and have the actions linked for the notification options menu", async () => {
        const user = userEvent.setup();
        renderMenu();

        const openMenu = screen.getByRole("button", { name: "Notification options" });
        await user.click(openMenu);

        await user.click(screen.getByRole("menuitem", { name: "Match default settings" }));
        expect(defaultValue.setRoomNotifState).toHaveBeenCalledWith(RoomNotifState.AllMessages);

        await user.click(openMenu);
        await user.click(screen.getByRole("menuitem", { name: "All messages" }));
        expect(defaultValue.setRoomNotifState).toHaveBeenCalledWith(RoomNotifState.AllMessagesLoud);

        await user.click(openMenu);
        await user.click(screen.getByRole("menuitem", { name: "Mentions and keywords" }));
        expect(defaultValue.setRoomNotifState).toHaveBeenCalledWith(RoomNotifState.MentionsOnly);

        await user.click(openMenu);
        await user.click(screen.getByRole("menuitem", { name: "Mute room" }));
        expect(defaultValue.setRoomNotifState).toHaveBeenCalledWith(RoomNotifState.Mute);
    });
});
