// Send-note confirm dialog appears only when 2+ recipients are picked.
// One recipient sends directly; two or more still gate behind the
// AlertDialog confirmation.
import { test, expect } from "@playwright/test";
import pg from "pg";

const DATABASE_URL = process.env.DATABASE_URL;
if (!DATABASE_URL) {
  throw new Error("DATABASE_URL must be set to run this UI test");
}

const TEST_PASSWORD = "TestPass123!";
const TEST_PASSWORD_HASH =
  "$2b$10$Bs636ukPMyz01nKrsi.5m.JlDXSN22AVCvn8cgPWWDbo5yJRQX2vu";

const pool = new pg.Pool({ connectionString: DATABASE_URL });
const createdUserIds = [];
const createdNoteIds = [];

function uniq() {
  return `${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
}

async function createUser(prefix, displayEn) {
  const username = `${prefix}_${uniq()}`;
  const { rows } = await pool.query(
    `INSERT INTO users (username, email, password_hash, display_name_en, preferred_language, is_active)
     VALUES ($1, $2, $3, $4, 'en', true) RETURNING id`,
    [username, `${username}@example.com`, TEST_PASSWORD_HASH, displayEn],
  );
  const id = rows[0].id;
  createdUserIds.push(id);
  await pool.query(
    `INSERT INTO user_roles (user_id, role_id)
       SELECT $1, id FROM roles WHERE name = 'user'`,
    [id],
  );
  return { id, username, displayEn };
}

async function loginViaUi(page, username) {
  await page.goto("/login");
  await page.locator("#username").fill(username);
  await page.locator("#password").fill(TEST_PASSWORD);
  await Promise.all([
    page.waitForURL((url) => !url.pathname.endsWith("/login"), {
      timeout: 15_000,
    }),
    page.locator('form button[type="submit"]').click(),
  ]);
}

async function composeNote(page, content) {
  await page.goto("/notes");
  await expect(page.getByTestId("notes-page")).toBeVisible();
  await page.getByTestId("notes-composer-open").click();
  await page.getByTestId("notes-composer-content").fill(content);
  const createPromise = page.waitForResponse(
    (r) =>
      new URL(r.url()).pathname === "/api/notes" &&
      r.request().method() === "POST" &&
      r.ok(),
    { timeout: 15_000 },
  );
  await page.getByTestId("notes-composer-save").click();
  const created = await createPromise;
  const note = await created.json();
  createdNoteIds.push(note.id);
  return note;
}

async function openSendDialog(page, noteId) {
  const card = page.getByTestId(`note-card-${noteId}`);
  await expect(card).toBeVisible();
  await card.hover();
  await page.getByTestId(`note-card-send-${noteId}`).click();
  await expect(page.getByTestId("send-note-dialog")).toBeVisible();
}

async function pickRecipient(page, recipient) {
  await page.getByTestId("send-note-search").fill("");
  await page.getByTestId("send-note-search").fill(recipient.displayEn);
  await page.getByTestId(`send-recipient-option-${recipient.id}`).click();
}

test.afterAll(async () => {
  if (createdNoteIds.length > 0) {
    await pool.query(
      `DELETE FROM note_replies WHERE note_id = ANY($1::int[])`,
      [createdNoteIds],
    );
    await pool.query(
      `DELETE FROM note_recipients WHERE note_id = ANY($1::int[])`,
      [createdNoteIds],
    );
    await pool.query(`DELETE FROM notes WHERE id = ANY($1::int[])`, [
      createdNoteIds,
    ]);
  }
  if (createdUserIds.length > 0) {
    await pool.query(`DELETE FROM notes WHERE user_id = ANY($1::int[])`, [
      createdUserIds,
    ]);
    await pool.query(
      `DELETE FROM notifications WHERE user_id = ANY($1::int[])`,
      [createdUserIds],
    );
    await pool.query(`DELETE FROM user_roles WHERE user_id = ANY($1::int[])`, [
      createdUserIds,
    ]);
    await pool.query(`DELETE FROM users WHERE id = ANY($1::int[])`, [
      createdUserIds,
    ]);
  }
  await pool.end();
});

test("single-recipient send skips the confirm dialog", async ({ page }) => {
  const sender = await createUser("notes_send1_sender", "Single Sender");
  const recipient = await createUser("notes_send1_recipient", "Solo Recipient");

  await loginViaUi(page, sender.username);
  const note = await composeNote(page, `Solo send ${uniq()}`);
  await openSendDialog(page, note.id);
  await pickRecipient(page, recipient);

  const sendPromise = page.waitForResponse(
    (r) =>
      new URL(r.url()).pathname === `/api/notes/${note.id}/send` &&
      r.request().method() === "POST" &&
      r.ok(),
    { timeout: 15_000 },
  );
  await page.getByTestId("send-note-submit").click();
  await sendPromise;

  await expect(page.getByTestId("send-note-confirm")).toBeHidden();
  await expect(page.getByTestId("send-note-dialog")).toBeHidden();
});

test("multi-recipient send still requires the confirm dialog", async ({
  page,
}) => {
  const sender = await createUser("notes_send2_sender", "Multi Sender");
  const r1 = await createUser("notes_send2_r1", "Recipient One");
  const r2 = await createUser("notes_send2_r2", "Recipient Two");

  await loginViaUi(page, sender.username);
  const note = await composeNote(page, `Group send ${uniq()}`);
  await openSendDialog(page, note.id);
  await pickRecipient(page, r1);
  await pickRecipient(page, r2);

  // Track every /send request so we can assert none fired before
  // the user explicitly confirmed the multi-recipient send.
  const preConfirmSendRequests = [];
  const sendPath = `/api/notes/${note.id}/send`;
  const requestListener = (req) => {
    if (
      new URL(req.url()).pathname === sendPath &&
      req.method() === "POST"
    ) {
      preConfirmSendRequests.push(req.url());
    }
  };
  page.on("request", requestListener);

  await page.getByTestId("send-note-submit").click();
  // Two+ recipients → confirm dialog must appear; no /send call yet.
  const confirm = page.getByTestId("send-note-confirm");
  await expect(confirm).toBeVisible();
  // The confirm body should mention the recipient count (2 here) so
  // users see what they're about to send to.
  await expect(confirm).toContainText("2");
  // Give the network a beat to prove no send fired pre-confirmation.
  await page.waitForTimeout(250);
  expect(preConfirmSendRequests).toEqual([]);
  page.off("request", requestListener);

  const sendPromise = page.waitForResponse(
    (r) =>
      new URL(r.url()).pathname === `/api/notes/${note.id}/send` &&
      r.request().method() === "POST" &&
      r.ok(),
    { timeout: 15_000 },
  );
  await page.getByTestId("send-note-confirm-submit").click();
  await sendPromise;
  await expect(page.getByTestId("send-note-dialog")).toBeHidden();
});
