// e2e: multi-select + bulk-delete on the Sent notes tab.
// Seeds three sent notes for a user, opens the Sent view, enters
// selection mode, picks two cards, confirms the bulk delete, and
// verifies that the count drops by two while the unselected note
// remains. Uses the same direct-DB seeding pattern as
// notes-inbox.spec.mjs so the test is hermetic.
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 createNote(userId, title, content) {
  const { rows } = await pool.query(
    `INSERT INTO notes (user_id, title, content, color, kind)
     VALUES ($1, $2, $3, 'yellow', 'text') RETURNING id, title, content, color, kind`,
    [userId, title, content],
  );
  const note = rows[0];
  createdNoteIds.push(note.id);
  return note;
}

async function addRecipient(note, senderId, recipientId) {
  await pool.query(
    `INSERT INTO note_recipients
       (note_id, sender_user_id, recipient_user_id, title, content, color, kind, status)
     VALUES ($1, $2, $3, $4, $5, $6, $7, 'unread')`,
    [note.id, senderId, recipientId, note.title, note.content, note.color, note.kind],
  );
}

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();
});

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(),
  ]);
}

test("Sent tab: select two notes, bulk-delete, count drops by two", async ({
  page,
}) => {
  const sender = await createUser("sent_bulk_sender", "Sender Bulk");
  const recipient = await createUser("sent_bulk_recipient", "Recipient Bulk");

  // Seed three sent notes (each needs at least one recipient row to be
  // returned by GET /notes?box=sent).
  const tag = uniq();
  const noteA = await createNote(sender.id, `Bulk A ${tag}`, "alpha");
  const noteB = await createNote(sender.id, `Bulk B ${tag}`, "beta");
  const noteC = await createNote(sender.id, `Bulk C ${tag}`, "gamma");
  const noteAId = noteA.id;
  const noteBId = noteB.id;
  const noteCId = noteC.id;
  await addRecipient(noteA, sender.id, recipient.id);
  await addRecipient(noteB, sender.id, recipient.id);
  await addRecipient(noteC, sender.id, recipient.id);

  await loginViaUi(page, sender.username);
  await page.goto("/notes");
  await expect(page.getByTestId("notes-page")).toBeVisible();

  // Switch to Sent tab.
  await page.getByTestId("notes-overflow-menu-trigger").click();
  await page.getByTestId("notes-overflow-sent").click();

  const cardA = page.getByTestId(`sent-note-card-${noteAId}`);
  const cardB = page.getByTestId(`sent-note-card-${noteBId}`);
  const cardC = page.getByTestId(`sent-note-card-${noteCId}`);
  await expect(cardA).toBeVisible();
  await expect(cardB).toBeVisible();
  await expect(cardC).toBeVisible();

  // Enter selection mode and pick A + B (leave C).
  await page.getByTestId("sent-bulk-select-toggle").click();
  await expect(page.getByTestId("sent-bulk-bar")).toBeVisible();
  await cardA.click();
  await cardB.click();
  await expect(page.getByTestId("sent-bulk-count")).toContainText("2");

  // Open confirm and execute the bulk delete.
  await page.getByTestId("sent-bulk-delete").click();
  await expect(page.getByTestId("sent-bulk-confirm")).toBeVisible();

  const deletedA = page.waitForResponse(
    (r) =>
      new URL(r.url()).pathname === `/api/notes/${noteAId}` &&
      r.request().method() === "DELETE" &&
      r.status() >= 200 &&
      r.status() < 300,
    { timeout: 15_000 },
  );
  const deletedB = page.waitForResponse(
    (r) =>
      new URL(r.url()).pathname === `/api/notes/${noteBId}` &&
      r.request().method() === "DELETE" &&
      r.status() >= 200 &&
      r.status() < 300,
    { timeout: 15_000 },
  );
  await page.getByTestId("sent-bulk-confirm-delete").click();
  await Promise.all([deletedA, deletedB]);

  // A and B are gone; C remains.
  await expect(cardA).toBeHidden();
  await expect(cardB).toBeHidden();
  await expect(cardC).toBeVisible();

  // Verify in the DB that A and B were actually deleted.
  const { rows } = await pool.query(
    `SELECT id FROM notes WHERE id = ANY($1::int[])`,
    [[noteAId, noteBId, noteCId]],
  );
  const remainingIds = rows.map((r) => r.id);
  expect(remainingIds).toEqual([noteCId]);
});
