// Playwright e2e for the per-card and bulk delete affordances on
// /orders/incoming added in.
//
// Covers:
//   - Bulk delete: receiver selects 2 unclaimed orders via per-card
//     checkboxes, opens the confirm dialog from the sticky bulk action
//     bar, confirms, and the list shrinks by 2 (the rest stay).
//   - Per-card delete: receiver claims a third order, then deletes it
//     from the "my claimed" section using the per-card trash icon and
//     the same confirm dialog. The list shrinks by 1.
//
// Mirrors the existing order-receiver-flow.spec.mjs harness (same
// password hash, same DB cleanup pattern).
//
// Run with:
//   DATABASE_URL=... pnpm --filter @workspace/tx-os test:e2e

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 the incoming-orders delete 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 createdOrderIds = [];

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

async function createUser(prefix, roleName, displayNameEn) {
  const username = `${prefix}_${uniqueSuffix()}`;
  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,
      displayNameEn ?? username,
    ],
  );
  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 = $2`,
    [id, roleName],
  );
  return { id, username, displayName: displayNameEn ?? username };
}

async function getAvailableService() {
  const { rows } = await pool.query(
    `SELECT id, name_en AS "nameEn", name_ar AS "nameAr"
       FROM services WHERE is_available = true
      ORDER BY id ASC LIMIT 1`,
  );
  if (rows.length === 0) {
    throw new Error("No available service found in DB");
  }
  return rows[0];
}

async function placeOrderDirect(userId, serviceId, notes) {
  const { rows } = await pool.query(
    `INSERT INTO service_orders (user_id, service_id, status, notes)
     VALUES ($1, $2, 'pending', $3) RETURNING id`,
    [userId, serviceId, notes],
  );
  const id = rows[0].id;
  createdOrderIds.push(id);
  return id;
}

test.afterAll(async () => {
  if (createdOrderIds.length > 0) {
    await pool.query(`DELETE FROM service_orders WHERE id = ANY($1::int[])`, [
      createdOrderIds,
    ]);
  }
  if (createdUserIds.length > 0) {
    await pool.query(
      `DELETE FROM service_orders
        WHERE user_id = ANY($1::int[]) OR assigned_to = 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 setLanguage(page, lang) {
  await page.addInitScript((l) => {
    try {
      window.localStorage.setItem("tx-lang", l);
    } catch {
      /* ignore */
    }
  }, lang);
}

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

async function countCards(page) {
  return page.locator('[data-testid^="incoming-order-card-"]').count();
}

test.describe("Incoming orders — delete affordances", () => {
  test("bulk delete two unclaimed orders shrinks the list by 2", async ({
    browser,
  }) => {
    const requester = await createUser(
      "ord_del_req",
      "user",
      `DelReq ${uniqueSuffix()}`,
    );
    const receiver = await createUser(
      "ord_del_recv",
      "order_receiver",
      `DelRecv ${uniqueSuffix()}`,
    );
    const service = await getAvailableService();

    // Three pending unclaimed orders so we can assert the post-delete
    // count is exactly one.
    const o1 = await placeOrderDirect(requester.id, service.id, "delete-me-1");
    const o2 = await placeOrderDirect(requester.id, service.id, "delete-me-2");
    const o3 = await placeOrderDirect(requester.id, service.id, "keep-me");

    const ctx = await browser.newContext();
    try {
      const page = await ctx.newPage();
      await setLanguage(page, "en");
      await loginViaUi(page, receiver.username, TEST_PASSWORD);

      await page.goto("/orders/incoming");
      await expect(
        page.locator(`[data-testid="incoming-order-card-${o1}"]`),
      ).toBeVisible();
      await expect(
        page.locator(`[data-testid="incoming-order-card-${o2}"]`),
      ).toBeVisible();
      await expect(
        page.locator(`[data-testid="incoming-order-card-${o3}"]`),
      ).toBeVisible();

      const initial = await countCards(page);
      expect(initial).toBeGreaterThanOrEqual(3);

      // Select two via per-card checkboxes (always visible).
      await page.locator(`[data-testid="order-select-${o1}"]`).click();
      await page.locator(`[data-testid="order-select-${o2}"]`).click();

      const bar = page.locator('[data-testid="bulk-action-bar"]');
      await expect(bar).toBeVisible();
      await expect(bar).toContainText("2 orders selected");

      const bulkPromise = page.waitForResponse(
        (resp) => {
          const path = new URL(resp.url()).pathname;
          return (
            path === "/api/orders/bulk-delete" &&
            resp.request().method() === "POST"
          );
        },
        { timeout: 15_000 },
      );
      await page.locator('[data-testid="bulk-delete"]').click();
      await page.locator('[data-testid="confirm-delete"]').click();
      const bulkResp = await bulkPromise;
      expect(bulkResp.status()).toBe(200);
      const body = await bulkResp.json();
      expect(body.deletedIds.sort()).toEqual([o1, o2].sort());
      expect(body.failedIds).toEqual([]);

      await expect(
        page.locator(`[data-testid="incoming-order-card-${o1}"]`),
      ).toHaveCount(0);
      await expect(
        page.locator(`[data-testid="incoming-order-card-${o2}"]`),
      ).toHaveCount(0);
      await expect(
        page.locator(`[data-testid="incoming-order-card-${o3}"]`),
      ).toBeVisible();

      const after = await countCards(page);
      expect(after).toBe(initial - 2);
    } finally {
      await ctx.close();
    }
  });

  test("per-card trash deletes a single claimed order", async ({ browser }) => {
    const requester = await createUser(
      "ord_del_req2",
      "user",
      `DelReq2 ${uniqueSuffix()}`,
    );
    const receiver = await createUser(
      "ord_del_recv2",
      "order_receiver",
      `DelRecv2 ${uniqueSuffix()}`,
    );
    const service = await getAvailableService();

    const o1 = await placeOrderDirect(requester.id, service.id, "claim-me");

    const ctx = await browser.newContext();
    try {
      const page = await ctx.newPage();
      await setLanguage(page, "en");
      await loginViaUi(page, receiver.username, TEST_PASSWORD);

      await page.goto("/orders/incoming");
      const card = page.locator(
        `[data-testid="incoming-order-card-${o1}"]`,
      );
      await expect(card).toBeVisible();

      // Claim the order so it moves to "My active orders".
      const claimPromise = page.waitForResponse(
        (resp) => {
          const path = new URL(resp.url()).pathname;
          return (
            path === `/api/orders/${o1}/confirm-receipt` &&
            resp.request().method() === "PATCH"
          );
        },
        { timeout: 15_000 },
      );
      await card.getByRole("button", { name: "Confirm Receipt", exact: true }).click();
      const claimResp = await claimPromise;
      expect(claimResp.status()).toBe(200);

      // Now delete via per-card trash.
      const before = await countCards(page);
      const deletePromise = page.waitForResponse(
        (resp) => {
          const path = new URL(resp.url()).pathname;
          return (
            path === `/api/orders/${o1}` &&
            resp.request().method() === "DELETE"
          );
        },
        { timeout: 15_000 },
      );
      await page.locator(`[data-testid="order-delete-${o1}"]`).click();
      await page.locator('[data-testid="confirm-delete"]').click();
      const delResp = await deletePromise;
      expect(delResp.status()).toBe(204);

      await expect(card).toHaveCount(0);
      const after = await countCards(page);
      expect(after).toBe(before - 1);
    } finally {
      await ctx.close();
    }
  });
});
