import { type Page } from '@playwright/test';
import {
  expect,
  test,
  type Client
} from '../../fixtures/multi-client';
import { ChatMessagesPage } from '../../pages/chat-messages.page';
import { ChatRoomPage } from '../../pages/chat-room.page';
import { RegisterPage } from '../../pages/register.page';
import { ServerSearchPage } from '../../pages/server-search.page';

const PLUGIN_SOURCE_URL = 'http://localhost:4200/plugins/e2e-plugin-source.json';
const PLUGIN_TITLE = 'E2E All API Plugin';
const EDITED_MESSAGE = 'Plugin API edited message';
const ORIGINAL_MESSAGE = 'Plugin API original message';
const DELETED_MESSAGE = 'Plugin API deleted message';
const DELETED_MESSAGE_CONTENT = '[Message deleted]';
const PLUGIN_BOT_MESSAGE = 'Plugin bot message from all-api fixture';
const CUSTOM_EMBED_TEXT = 'E2E custom embed: Plugin API custom embed';
const SOUND_BOARD_TEXT = 'E2E soundboard ready';
const SOUND_BOARD_LABEL = 'E2E Soundboard';
const SOUND_BOARD_PLAYED_MESSAGE = 'E2E soundboard played Airhorn to voice channel';
const VOICE_CHANNEL = 'Plugin Voice';

test.describe('Plugin API multi-user runtime', () => {
  test.describe.configure({ timeout: 180_000 });

  test('runs chat, embed, soundboard, and profile APIs between two users', async ({ createClient }) => {
    const scenario = await createPluginApiScenario(createClient);

    await test.step('Alice has the server plugin active', async () => {
      await expect(soundboardComposerButton(scenario.alice.page)).toBeVisible({ timeout: 20_000 });
      await expect(scenario.alice.page.getByText(SOUND_BOARD_TEXT, { exact: true })).toBeVisible({ timeout: 20_000 });
      await expect(scenario.alice.page.getByTestId('e2e-plugin-owned-dom')).toHaveAttribute('data-plugin-owner', 'e2e.all-api-plugin');
    });

    await test.step('Activate the server plugin for Bob as the embed/soundboard receiver', async () => {
      await installGrantAndActivatePlugin(scenario.bob.page, false);
      await closeSettingsModal(scenario.bob.page);
      await expect(soundboardComposerButton(scenario.bob.page)).toBeVisible({ timeout: 20_000 });
      await expect(scenario.bob.page.getByText(SOUND_BOARD_TEXT, { exact: true })).toBeVisible({ timeout: 20_000 });
      await expect(scenario.bob.page.getByTestId('e2e-plugin-owned-dom')).toHaveAttribute('data-plugin-owner', 'e2e.all-api-plugin');
    });

    await test.step('Alice opens the plugin soundboard modal and plays a sound to voice', async () => {
      await soundboardComposerButton(scenario.alice.page).click();
      await expect(scenario.alice.page.getByRole('dialog', { name: SOUND_BOARD_LABEL })).toBeVisible({ timeout: 20_000 });
      await expect(scenario.alice.page.getByTestId('e2e-soundboard-modal')).toHaveAttribute('data-plugin-owner', 'e2e.all-api-plugin');
      await scenario.alice.page.getByRole('button', { name: 'Play airhorn to voice' }).click();
      await expect(scenario.alice.page.getByTestId('e2e-soundboard-status')).toHaveText(SOUND_BOARD_PLAYED_MESSAGE, { timeout: 20_000 });
    });

    await test.step('Bob receives messages sent and edited by Alice through the plugin API', async () => {
      await expect(scenario.bobMessages.getMessageItemByText(EDITED_MESSAGE)).toBeVisible({ timeout: 30_000 });
      await expect(scenario.bobMessages.getMessageItemByText(ORIGINAL_MESSAGE)).toHaveCount(0);
      await expect(scenario.bob.page.getByText('(edited)')).toBeVisible({ timeout: 20_000 });
    });

    await test.step('Bob sees plugin API deletion state and plugin-user messages', async () => {
      await expect(scenario.bobMessages.getMessageItemByText(DELETED_MESSAGE_CONTENT)).toBeVisible({ timeout: 30_000 });
      await expect(scenario.bobMessages.getMessageItemByText(DELETED_MESSAGE)).toHaveCount(0);
      await expect(scenario.bobMessages.getMessageItemByText(PLUGIN_BOT_MESSAGE)).toBeVisible({ timeout: 30_000 });
      await expect(scenario.bobMessages.getMessageItemByText(SOUND_BOARD_PLAYED_MESSAGE)).toBeVisible({ timeout: 30_000 });
    });

    await test.step('Bob renders Alice custom embed through the plugin embed API', async () => {
      await expect(scenario.bob.page.getByTestId('plugin-message-embeds')).toContainText(CUSTOM_EMBED_TEXT, { timeout: 30_000 });
    });

    await test.step('Bob sees Alice profile name changed by the plugin API', async () => {
      await expect(scenario.bobMessages.getMessageItemByText(EDITED_MESSAGE)).toContainText('Alice Plugin Renamed', { timeout: 30_000 });
    });
  });
});

