// Authenticated critical path (docs/QA_PLAN.md B15). Runs logged-in via the
// storageState produced by auth.setup.js against the seeded scratch DB.
//
// Selectors are grounded in real DOM (no test-ids exist yet):
//   - quick-pay: StatusBadge button, title "Click to mark paid" / "...unpaid"
//   - bills:     seeded names from scripts/seedDemoData.js (e.g. "Electric Company")
//   - nav:       sidebar link labels from client/components/layout/Sidebar.tsx
const { test, expect } = require('@playwright/test');
const { STORAGE_STATE } = require('./constants');

test.use({ storageState: STORAGE_STATE });

test('tracker renders seeded bills', async ({ page }) => {
  await page.goto('/');
  // Date-independent proof the seeded month loaded with rows.
  await expect(page.getByText(/\d+ bills/).first()).toBeVisible();
  // At least one clickable status toggle is rendered (seeded bills are mostly
  // autopay → "autodraft", so the toggle usually reads "mark unpaid"). The app
  // renders both a desktop table row and a hidden mobile row, so scope to :visible.
  const toggles = page.locator('button[title="Click to mark paid"]:visible, button[title="Click to mark unpaid"]:visible');
  await expect(toggles.first()).toBeVisible();
});

test("toggling a bill's paid status updates then restores it (desktop)", async ({ page, isMobile }) => {
  test.skip(isMobile, 'mobile rows use a different control; covered on desktop');
  await page.goto('/');

  // :visible avoids the hidden mobile-row duplicates the app also renders.
  const pay = page.locator('button[title="Click to mark paid"]:visible'); // currently unpaid
  const paid = page.locator('button[title="Click to mark unpaid"]:visible'); // currently paid/autodraft
  await expect(pay.or(paid).first()).toBeVisible();

  const p0 = await pay.count();
  const u0 = await paid.count();
  expect(p0 + u0).toBeGreaterThan(0);

  // Marking a bill PAID is immediate; marking it UNPAID pops a "Remove Payment"
  // confirmation (the deletion-safety flow) that must be confirmed.
  const confirmRemoveIfPrompted = async () => {
    await page
      .getByRole('button', { name: 'Remove Payment' })
      .click({ timeout: 3000 })
      .catch(() => {});
  };

  // Toggle one bill and confirm the optimistic update reconciles (counts shift by
  // exactly one), then toggle back and confirm the original counts return — no
  // double-count, no stuck state. Direction depends on the seed's mostly-autopay bills.
  if (p0 > 0) {
    await pay.first().click();
    await expect(pay).toHaveCount(p0 - 1);
    await expect(paid).toHaveCount(u0 + 1);

    await paid.first().click();
    await confirmRemoveIfPrompted();
    await expect(pay).toHaveCount(p0);
    await expect(paid).toHaveCount(u0);
  } else {
    await paid.first().click();
    await confirmRemoveIfPrompted();
    await expect(paid).toHaveCount(u0 - 1);
    await expect(pay).toHaveCount(p0 + 1);

    await pay.first().click();
    await expect(paid).toHaveCount(u0);
    await expect(pay).toHaveCount(p0);
  }
});

test('authenticated pages render without server errors or crashes', async ({ page }) => {
  const pageErrors = [];
  const serverErrors = [];
  page.on('pageerror', (e) => pageErrors.push(String(e)));
  page.on('response', (r) => {
    if (r.url().includes('/api/') && r.status() >= 500) serverErrors.push(`${r.status()} ${r.url()}`);
  });

  // Mirrors the sidebar; navigate by URL so it's viewport-independent.
  for (const path of ['/', '/summary', '/bills', '/calendar', '/analytics', '/spending', '/snowball', '/categories']) {
    await page.goto(path);
    await page.waitForLoadState('networkidle');
    await expect(page.locator('body')).not.toBeEmpty();
  }

  expect(pageErrors, `uncaught page errors:\n${pageErrors.join('\n')}`).toEqual([]);
  expect(serverErrors, `5xx API responses:\n${serverErrors.join('\n')}`).toEqual([]);

  // TODO: once Summary's DOM is pinned, assert its paid/unpaid/remaining totals
  // reconcile numerically with the Tracker for the same month (QA_PLAN B5).
});

test('top nav navigates to Analytics (desktop)', async ({ page, isMobile }) => {
  test.skip(isMobile, 'nav collapses to a menu on mobile');
  await page.goto('/');
  // Desktop uses a top nav ("Analytics" is a direct item; most pages live under
  // the "Tracker" dropdown). Click the visible Analytics nav item.
  const analytics = page
    .getByRole('link', { name: 'Analytics' })
    .or(page.getByRole('button', { name: 'Analytics' }))
    .first();
  await analytics.click();
  await expect(page).toHaveURL(/\/analytics$/);
});
