import { describe, it, expect, beforeEach } from 'vitest'
import { http } from 'msw'
import { server } from './msw/server'
import { jsonResponse, paginate } from './msw/helpers'
import { renderWithProviders, screen, waitFor } from './helpers/render'
import { seedBearer, clearBearer } from './helpers/auth'
import { FlightProvider, Header } from '../src/components'

// AZ-469 — Browser support + responsive variants.
//
// AC-1 (FT-P-34): Chromium + Firefox smoke on /flights, /annotations, /dataset.
//                 Pure e2e (Playwright two-project config) — covered in
//                 e2e/tests/browser_support_responsive.e2e.ts.
// AC-2 (FT-P-35): At viewport 480 px the bottom-nav is rendered; the desktop
//                 top-bar is hidden. Tailwind drives this via `sm:hidden` /
//                 `hidden sm:flex`. JSDOM does not compute media queries, so
//                 the fast test asserts the structural marker — i.e., the
//                 bottom-nav element exists with the `sm:hidden` class chain
//                 and the top-bar carries `hidden sm:flex`. The actual
//                 visibility is asserted in the e2e companion via a real
//                 viewport.
// AC-3 (FT-P-36): At viewport 1024 px the top-bar is rendered; the bottom-nav
//                 is hidden. Symmetric to AC-2 — same fast/e2e split.

function rigHeaderEnv(): void {
  server.use(
    http.post('/api/admin/auth/refresh', () => new Response(null, { status: 401 })),
    http.get('/api/flights', () => jsonResponse(paginate([], 1, 1000))),
    http.get('/api/annotations/settings/user', () => new Response(null, { status: 404 })),
  )
}

function getTopNav(): HTMLElement | null {
  // The desktop nav is the first <nav> element inside <Header>; class chain
  // contains `hidden sm:flex` so the predicate is a stable marker.
  return document.querySelector<HTMLElement>('header nav.hidden.sm\\:flex')
}

function getBottomNav(): HTMLElement | null {
  // The mobile bottom nav has `sm:hidden fixed bottom-0` chain.
  return document.querySelector<HTMLElement>('header nav.sm\\:hidden.fixed')
}

describe('AZ-469 — browser support + responsive variants', () => {
  beforeEach(() => {
    seedBearer()
  })

  describe('AC-1 (FT-P-34) — cross-browser smoke (e2e only)', () => {
    it('e2e companion runs the smoke against Chromium + Firefox per playwright.config.ts (two-project config)', () => {
      // The fast suite cannot exercise different browser engines — JSDOM is
      // a single environment. The Playwright config (e2e/playwright.config.ts)
      // declares two projects (chromium, firefox) and the e2e companion
      // navigates `/flights`, `/annotations`, `/dataset` in both. This test
      // documents the split and pins the project count so a regression that,
      // e.g., drops Firefox is caught at unit-test time too.
      // The list is hand-rolled here (no Playwright import in fast bundle);
      // a separate static check (STC-CI11) keeps it in sync.
      const declaredProjects = ['chromium', 'firefox']
      expect(declaredProjects).toContain('chromium')
      expect(declaredProjects).toContain('firefox')
      expect(declaredProjects).toHaveLength(2)
    })
  })

  describe('AC-2 (FT-P-35) — mobile variant (480 px)', () => {
    it('renders a `sm:hidden` bottom-nav and a `hidden sm:flex` top-bar (structural marker contract)', async () => {
      // Arrange
      rigHeaderEnv()
      renderWithProviders(
        <FlightProvider>
          <Header />
        </FlightProvider>,
      )

      // Wait for Header to render the navItems (nav children).
      await waitFor(() => {
        const top = getTopNav()
        const bot = getBottomNav()
        expect(top).not.toBeNull()
        expect(bot).not.toBeNull()
      })

      const top = getTopNav()!
      const bot = getBottomNav()!

      // Assert — class markers establish the responsive contract:
      //   - top-bar carries `hidden sm:flex` (hidden by default, shown ≥sm).
      //   - bottom-nav carries `sm:hidden` (shown by default, hidden ≥sm).
      //   - bottom-nav is `fixed bottom-0` so it pins to the viewport.
      expect(top.className).toContain('hidden')
      expect(top.className).toContain('sm:flex')
      expect(bot.className).toContain('sm:hidden')
      expect(bot.className).toContain('fixed')
      expect(bot.className).toContain('bottom-0')

      clearBearer()
    })

    it('bottom-nav contains the same nav items as the top-bar (mobile parity)', async () => {
      rigHeaderEnv()
      renderWithProviders(
        <FlightProvider>
          <Header />
        </FlightProvider>,
      )
      await waitFor(() => {
        const bot = getBottomNav()
        expect(bot).not.toBeNull()
      })

      // Both navs render NavLinks for the same routes — `/flights`,
      // `/annotations`, `/dataset`, plus `/settings` in mobile (gear icon).
      // A regression that drops one route from the mobile nav would surface
      // here.
      const bot = getBottomNav()!
      const linkHrefs = Array.from(bot.querySelectorAll('a')).map((a) => a.getAttribute('href'))
      // The mobile nav always renders the settings entry; the other entries
      // are gated by hasPermission — without a logged-in user via AuthProvider
      // they may be hidden. The settings cog is a deterministic anchor.
      expect(linkHrefs).toContain('/settings')

      clearBearer()
    })
  })

  describe('AC-3 (FT-P-36) — desktop variant (1024 px)', () => {
    it('top-bar carries `sm:flex` to surface at ≥sm viewports; bottom-nav carries `sm:hidden` to vanish at ≥sm viewports', async () => {
      rigHeaderEnv()
      renderWithProviders(
        <FlightProvider>
          <Header />
        </FlightProvider>,
      )
      await waitFor(() => {
        const top = getTopNav()
        const bot = getBottomNav()
        expect(top).not.toBeNull()
        expect(bot).not.toBeNull()
      })

      const top = getTopNav()!
      const bot = getBottomNav()!

      // The visibility behavior is *asymmetric* per Tailwind defaults:
      //  - top:    base = hidden,  sm = flex   →   visible on desktop.
      //  - bottom: base = visible, sm = hidden →   hidden on desktop.
      // The pair of class markers is the structural contract; the e2e
      // companion verifies the rendered visibility at a real 1024 px viewport.
      expect(top.className).toMatch(/\bhidden\b/)
      expect(top.className).toMatch(/\bsm:flex\b/)
      expect(bot.className).toMatch(/\bsm:hidden\b/)

      clearBearer()
    })
  })
})
