import { Page, expect } from "@playwright/test";

export interface WaitForReplyOptions {
  /** Timeout en ms para esperar la respuesta (default: 30s) */
  timeout?: number;
  /** Filtrar por sender display name si se especifica */
  sender?: string;
}

/**
 * Navega a un room por nombre usando el buscador de Element.
 */
export async function goToRoom(page: Page, roomName: string) {
  // Abrir busqueda de rooms (Ctrl+K o click en el search)
  const searchButton = page.locator('[aria-label="Search"]').first();
  await searchButton.click();

  // Escribir nombre del room en el campo de busqueda
  const searchInput = page.getByRole("searchbox").first();
  await searchInput.fill(roomName);

  // Seleccionar el room de los resultados
  const roomResult = page
    .getByRole("option", { name: new RegExp(roomName, "i") })
    .first();
  await roomResult.click();

  // Verificar que estamos en el room correcto (header muestra el nombre)
  await expect(
    page.locator(`[data-testid="room-header-name"], h2`).filter({ hasText: roomName }).first()
  ).toBeVisible({ timeout: 10_000 });
}

/**
 * Envia un mensaje de texto en el room actual.
 */
export async function sendMessage(page: Page, text: string) {
  const composer = page.getByRole("textbox", { name: /message/i });
  await composer.fill(text);
  await composer.press("Enter");

  // Esperar a que el mensaje aparezca en el timeline
  await expect(
    page.locator(".mx_EventTile_body, .mx_MTextBody").filter({ hasText: text }).last()
  ).toBeVisible({ timeout: 10_000 });
}

/**
 * Espera una respuesta de un bot en el timeline.
 * Detecta "Unable to decrypt" y falla con mensaje claro.
 */
export async function waitForBotReply(
  page: Page,
  options?: WaitForReplyOptions
): Promise<string> {
  const timeout = options?.timeout ?? 30_000;

  // Esperar a que aparezca un nuevo mensaje (que no sea del usuario actual)
  // Los mensajes de bots tienen un sender distinto al del usuario autenticado
  const startTime = Date.now();

  while (Date.now() - startTime < timeout) {
    // Detectar mensajes que no se pueden descifrar
    const undecryptable = page.locator(
      '.mx_DecryptionFailureBody, [class*="UnableToDecrypt"]'
    );
    const undecryptableCount = await undecryptable.count();
    if (undecryptableCount > 0) {
      throw new Error(
        "E2EE error: se detectaron mensajes 'Unable to decrypt'. " +
          "Verificar que cross-signing esta configurado correctamente " +
          "y que la recovery key es valida."
      );
    }

    // Buscar el ultimo mensaje en el timeline
    const lastMessage = await getLastMessage(page);
    if (lastMessage) {
      // Si se especifica sender, verificar que coincide
      if (options?.sender) {
        const lastSender = await getLastMessageSender(page);
        if (lastSender && lastSender.includes(options.sender)) {
          return lastMessage;
        }
      } else {
        // Sin filtro de sender, cualquier mensaje nuevo sirve
        return lastMessage;
      }
    }

    await page.waitForTimeout(500);
  }

  throw new Error(
    `Timeout (${timeout}ms): no se recibio respuesta del bot` +
      (options?.sender ? ` (sender esperado: ${options.sender})` : "")
  );
}

/**
 * Obtiene el texto del ultimo mensaje visible en el timeline.
 */
export async function getLastMessage(page: Page): Promise<string | null> {
  const messages = page.locator(".mx_EventTile_body, .mx_MTextBody");
  const count = await messages.count();
  if (count === 0) return null;
  return messages.last().textContent();
}

/**
 * Obtiene el display name del sender del ultimo mensaje.
 */
async function getLastMessageSender(page: Page): Promise<string | null> {
  const senders = page.locator(
    ".mx_DisambiguatedProfile_displayName, .mx_SenderProfile_name"
  );
  const count = await senders.count();
  if (count === 0) return null;
  return senders.last().textContent();
}

/**
 * Verifica que no hay mensajes "Unable to decrypt" en el timeline visible.
 * Lanza error descriptivo si los encuentra.
 */
export async function assertNoDecryptionErrors(page: Page) {
  const undecryptable = page.locator(
    '.mx_DecryptionFailureBody, [class*="UnableToDecrypt"]'
  );
  const texts = await undecryptable.allTextContents();
  if (texts.length > 0) {
    throw new Error(
      `E2EE error: ${texts.length} mensaje(s) no pudieron descifrarse. ` +
        "Verificar cross-signing y recovery key."
    );
  }
}
