import { expect, test } from '@playwright/test'
import { startFixtureCollector, type FixtureCollector, type FixtureCollectorState } from './fixture-collector'
import {
  CI_BUILD_CRIT_TEMP_ITEM,
  CI_BUILD_INCIDENT_ITEMS,
  CI_BUILD_INCIDENT_PANELS,
  CI_BUILD_OFFLOAD_ADAPTER,
  CLUSTER_QUEUE_EMPTY,
  FLEET_PANEL_CRIT_TEMP,
  FLEET_PANEL_NORMAL_TEMP,
  OFFLOAD_CONTROL_CRIT,
  OFFLOAD_CONTROL_NORMAL,
  REMOTE_JOBS_EMPTY,
  X1_OFFLOAD_VERBS,
  READ_ONLY_MACHINE_VERBS,
} from './fixtures/ci-build-fixtures'
import { ADAPTER_STATUSES, CI_ONLY_PANELS } from './fixtures/collector-fixtures'

test.describe.configure({ mode: 'serial' })

let collector: FixtureCollector

const INCIDENT_STATE: FixtureCollectorState = {
  panels: CI_BUILD_INCIDENT_PANELS,
  adapters: [CI_BUILD_OFFLOAD_ADAPTER, { id: 'ghci', interval: 60_000, consecutiveErrors: 0, stale: false }],
  items: CI_BUILD_INCIDENT_ITEMS,
}

test.beforeAll(async () => {
  collector = await startFixtureCollector(INCIDENT_STATE)
})

test.afterAll(async () => {
  await collector.close()
})

async function collectConsoleErrors(page: import('@playwright/test').Page, run: () => Promise<void>) {
  const errors: string[] = []
  page.on('console', (msg) => {
    if (msg.type() === 'error') errors.push(msg.text())
  })
  page.on('pageerror', (err) => errors.push(err.message))
  await run()
  return errors
}

test('renders CI & Build in dark theme with incident fixture fleet and panels', async ({ page }) => {
  const errors = await collectConsoleErrors(page, async () => {
    await page.goto('/ci')
    await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark')
    await expect(page.getByTestId('ci-build-page')).toBeVisible({ timeout: 15_000 })
    await expect(page.getByRole('heading', { level: 2, name: 'CI & Build' })).toBeVisible()

    const fleet = page.getByTestId('ci-build-fleet')
    await expect(fleet.getByTestId('machine-card-laptop')).toBeVisible()
    await expect(fleet.getByTestId('machine-card-debian1')).toBeVisible()
    await expect(fleet.getByTestId('machine-card-debian2')).toContainText('enrolling')

    await expect(page.getByTestId('cluster-queue')).toBeVisible()
    await expect(page.getByTestId('offload-control')).toBeVisible()
    await expect(page.getByTestId('remote-jobs-table')).toBeVisible()
    await expect(page.getByTestId('ci-build-incidents').getByText('Remote capacity idle while queue is stalled')).toBeVisible()
  })

  expect(errors).toEqual([])
})

test('renders correctly in light theme', async ({ page }) => {
  const errors = await collectConsoleErrors(page, async () => {
    await page.goto('/ci')
    await expect(page.locator('[role="radiogroup"][aria-label="Theme"]')).toHaveAttribute('data-hydrated', 'true')
    await page.getByRole('radio', { name: /Light/ }).click()
    await expect(page.locator('html')).toHaveAttribute('data-theme', 'light')
    await expect(page.getByTestId('ci-build-page')).toBeVisible()
    await expect(page.getByTestId('machine-card-debian1')).toBeVisible()
  })
  expect(errors).toEqual([])
})

test('clicking a machine card opens detail modal with telemetry sections', async ({ page }) => {
  await page.goto('/ci')
  await page.getByTestId('machine-card-debian1').click()
  const modal = page.getByTestId('machine-detail-modal')
  await expect(modal).toBeVisible()
  await expect(modal.getByTestId('machine-core-grid')).toBeVisible()
  await expect(modal.getByTestId('machine-memory-section')).toBeVisible()
  await expect(modal.getByTestId('machine-network-section')).toBeVisible()
  await expect(modal.getByTestId('machine-disk-section')).toBeVisible()
  await expect(modal.getByText('92%')).toBeVisible()
})

test('every action button carries a valid X1 offload verb', async ({ page }) => {
  await page.goto('/ci')
  await expect(page.getByTestId('machine-card-debian1')).toBeVisible()
  const verbs = await page.getByTestId('ci-build-offload').locator('[data-action-verb]').evaluateAll((nodes) =>
    nodes.map((node) => node.getAttribute('data-action-verb')).filter(Boolean),
  )
  expect(verbs.length).toBeGreaterThan(0)
  for (const verb of verbs) {
    if ((READ_ONLY_MACHINE_VERBS as readonly string[]).includes(verb!)) continue
    expect(X1_OFFLOAD_VERBS).toContain(verb)
  }
})