interface PluginApiScenario {
  alice: Client;
  aliceRoom: ChatRoomPage;
  bob: Client;
  bobRoom: ChatRoomPage;
  aliceMessages: ChatMessagesPage;
  bobMessages: ChatMessagesPage;
}

async function createPluginApiScenario(createClient: () => Promise<Client>): Promise<PluginApiScenario> {
  const suffix = uniqueName('plugin-api');
  const serverName = `Plugin API Server ${suffix}`;
  const alice = await createClient();
  const bob = await createClient();

  await registerUser(alice.page, `alice_${suffix}`, 'Alice');
  await registerUser(bob.page, `bob_${suffix}`, 'Bob');

  const aliceSearch = new ServerSearchPage(alice.page);

  await aliceSearch.createServer(serverName, { description: 'Two-user plugin API E2E coverage' });
  await expect(alice.page).toHaveURL(/\/room\//, { timeout: 30_000 });

  const aliceRoom = new ChatRoomPage(alice.page);

  await aliceRoom.ensureVoiceChannelExists(VOICE_CHANNEL);
  await installGrantAndActivatePlugin(alice.page, true);
  await closeSettingsModal(alice.page);
  await expect(soundboardComposerButton(alice.page)).toBeVisible({ timeout: 20_000 });

  const bobSearch = new ServerSearchPage(bob.page);

  await bobSearch.joinServerFromSearch(serverName, { acceptPluginDownloads: true });
  await expect(bob.page).toHaveURL(/\/room\//, { timeout: 30_000 });

  const bobRoom = new ChatRoomPage(bob.page);

  await aliceRoom.joinVoiceChannel(VOICE_CHANNEL);
  await bobRoom.joinVoiceChannel(VOICE_CHANNEL);
  await expect(aliceRoom.voiceControls).toBeVisible({ timeout: 30_000 });
  await expect(bobRoom.voiceControls).toBeVisible({ timeout: 30_000 });

  const aliceMessages = new ChatMessagesPage(alice.page);
  const bobMessages = new ChatMessagesPage(bob.page);

  await aliceMessages.waitForReady();
  await bobMessages.waitForReady();
  await expect(alice.page.locator('[data-testid^="room-user-card-"]', { hasText: 'Bob' })).toBeVisible({ timeout: 30_000 });
  await expect(bob.page.locator('[data-testid^="room-user-card-"]', { hasText: 'Alice' })).toBeVisible({ timeout: 30_000 });

  return {
    alice,
    aliceRoom,
    bob,
    bobRoom,
    aliceMessages,
    bobMessages
  };
}

async function registerUser(page: Page, username: string, displayName: string): Promise<void> {
  const registerPage = new RegisterPage(page);

  await registerPage.goto();
  await registerPage.register(username, displayName, 'TestPass123!');
  await expect(page).toHaveURL(/\/search/, { timeout: 30_000 });
}

async function installGrantAndActivatePlugin(page: Page, installFromStore: boolean): Promise<void> {
  await page.getByRole('button', { name: 'Plugin Store' }).click();
  await expect(page).toHaveURL(/\/plugin-store/, { timeout: 20_000 });
  await expect(page.getByTestId('plugin-store-page')).toBeVisible({ timeout: 20_000 });

  if (installFromStore) {
    await page.getByLabel('Plugin source manifest URL').fill(PLUGIN_SOURCE_URL);
    await page.getByRole('button', { name: 'Add Source' }).click();
    await expect(page.getByRole('heading', { name: PLUGIN_TITLE })).toBeVisible({ timeout: 20_000 });
    await page.getByRole('button', { exact: true, name: /^(Install|Install to Server)$/ }).click();
    await expect(page.getByRole('dialog', { name: PLUGIN_TITLE })).toBeVisible({ timeout: 10_000 });
    await page.getByRole('button', { name: 'Install and Activate' }).click();
    await expect(page.locator('article', { hasText: PLUGIN_TITLE }).getByText('Installed')).toBeVisible({ timeout: 20_000 });
  }

  await page.getByRole('button', { name: 'Manage Plugins' }).click();
  await expect(page.getByTestId('plugin-manager')).toBeVisible({ timeout: 20_000 });
  await expect(page.locator('article', { hasText: PLUGIN_TITLE })).toBeVisible({ timeout: 20_000 });
  await page.locator('article', { hasText: PLUGIN_TITLE })
    .getByRole('button', { name: 'Select' })
    .click();

  await page.getByRole('button', { name: 'Grant all requested' }).click();
  await page.getByRole('button', { name: 'Activate ready plugins' }).click();
  await expect(page.locator('article', { hasText: PLUGIN_TITLE }).getByText('ready', { exact: true })).toBeVisible({ timeout: 30_000 });
  await page.getByRole('button', { name: 'Logs' }).click();
  await expect(page.getByText('all-api plugin completed')).toBeVisible({ timeout: 30_000 });
}

async function closeSettingsModal(page: Page): Promise<void> {
  await page.keyboard.press('Escape');
  await expect(page.getByTestId('plugin-manager')).toHaveCount(0);
}

function uniqueName(prefix: string): string {
  return `${prefix}-${Date.now()}-${Math.random().toString(36)
    .slice(2, 8)}`;
}

function soundboardComposerButton(page: Page) {
  return page.locator('app-chat-message-composer')
    .getByRole('button', { exact: true, name: SOUND_BOARD_LABEL });
}
