// lightweight render test for the upcoming-meeting alert
// entrance animation. Asserts the alert panel renders with the
// matching scale-in/opacity transition, an accent ring (boxShadow with
// the user's accent color), and a pulsing animate-ping halo around the
// drag handle — the visual language we adopted from the incoming-note
// popup.
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");

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 todayLocal() {
  const d = new Date();
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
}
function timePlusMinutes(delta) {
  const d = new Date(Date.now() + delta * 60_000);
  return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}:00`;
}

test.afterAll(async () => {
  if (createdMeetingIds.length > 0) {
    await pool.query(
      `DELETE FROM executive_meeting_alert_state WHERE meeting_id = ANY($1::int[])`,
      [createdMeetingIds],
    );
    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();
});

test("upcoming-meeting alert renders with scale-in entrance, accent ring, and ping halo", async ({
  page,
}) => {
  test.setTimeout(60_000);
  const date = todayLocal();
  const startTime = timePlusMinutes(3);
  const endTime = timePlusMinutes(33);
  const { rows: dnRows } = await pool.query(
    `SELECT COALESCE(MAX(daily_number), 0) + 1 AS n FROM executive_meetings WHERE meeting_date = $1`,
    [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`,
    [
      dnRows[0].n,
      "ALERT_ANIM_TEST AR",
      "ALERT_ANIM_TEST EN",
      date,
      startTime,
      endTime,
    ],
  );
  createdMeetingIds.push(rows[0].id);

  await loginViaUi(page, "admin", "admin123");
  await page.goto("/");

  const alert = page.getByTestId("upcoming-meeting-alert");
  await expect(alert).toBeVisible({ timeout: 15_000 });

  // After the entrance animation settles the panel is fully visible
  // (opacity-100 / scale-100). We assert the resolved styles describe
  // the scale + opacity transition we configured.
  const transition = await alert.evaluate(
    (el) => getComputedStyle(el).transition,
  );
  expect(transition).toMatch(/transform/);
  expect(transition).toMatch(/opacity/);
  // The cubic-bezier spring curve we use for the entrance.
  expect(transition).toMatch(/cubic-bezier\(0\.34,\s*1\.56,\s*0\.64,\s*1\)/);

  // Accent ring is implemented via boxShadow (so the color tracks the
  // user's accent). Verify a 4px inset-less colored ring is the first
  // shadow layer alongside the existing drop shadow.
  const boxShadow = await alert.evaluate(
    (el) => getComputedStyle(el).boxShadow,
  );
  expect(boxShadow).toMatch(/rgba?\([^)]*\)\s+0px\s+0px\s+0px\s+4px/);

  // Pulsing accent halo around the drag handle (animate-ping element).
  const handle = page.getByTestId("alert-drag-handle");
  await expect(handle).toBeVisible();
  const halo = handle.locator(".animate-ping").first();
  await expect(halo).toHaveCount(1);
});
