/*
Copyright 2026 Element Creations 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 { renderHook, waitFor } from "jest-matrix-react";
import React from "react";

import { PlatformCallType, useRoomCall } from "../../../src/hooks/room/useRoomCall";
import {
    getMockClientWithEventEmitter,
    mkRoom,
    mockClientMethodsRooms,
    mockClientMethodsServer,
    mockClientMethodsUser,
    MockEventEmitter,
    setupAsyncStoreWithClient,
} from "../../test-utils";
import { ScopedRoomContextProvider } from "../../../src/contexts/ScopedRoomContext";
import RoomContext, { type RoomContextType } from "../../../src/contexts/RoomContext";
import { MatrixClientContextProvider } from "../../../src/components/structures/MatrixClientContextProvider";
import type LegacyCallHandler from "../../../src/LegacyCallHandler";
import { SdkContextClass } from "../../../src/contexts/SDKContext";
import SettingsStore from "../../../src/settings/SettingsStore";
import { CallStore } from "../../../src/stores/CallStore";

describe("useRoomCall", () => {
    const client = getMockClientWithEventEmitter({
        ...mockClientMethodsUser(),
        ...mockClientMethodsServer(),
        ...mockClientMethodsRooms(),
        matrixRTC: new MockEventEmitter(),
        _unstable_getRTCTransports: jest.fn().mockResolvedValue([]),
        getCrypto: () => null,
    });
    const room = mkRoom(client, "!test-room");
    // Create a stable room context for this test
    const mockRoomViewStore = {
        isViewingCall: jest.fn().mockReturnValue(false),
        on: jest.fn(),
        off: jest.fn(),
        emit: jest.fn(),
    };

    const roomContext = {
        ...RoomContext,
        roomId: room.roomId,
        roomViewStore: mockRoomViewStore,
    } as unknown as RoomContextType;

    beforeEach(() => {
        const callHandler = {
            getCallForRoom: jest.fn().mockReturnValue(null),
            isCallSidebarShown: jest.fn().mockReturnValue(true),
            addListener: jest.fn(),
            removeListener: jest.fn(),
            on: jest.fn(),
            off: jest.fn(),
        };
        jest.spyOn(SdkContextClass.instance, "legacyCallHandler", "get").mockReturnValue(
            callHandler as unknown as LegacyCallHandler,
        );
        const origGetValue = SettingsStore.getValue;
        jest.spyOn(SettingsStore, "getValue").mockImplementation((name, ...params): any => {
            if (name === "feature_group_calls") return true;
            return origGetValue(name, ...params);
        });
    });

    afterEach(() => {
        jest.restoreAllMocks();
    });

    function render() {
        return renderHook(() => useRoomCall(room), {
            wrapper: ({ children }) => (
                <MatrixClientContextProvider client={client}>
                    <ScopedRoomContextProvider {...roomContext}>{children}</ScopedRoomContextProvider>
                </MatrixClientContextProvider>
            ),
        });
    }

    describe("Element Call focus detection", () => {
        it("Blocks Element Call if required foci are not configured", async () => {
            await setupAsyncStoreWithClient(CallStore.instance, client);
            const { result } = render();
            await waitFor(() => expect(result.current.callOptions).toEqual([PlatformCallType.LegacyCall]));
        });
        it("Blocks Element Call if transport foci are the wrong type", async () => {
            client._unstable_getRTCTransports.mockResolvedValue([{ type: "anything-else" }]);
            await setupAsyncStoreWithClient(CallStore.instance, client);
            const { result } = render();
            await waitFor(() => expect(result.current.callOptions).toEqual([PlatformCallType.LegacyCall]));
        });
        it("Blocks Element Call if well-known foci are the wrong type", async () => {
            client.getClientWellKnown.mockReturnValue({
                "org.matrix.msc4143.rtc_foci": {
                    type: "anything-else",
                },
            });
            await setupAsyncStoreWithClient(CallStore.instance, client);
            const { result } = render();
            await waitFor(() => expect(result.current.callOptions).toEqual([PlatformCallType.LegacyCall]));
        });
        it("Allows Element Call if foci is provided via getRTCTransports", async () => {
            client._unstable_getRTCTransports.mockResolvedValue([
                { type: "livekit", livekit_service_url: "https://example.org" },
            ]);
            await setupAsyncStoreWithClient(CallStore.instance, client);

            const { result } = render();
            await waitFor(() =>
                expect(result.current.callOptions).toEqual([PlatformCallType.ElementCall, PlatformCallType.LegacyCall]),
            );
        });
        it("Allows Element Call if foci is provided via .well-known", async () => {
            client.getClientWellKnown.mockReturnValue({
                "org.matrix.msc4143.rtc_foci": {
                    type: "livekit",
                    livekit_service_url: "https://example.org",
                },
            });
            await setupAsyncStoreWithClient(CallStore.instance, client);
            const { result } = render();
            await waitFor(() =>
                expect(result.current.callOptions).toEqual([PlatformCallType.ElementCall, PlatformCallType.LegacyCall]),
            );
        });
        it("Ensure handler reacts to transport changes", async () => {
            // Clear all transports
            client._unstable_getRTCTransports.mockResolvedValue([]);
            client.getClientWellKnown.mockReturnValue({});

            await setupAsyncStoreWithClient(CallStore.instance, client);
            const { result } = render();

            // Ensure Element Call is not a call option.
            expect(result.current.callOptions).toEqual([PlatformCallType.LegacyCall]);

            // Now enable a transport and ensure that useRoomCall picks it up reactively.
            client._unstable_getRTCTransports.mockResolvedValue([
                { type: "livekit", livekit_service_url: "https://example.org" },
            ]);
            await setupAsyncStoreWithClient(CallStore.instance, client);
            await waitFor(() =>
                expect(result.current.callOptions).toEqual([PlatformCallType.ElementCall, PlatformCallType.LegacyCall]),
            );
        });
    });
});
