/**
 * Phase 2 browser chaos — what happens when the browser drops state mid-session?
 *
 * Real users: Safari ITP, "Clear browsing data", incognito mode expiring,
 * extensions that wipe storage on tab close. The app must NEVER white-screen
 * or expose other users' data when its own state vanishes.
 */
import { test, expect } from '../../fixtures/test';
import { readStorage, clearLocalStorage, clearAllStorage } from '../../helpers/storage-helpers';

test.describe('Browser chaos — storage purge', () => {
  test('localStorage.clear() mid-session → next nav goes to /join, no crash', async ({
    page,
    guest,
    signIn,
  }) => {
    const g = await guest('Purge1');
    await signIn(page, g);
    await page.goto('/feed');
    await expect(page.getByRole('link', { name: 'Galerie' })).toBeVisible();

    // Listen for any unhandled page errors so a crash is visible.
    const errors: Error[] = [];
    page.on('pageerror', (e) => errors.push(e));

    await clearLocalStorage(page);
    await page.goto('/feed');

    // The app may redirect to /join, render an empty feed with a "sign in" prompt, or
    // surface the join screen inline. Any of these is fine — the assertion is "no crash".
    expect(errors.filter((e) => !e.message.includes('AbortError'))).toHaveLength(0);

    // Eventually the user lands somewhere they can recover from.
    const url = new URL(page.url());
    expect(['/join', '/feed', '/recover', '/']).toContain(url.pathname);
  });

  test('cookies cleared mid-session — JWT in localStorage still works (no cookie dependency)', async ({
    page,
    guest,
    signIn,
  }) => {
    const g = await guest('Purge2');
    await signIn(page, g);
    await page.goto('/feed');

    await page.context().clearCookies();

    // The api.ts client reads from localStorage, not cookies, so a /me/context call should still work.
    const stillAuthed = await page.evaluate(async () => {
      const res = await fetch('/api/v1/me/context', {
        headers: { Authorization: `Bearer ${localStorage.getItem('eventsnap_jwt')}` },
      });
      return res.status;
    });
    expect(stillAuthed).toBe(200);
  });

  test('sessionStorage cleared has no effect on auth (auth lives in localStorage)', async ({
    page,
    guest,
    signIn,
  }) => {
    const g = await guest('Purge3');
    await signIn(page, g);
    await page.goto('/feed');

    await page.evaluate(() => sessionStorage.clear());
    await page.reload();

    const storage = await readStorage(page);
    expect(storage.jwt).toBeTruthy(); // localStorage survived
    await expect(page.getByRole('link', { name: 'Galerie' })).toBeVisible();
  });

  test('clearAllStorage on /admin forces re-login', async ({ page, api }) => {
    const adminJwt = await api.adminLogin();
    await page.goto('/');
    await page.evaluate((j) => localStorage.setItem('eventsnap_jwt', j), adminJwt);
    await page.goto('/admin');

    await clearAllStorage(page);

    // The `goto` is deliberately allowed to REJECT. What is under test is a race the app wins:
    // the admin layout redirects to /admin/login the moment it sees no JWT, and a redirect that
    // lands first makes this navigation either "interrupted by another navigation" or
    // ERR_ABORTED. Both mean the app did exactly the right thing, quickly — so failing on them
    // made the test red precisely when the behaviour was correct, roughly one run in three, and
    // it read as a regression in whatever change happened to be in flight. (`waitUntil: 'commit'`
    // narrows the window but does not close it; an abort can beat commit too.)
    //
    // `waitForURL` below is the assertion, and it is unaffected by how the navigation ended.
    await page.goto('/admin').catch(() => {});
    // The admin layout should bounce them to /admin/login when the JWT is gone.
    await page.waitForURL(/admin\/login|join/, { timeout: 5_000 });
  });

  test('PIN survives clearAuth (intentional per auth.ts comment)', async ({
    page,
    guest,
    signIn,
  }) => {
    const g = await guest('PurgePin');
    await signIn(page, g);
    await page.goto('/account');
    // Let the page finish settling before touching its execution context. `goto` resolves at
    // `load`, but the layout's boot hydration is still in flight and any navigation it triggers
    // destroys the context out from under the `page.evaluate` below — which surfaced as an
    // intermittent "Execution context was destroyed" that has nothing to do with what this test
    // asserts.
    await page.waitForLoadState('networkidle');

    // Simulate clearAuth() — clears JWT + user_id but keeps PIN so the user can recover.
    await page.evaluate(() => {
      localStorage.removeItem('eventsnap_jwt');
      localStorage.removeItem('eventsnap_user_id');
    });
    const remaining = await readStorage(page);
    expect(remaining.jwt).toBeNull();
    expect(remaining.userId).toBeNull();
    expect(remaining.pin).toBe(g.pin);
  });
});
