import { test, expect } from '@playwright/test'

// AZ-478 — e2e companion for NFT-RES-03 (offline at boot) and
// NFT-RES-10 (SSE disconnect indicator). Both are marked `fast + e2e` in
// the task spec; NFT-RES-09 (tainted-canvas fallback) is fast-only and is
// not duplicated here.
//
// Both tests are `test.fail()` today because the production drifts pinned
// in `tests/network_resilience.test.tsx` are unfixed:
//
//   - NFT-RES-03: SPA falls through to /login on offline boot rather than
//     rendering an explicit network-error indicator.
//   - NFT-RES-10: SSE consumers do NOT render a connection-lost indicator
//     when the EventSource fires error+CLOSED.
//
// Once the drifts land, remove the `test.fail` and these turn green.

const ALICE_EMAIL = 'op_alice@test.local'
const ALICE_PASSWORD = 'TestPassword!23'

async function login(page: import('@playwright/test').Page): Promise<void> {
  await page.goto('/login')
  await page.getByLabel(/email/i).fill(ALICE_EMAIL)
  await page.getByLabel(/password/i).fill(ALICE_PASSWORD)
  await Promise.all([
    page.waitForResponse(
      (r) => r.url().includes('/api/admin/auth/login') && r.request().method() === 'POST',
    ),
    page.getByRole('button', { name: /sign in/i }).click(),
  ])
}

test.describe('AZ-478 — network resilience (e2e companion)', () => {
  test.fail(
    'NFT-RES-03 — offline at boot: SPA renders an explicit network-error indicator',
    async ({ page }) => {
      test.setTimeout(20_000)

      // Block ALL /api/* requests at the network layer to simulate a true
      // offline boot. The SPA boot path hits /api/admin/auth/refresh first;
      // every other downstream request also fails.
      await page.route('**/api/**', async (route) => {
        await route.abort('failed')
      })

      await page.goto('/')

      // Drift: the SPA redirects to /login silently. Spec NFT-RES-03 calls
      // for an in-DOM network-error indicator with the i18n-keyed text.
      // We look for either an explicit data-testid or a localized banner;
      // the assertion keeps both shapes acceptable so the fix can choose.
      const banner = page.locator('[data-testid="network-error-banner"]')
      const localizedText = page.getByText(/offline|network unavailable|connection lost/i)

      await expect(banner.or(localizedText)).toBeVisible({ timeout: 5_000 })

      // Defence in depth — service worker remains unregistered.
      const swCount = await page.evaluate(async () => {
        if (!('serviceWorker' in navigator)) return 0
        const regs = await navigator.serviceWorker.getRegistrations()
        return regs.length
      })
      expect(swCount).toBe(0)
    },
  )

  test.fail(
    'NFT-RES-10 — SSE disconnect surfaces a connection-lost indicator within 2 s',
    async ({ page }) => {
      test.setTimeout(20_000)

      await login(page)
      await page.goto('/flights')
      await page.getByRole('button', { name: /gps/i }).click()
      await page.getByRole('button', { name: /select flight/i }).click()
      await page.getByRole('button', { name: /flight-1|recon alpha/i }).first().click()

      // Wait until the live-GPS SSE is observed, then abort all subsequent
      // event-stream requests to drive the server-disconnect path. This
      // mirrors the spec scenario: the SSE was healthy, then drops.
      await page.waitForRequest(
        (req) => /\/api\/flights\/[^/]+\/live-gps/.test(req.url()),
        { timeout: 5_000 },
      )

      await page.route('**/api/flights/**/live-gps**', async (route) => {
        await route.abort('failed')
      })

      // Force a re-subscribe so the abort takes effect on a live channel.
      // Switching back to params then to GPS retriggers the effect.
      await page.getByRole('button', { name: /params/i }).click()
      await page.getByRole('button', { name: /gps/i }).click()

      // Drift: the SPA today never renders a connection-lost indicator.
      // Spec NFT-RES-10 requires the indicator within 2 s, with i18n-keyed
      // text. Accept either a data-testid hook or the localized text.
      const banner = page.locator('[data-testid="sse-disconnect-banner"]')
      const localizedText = page.getByText(/connection lost|disconnected|reconnect/i)

      await expect(banner.or(localizedText)).toBeVisible({ timeout: 2_000 })
    },
  )
})
