import AxeBuilder from '@axe-core/playwright'
import { expect, test, type Page } from '@playwright/test'
import { assertApprovedBaseline } from './visual-baselines'

type MediaCase = {
  name: string
  reducedMotion?: 'reduce' | 'no-preference'
  forcedColors?: 'active' | 'none'
  contrast?: 'more' | 'no-preference'
  zoom?: number
}

const MEDIA_CASES: MediaCase[] = [
  { name: 'default' },
  { name: 'reduced-motion', reducedMotion: 'reduce' },
  { name: 'forced-colors', forcedColors: 'active' },
  { name: 'high-contrast', contrast: 'more' },
  { name: 'zoom-200', zoom: 2 },
]

async function applyMedia(page: Page, mediaCase: MediaCase) {
  await page.emulateMedia({
    reducedMotion: mediaCase.reducedMotion ?? 'no-preference',
    forcedColors: mediaCase.forcedColors ?? 'none',
    contrast: mediaCase.contrast ?? 'no-preference',
  })

  if (mediaCase.zoom != null) {
    await page.addStyleTag({ content: `html { zoom: ${mediaCase.zoom}; }` })
  }
}

async function runAxe(page: Page, mediaCase?: MediaCase) {
  const builder = new AxeBuilder({ page }).options({
    rules: { 'focus-order-semantics': { enabled: true } },
  })

  if (mediaCase?.forcedColors === 'active') {
    // axe-core's color-contrast check reads authored CSS values, not the UA's
    // forced-colors used-value substitution — it flags a false positive here
    // even though the browser actually renders CanvasText-on-Canvas.
    builder.disableRules(['color-contrast'])
  }

  return builder.analyze()
}

async function expectNoRoleClickViolations(page: Page) {
  const count = await page.locator('[onclick]:not([role]), [tabindex][onclick]:not([role])').count()
  expect(count).toBe(0)
}

test.describe('ui primitives accessibility gate', () => {
  test('goes red on an injected no-role click target', async ({ page }) => {
    await page.goto('/ui-primitives-a11y-bad')

    const scan = await runAxe(page)
    const broken = page.getByTestId('bad-click-target')

    await expect(broken).toBeVisible()
    await expect(broken).not.toHaveAttribute('role', /.+/)
    expect(scan.violations.some((violation) => violation.id === 'focus-order-semantics')).toBe(true)
  })

  for (const mediaCase of MEDIA_CASES) {
    test(`stays green on the clean fixture under ${mediaCase.name}`, async ({ page }) => {
      await page.goto('/ui-primitives-a11y')
      await applyMedia(page, mediaCase)

      const scan = await runAxe(page, mediaCase)

      expect(scan.violations).toEqual([])
      await expectNoRoleClickViolations(page)
      await expect(page.getByRole('heading', { name: 'UI primitives gate fixture' })).toBeVisible()
    })
  }

  test('stores visual baselines only after explicit approval', async ({ page }) => {
    assertApprovedBaseline('ui-primitives-clean.png')
    assertApprovedBaseline('palette-picker-clean.png')

    await page.goto('/ui-primitives-a11y')
    await expect(page.getByTestId('ui-primitives-a11y')).toHaveScreenshot('ui-primitives-clean.png')

    await page.goto('/palette-picker-visual')
    await expect(page.getByTestId('palette-picker-container')).toHaveScreenshot('palette-picker-clean.png')
  })
})