test('normal-temp fixture does not show critical temperature banner in modal', async ({ page }) => {
  await collector.close()
  collector = await startFixtureCollector({
    panels: [
      OFFLOAD_CONTROL_NORMAL,
      CLUSTER_QUEUE_EMPTY,
      REMOTE_JOBS_EMPTY,
      FLEET_PANEL_NORMAL_TEMP,
      ...CI_BUILD_INCIDENT_PANELS.filter((panel) => panel.id === 'ci'),
    ],
    adapters: [CI_BUILD_OFFLOAD_ADAPTER, { id: 'ghci', interval: 60_000, consecutiveErrors: 0, stale: false }],
    items: [],
  })

  await page.goto('/ci')
  await page.getByTestId('machine-card-debian1').click()
  await expect(page.getByTestId('machine-detail-modal')).toBeVisible()
  await expect(page.getByTestId('machine-crit-temp-banner')).toHaveCount(0)
})

test('crit-temp fixture shows critical temperature banner in modal', async ({ page }) => {
  await collector.close()
  collector = await startFixtureCollector({
    panels: [
      OFFLOAD_CONTROL_CRIT,
      CLUSTER_QUEUE_EMPTY,
      REMOTE_JOBS_EMPTY,
      FLEET_PANEL_CRIT_TEMP,
      ...CI_BUILD_INCIDENT_PANELS.filter((panel) => panel.id === 'ci'),
    ],
    adapters: [CI_BUILD_OFFLOAD_ADAPTER, { id: 'ghci', interval: 60_000, consecutiveErrors: 0, stale: false }],
    items: [CI_BUILD_CRIT_TEMP_ITEM],
  })

  await page.goto('/ci')
  await page.getByTestId('machine-card-debian1').click()
  await expect(page.getByTestId('machine-crit-temp-banner')).toBeVisible()
  await expect(page.getByText(/CRITICAL temperature/)).toBeVisible()
})

test('CI-only fixture renders CI content and labels every unavailable offload KPI', async ({ page }) => {
  collector.replaceState({
    panels: CI_ONLY_PANELS,
    adapters: ADAPTER_STATUSES.filter((adapter) => adapter.id === 'ghci'),
    items: [],
  })

  await page.goto('/ci', { waitUntil: 'domcontentloaded' })

  await expect(page.getByText('alexcodeplace/multideal #4821')).toBeVisible()
  await expect(page.getByTestId('ci-build-page')).toBeVisible()
  await expect(page.getByText('No offload panels in collector state.')).toHaveCount(0)
  await expect(page.getByTestId('ci-build-offload')).toContainText('Offload control not recorded')
  await expect(page.getByTestId('ci-build-offload')).toContainText('Cluster queue not recorded')
  await expect(page.getByTestId('ci-build-offload')).toContainText('Fleet not recorded')
  await expect(page.getByTestId('ci-build-offload')).toContainText('Remote jobs not recorded')
  await expect(page.getByTestId('offload-kpi-remote24h')).toContainText('not recorded')
  await expect(page.getByTestId('offload-kpi-remoteSuccessPct')).toContainText('not recorded')
  await expect(page.getByTestId('offload-kpi-oldestQueuedMs')).toContainText('not recorded')
  await expect(page.getByTestId('offload-kpi-exit127')).toContainText('not recorded')
  await expect(page.getByTestId('offload-kpi-epochDiscarded')).toContainText('not recorded')
  await expect(page.getByTestId('offload-kpi-longestPullMs')).toContainText('not recorded')
})

test('Logs opens read-only viewer; Drain confirm appears above machine modal', async ({ page }) => {
  collector.replaceState(INCIDENT_STATE)
  await page.goto('/ci')
  await expect(page.getByTestId('machine-card-debian1')).toBeVisible({ timeout: 15_000 })
  await page.getByTestId('machine-card-debian1').click()
  const machineModal = page.getByTestId('machine-detail-modal')
  await expect(machineModal).toBeVisible()

  await machineModal.getByRole('button', { name: 'Logs' }).click()
  const logsModal = page.getByTestId('host-logs-modal')
  await expect(logsModal).toBeVisible()
  await expect(logsModal.getByTestId('host-logs-body')).toContainText('builder ready')
  await expect(page.getByTestId('action-confirm')).toHaveCount(0)

  await logsModal.getByLabel('Close').click()
  await expect(logsModal).toHaveCount(0)

  await machineModal.getByRole('button', { name: 'Drain' }).click()
  const confirm = page.getByTestId('action-confirm')
  await expect(confirm).toBeVisible()
  await expect(machineModal).toBeVisible()
})
