// #499: e2e for the Quick Actions dialog header layout. The Radix
// DialogContent renders a Close (X) button at physical `right-4 top-4`
// inside the dialog regardless of `dir`. Before #499 the
// DialogHeader had no trailing padding, so in Arabic (RTL) the
// `text-right`-aligned title "إجراءات سريعة" sat directly under the
// X — a visible overlap. The fix adds `pr-8` to the DialogHeader so
// the title text always clears the close button. This spec asserts
// the bounding boxes don't overlap in BOTH languages.
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 quick-actions dialog layout test",
  );
}
const pool = new pg.Pool({ connectionString: DATABASE_URL });
const createdMeetingIds = [];

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

function pickFutureDate(offsetDays) {
  const d = new Date(Date.now() + offsetDays * 86_400_000);
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, "0");
  const day = String(d.getDate()).padStart(2, "0");
  return `${y}-${m}-${day}`;
}

async function nextDailyNumberForDate(date) {
  const { rows } = await pool.query(
    `SELECT COALESCE(MAX(daily_number), 0) + 1 AS n
       FROM executive_meetings
      WHERE meeting_date = $1`,
    [date],
  );
  return rows[0].n;
}

async function insertMeeting({ date, titleEn, titleAr, startTime, endTime }) {
  const dn = await nextDailyNumberForDate(date);
  const { rows } = await pool.query(
    `INSERT INTO executive_meetings
       (daily_number, title_ar, title_en, meeting_date, start_time, end_time, status)
     VALUES ($1, $2, $3, $4, $5, $6, 'scheduled')
     RETURNING id`,
    [dn, titleAr ?? titleEn, titleEn, date, startTime, endTime],
  );
  const id = rows[0].id;
  createdMeetingIds.push(id);
  return id;
}

test.afterAll(async () => {
  if (createdMeetingIds.length > 0) {
    await pool.query(
      `DELETE FROM executive_meeting_audit_logs WHERE entity_type = 'meeting' AND entity_id = ANY($1::int[])`,
      [createdMeetingIds],
    );
    await pool.query(
      `DELETE FROM executive_meeting_attendees WHERE meeting_id = ANY($1::int[])`,
      [createdMeetingIds],
    );
    await pool.query(
      `DELETE FROM executive_meetings WHERE id = ANY($1::int[])`,
      [createdMeetingIds],
    );
  }
  await pool.end();
});

function rectsOverlap(a, b) {
  if (!a || !b) return false;
  return (
    a.x < b.x + b.width &&
    a.x + a.width > b.x &&
    a.y < b.y + b.height &&
    a.y + a.height > b.y
  );
}

async function openDialogAndAssertNoOverlap(page, id) {
  const row = page.locator(`[data-testid="em-row-${id}"]`);
  await expect(row).toBeVisible({ timeout: 10_000 });
  await row.click();
  const surface = page.locator(`[data-testid="em-row-quick-${id}"]`);
  await expect(surface).toBeVisible();
  // The Radix DialogContent renders an unlabeled close button as its
  // last child <button> with sr-only label "Close". We target the
  // button that contains an svg + sr-only span — there's only one
  // button in the header area; the Postpone button has its own
  // testid and lives outside the header.
  const closeBtn = surface.locator('button[type="button"]').filter({
    has: page.locator("svg"),
  }).filter({ hasText: /^Close$/ });
  await expect(closeBtn).toHaveCount(1);
  const titleEl = surface.locator('[id^="radix-"][id$="-title"]').first();
  await expect(titleEl).toBeVisible();
  const closeBox = await closeBtn.boundingBox();
  const titleBox = await titleEl.boundingBox();
  expect(closeBox).not.toBeNull();
  expect(titleBox).not.toBeNull();
  // Hard guard: the title's bounding box must NOT overlap the close
  // button's bounding box. Before #499 in AR the rects overlapped on
  // the right edge by ~16-24px (X at right-4 = ~16px from edge,
  // 16-20px wide; title text-right pinned to the same edge).
  expect(
    rectsOverlap(closeBox, titleBox),
    `Title and Close button bounding boxes overlap. close=${JSON.stringify(closeBox)} title=${JSON.stringify(titleBox)}`,
  ).toBe(false);
}

test("EN (LTR): Quick Actions dialog title does not overlap the Close X", async ({
  page,
}) => {
  await page.addInitScript(() => {
    try {
      window.localStorage.setItem("tx-lang", "en");
    } catch {
      /* ignore */
    }
  });
  const date = pickFutureDate(40);
  const id = await insertMeeting({
    date,
    titleEn: "QA Layout EN",
    startTime: "10:00:00",
    endTime: "10:30:00",
  });
  await loginViaUi(page, "admin", "admin123");
  await page.goto("/executive-meetings");
  await page.locator('input[type="date"]').first().fill(date);
  await openDialogAndAssertNoOverlap(page, id);
});

test("AR (RTL): Quick Actions dialog title 'إجراءات سريعة' does not overlap the Close X", async ({
  page,
}) => {
  await page.addInitScript(() => {
    try {
      window.localStorage.setItem("tx-lang", "ar");
    } catch {
      /* ignore */
    }
  });
  const date = pickFutureDate(41);
  const id = await insertMeeting({
    date,
    titleEn: "QA Layout AR",
    titleAr: "تخطيط الإجراءات السريعة",
    startTime: "10:00:00",
    endTime: "10:30:00",
  });
  await loginViaUi(page, "admin", "admin123");
  await page.goto("/executive-meetings");
  await page.locator('input[type="date"]').first().fill(date);
  const surface = page.locator(`[data-testid="em-row-quick-${id}"]`);
  // Sanity: dialog mounted in RTL.
  await page.locator(`[data-testid="em-row-${id}"]`).click();
  await expect(surface).toBeVisible({ timeout: 10_000 });
  await expect(surface).toHaveAttribute("dir", "rtl");
  // Re-run the overlap check via the shared helper (which re-opens the
  // dialog via row click — already open here, but click is idempotent
  // for already-open quick-actions and the helper just re-finds the
  // surface).
  const closeBtn = surface
    .locator('button[type="button"]')
    .filter({ hasText: /^Close$/ });
  await expect(closeBtn).toHaveCount(1);
  const titleEl = surface.locator('[id^="radix-"][id$="-title"]').first();
  await expect(titleEl).toContainText("إجراءات سريعة");
  const closeBox = await closeBtn.boundingBox();
  const titleBox = await titleEl.boundingBox();
  expect(closeBox).not.toBeNull();
  expect(titleBox).not.toBeNull();
  expect(
    rectsOverlap(closeBox, titleBox),
    `RTL title and Close button overlap. close=${JSON.stringify(closeBox)} title=${JSON.stringify(titleBox)}`,
  ).toBe(false);
});
