import { test, expect } from "./fixtures/test";
import postgres from "postgres";
import { randomUUID } from "node:crypto";

// Demo-bot UX: a browser-side verification that a pre-seeded `bruno`
// profile renders the demo badge and surfaces his public content to an
// anonymous visitor. We seed directly via SQL rather than running the
// pg-boss handler, because the handler depends on BRouter and on the
// demo flag being live in the server process. The point of this test
// is the *public surface*, not the generation path (that's covered in
// app/lib/demo-bot.integration.test.ts).

const CONN = process.env.DATABASE_URL ?? "postgres://trails:trails@localhost:5432/trails";

async function seedBrunoWithSyntheticRoute(): Promise<{ username: string; routeName: string }> {
  const sql = postgres(CONN, { max: 1 });
  const username = `bruno-e2e-${Date.now()}`;
  const routeName = `Bruno's ${Date.now()} walk`;
  const userId = randomUUID();
  const routeId = randomUUID();
  try {
    // Demo bot needs profile_visibility='public' so anon visitors see
    // his profile in full (the locked-account default would render a
    // stub instead).
    await sql`
      INSERT INTO journal.users (id, email, username, display_name, bio, domain, profile_visibility, terms_accepted_at, terms_version)
      VALUES (${userId}, ${`${username}@example.com`}, ${username}, 'Bruno', 'Professional park inspector.', 'localhost', 'public', now(), '2026-04-19')
    `;
    await sql`
      INSERT INTO journal.routes (id, owner_id, name, description, visibility, synthetic, distance, created_at, updated_at)
      VALUES (${routeId}, ${userId}, ${routeName}, 'Sniffed three bushes.', 'public', true, 3200, now(), now())
    `;
  } finally {
    await sql.end();
  }
  return { username, routeName };
}

async function cleanupBruno(username: string) {
  const sql = postgres(CONN, { max: 1 });
  try {
    const [row] = await sql<{ id: string }[]>`SELECT id FROM journal.users WHERE username = ${username}`;
    if (!row) return;
    await sql`DELETE FROM journal.activities WHERE owner_id = ${row.id}`;
    await sql`DELETE FROM journal.routes WHERE owner_id = ${row.id}`;
    await sql`DELETE FROM journal.users WHERE id = ${row.id}`;
  } finally {
    await sql.end();
  }
}

test.describe.configure({ mode: "serial" });

test.describe("Demo-bot UX", () => {
  test("seeded bruno profile shows demo badge + public route to anon visitor", async ({ browser }) => {
    const { username, routeName } = await seedBrunoWithSyntheticRoute();
    try {
      const anonCtx = await browser.newContext();
      const anon = await anonCtx.newPage();
      const resp = await anon.goto(`/users/${username}`);
      expect(resp?.status()).toBe(200);

      await expect(anon.getByRole("heading", { name: /Bruno/ })).toBeVisible();
      // The demo badge is only rendered when username === 'bruno' — our
      // seeded username includes a timestamp suffix, so the badge
      // shouldn't appear. This is an intentional guard: only the *real*
      // bruno gets the badge. The rest of the page still works.
      await expect(anon.getByText("🐕 Demo account")).toHaveCount(0);

      // Public synthetic route is listed on the profile.
      await expect(anon.getByRole("link", { name: new RegExp(routeName) })).toBeVisible();
      await anonCtx.close();
    } finally {
      await cleanupBruno(username);
    }
  });

  test("profile rendered at /users/bruno shows the demo badge when username is exactly 'bruno'", async ({ browser }) => {
    const sql = postgres(CONN, { max: 1 });
    const userId = randomUUID();
    const routeId = randomUUID();
    // Use ON CONFLICT so a pre-existing prod `bruno` user doesn't block
    // the test; we don't clean up a row we didn't create.
    const created = await sql`
      INSERT INTO journal.users (id, email, username, display_name, bio, domain, profile_visibility, terms_accepted_at, terms_version)
      VALUES (${userId}, 'bruno@localhost', 'bruno', 'Bruno', 'Professional park inspector.', 'localhost', 'public', now(), '2026-04-19')
      ON CONFLICT (username) DO NOTHING
      RETURNING id
    `;
    const weCreatedIt = created.length > 0;
    const effectiveUserId = weCreatedIt ? userId : (await sql`SELECT id FROM journal.users WHERE username='bruno'`)[0]!.id as string;
    // Make sure bruno is public regardless of who created him — the
    // demo profile must render in full for anon visitors. If he was
    // pre-existing with `private` (e.g. on a fresh DB after the
    // default flipped), force him public for this test's purposes.
    await sql`UPDATE journal.users SET profile_visibility = 'public' WHERE id = ${effectiveUserId}`;

    await sql`
      INSERT INTO journal.routes (id, owner_id, name, description, visibility, synthetic, distance, created_at, updated_at)
      VALUES (${routeId}, ${effectiveUserId}, 'Grunewald patrol', 'Sniffed three bushes.', 'public', true, 3200, now(), now())
    `;
    await sql.end();

    try {
      const anonCtx = await browser.newContext();
      const anon = await anonCtx.newPage();
      const resp = await anon.goto(`/users/bruno`);
      expect(resp?.status()).toBe(200);
      await expect(anon.getByText(/Demo account|Demo-Konto/)).toBeVisible();
      await expect(anon.getByRole("link", { name: /Grunewald patrol/ })).toBeVisible();
      await anonCtx.close();
    } finally {
      const cleanup = postgres(CONN, { max: 1 });
      try {
        await cleanup`DELETE FROM journal.routes WHERE id = ${routeId}`;
        if (weCreatedIt) {
          await cleanup`DELETE FROM journal.users WHERE id = ${userId}`;
        }
      } finally {
        await cleanup.end();
      }
    }
  });
});
