import { test, expect } from '../../fixtures/multi-client';
import {
  MULTI_DEVICE_VOICE_CHANNEL,
  channelsSidePanel,
  createMultiDeviceScenario,
  expectCrossDeviceMessage,
  expectActiveVoiceOnDevice,
  expectPassiveVoiceOnDevice,
  logoutFromMenu,
  membersSidePanel,
  passiveVoiceChannelJoinBadge,
  readClientInstanceId,
  uniqueMultiDeviceName
} from '../../helpers/multi-device-session';

test.describe('Multi-device session', () => {
  test.describe.configure({ timeout: 300_000, retries: 1 });

  test('covers identity, chat sync, typing exclusion, and voice exclusivity', async ({ createClient }) => {
    const scenario = await createMultiDeviceScenario(createClient);
    const messageAtoB = `Cross-device A to B ${uniqueMultiDeviceName('msg')}`;
    const messageBtoA = `Cross-device B to A ${uniqueMultiDeviceName('msg')}`;
    const typingDraft = `Typing draft ${uniqueMultiDeviceName('draft')}`;

    await test.step('assigns distinct clientInstanceId per browser context', async () => {
      const instanceA = await readClientInstanceId(scenario.clientA.page);
      const instanceB = await readClientInstanceId(scenario.clientB.page);

      expect(instanceA).toBeTruthy();
      expect(instanceB).toBeTruthy();
      expect(instanceA).not.toEqual(instanceB);
    });

    await test.step('syncs chat from device A to device B', async () => {
      await expectCrossDeviceMessage(scenario.messagesA, scenario.messagesB, messageAtoB);
    });

    await test.step('syncs chat from device B to device A', async () => {
      await expectCrossDeviceMessage(scenario.messagesB, scenario.messagesA, messageBtoA);
    });

    await test.step('does not show own typing indicator on the other device for the same user', async () => {
      await scenario.messagesA.typeDraftWithTypingEvents(typingDraft);

      await expect(
        scenario.clientB.page.getByText(`${scenario.credentials.displayName} is typing`, { exact: false })
      ).toHaveCount(0, { timeout: 5_000 });
    });

    await test.step('shows passive in-voice UI on the second device when the first joins voice', async () => {
      await scenario.roomA.joinVoiceChannel(MULTI_DEVICE_VOICE_CHANNEL);
      await expectActiveVoiceOnDevice(scenario.clientA.page);

      await expectPassiveVoiceOnDevice(scenario.clientB.page, {
        displayName: scenario.credentials.displayName
      });

      await expect(
        membersSidePanel(scenario.clientB.page).getByText('In voice on another device', { exact: false })
      ).toBeVisible({ timeout: 20_000 });

      await expect(
        channelsSidePanel(scenario.clientB.page).locator('.opacity-50')
          .filter({
            hasText: scenario.credentials.displayName
          })
          .first()
      ).toBeVisible({ timeout: 20_000 });
    });

    await test.step('shows Join takeover affordance on passive device voice channel', async () => {
      await expect(passiveVoiceChannelJoinBadge(scenario.clientB.page)).toBeVisible({ timeout: 20_000 });
    });

    await test.step('transfers voice ownership when the passive device takes over', async () => {
      await scenario.roomB.joinVoiceChannel(MULTI_DEVICE_VOICE_CHANNEL);
      await expectActiveVoiceOnDevice(scenario.clientB.page);

      await expectPassiveVoiceOnDevice(scenario.clientA.page, {
        displayName: scenario.credentials.displayName
      });
    });

    await test.step('keeps the second device logged in when the first device logs out', async () => {
      const message = `Still logged in ${uniqueMultiDeviceName('logout')}`;

      await logoutFromMenu(scenario.clientA.page);

      await scenario.messagesB.sendMessage(message);
      await expect(scenario.messagesB.getMessageItemByText(message)).toBeVisible({ timeout: 20_000 });
      await expect(scenario.clientB.page).toHaveURL(/\/room\//, { timeout: 10_000 });
    });
  });
});
