// Regression for the per-card action row visibility on touch devices.
// On phones / iPad there is no hover, so the row that holds Send /
// archive / delete must be visible without any gesture. This test runs
// in a touch-emulating context, creates a note, and asserts the Send
// button is visible immediately — no hover required.
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 = [];

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

test.afterAll(async () => {
  if (createdUserIds.length > 0) {
    await pool.query(`DELETE FROM notes 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(),
  ]);
}

// Emulate a touch device on the default (chromium) browser. We avoid
// devices["iPad …"] because that pins the browser to webkit, which
// isn't installed in this environment. Chromium honours hasTouch +
// no mouse so `(hover: none)` matches.
test.use({
  hasTouch: true,
  isMobile: true,
  viewport: { width: 820, height: 1180 },
});

test.describe("note card actions on touch devices", () => {
  test("Send button is visible without hovering on touch devices", async ({
    page,
  }) => {
    const user = await createUser("notes_touch_actions", "Touch Tester");
    await loginViaUi(page, user.username);

    await page.goto("/notes");
    await expect(page.getByTestId("notes-page")).toBeVisible();

    // Sanity: the touch CSS variant is what we depend on. If the test
    // browser doesn't actually report `(hover: none)`, the assertion
    // below would still hold for the wrong reason (no hover applied),
    // so guard it.
    const noHover = await page.evaluate(
      () => window.matchMedia("(hover: none)").matches,
    );
    expect(noHover).toBe(true);

    const content = `touch-test ${uniq()}`;
    await page.getByTestId("notes-composer").click();
    await page.getByTestId("notes-composer-content").fill(content);
    const savePromise = page.waitForResponse(
      (r) =>
        new URL(r.url()).pathname === "/api/notes" &&
        r.request().method() === "POST" &&
        r.status() >= 200 &&
        r.status() < 300,
    );
    // Tap outside the composer to trigger auto-save.
    await page.touchscreen.tap(10, 10);
    const note = await (await savePromise).json();

    const card = page.getByTestId(`note-card-${note.id}`);
    await expect(card).toBeVisible();

    // The Send button must be visible without any hover gesture.
    const sendBtn = page.getByTestId(`note-card-send-${note.id}`);
    await expect(sendBtn).toBeVisible();

    // And it must actually be clickable (not occluded / opacity-0).
    const opacity = await sendBtn.evaluate(
      (el) => getComputedStyle(el.parentElement).opacity,
    );
    expect(Number(opacity)).toBe(1);
  });
});
