import { expect, test, type Page } from '@playwright/test'
import { startFixtureCollector, type FixtureCollector } from './fixture-collector'
import { SESSIONS_PANELS } from './fixtures/sessions-fixtures'

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

let collector: FixtureCollector

async function openProject(page: Page, project: string) {
  const table = page.getByRole('table', { name: `Sessions in Projects/${project}` })
  if (await table.count() === 0) {
    await page.getByRole('button', { name: new RegExp(`^Projects/${project} ·`) }).click()
  }
  await table.waitFor()
}

test.beforeAll(async () => {
  collector = await startFixtureCollector({ panels: SESSIONS_PANELS, adapters: [], items: [] })
})

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

test('lists every project, with the cli, launcher and state of each session', async ({ page }) => {
  await page.goto('/sessions', { waitUntil: 'domcontentloaded' })
  await page.getByRole('table', { name: 'Sessions in overdeck · obs-panel' }).waitFor()

  await expect(page.getByRole('table', { name: 'Sessions in Projects/multideal' })).toBeVisible()
  await openProject(page, 'invariantum')
  await openProject(page, 'security-gate')
  await openProject(page, 'virtuac')

  await expect(page.getByRole('table', { name: 'Sessions in Projects/invariantum' })).toBeVisible()
  await expect(page.getByRole('table', { name: 'Sessions in Projects/security-gate' })).toBeVisible()

  await expect(page.getByText('close the ledger enrollment gap')).toBeVisible()
  await expect(page.getByText('detached — reattachable')).toBeVisible()
  await expect(page.getByTestId('status-chip').filter({ hasText: /^orphaned$/ })).toHaveCount(2)
  await expect(page.getByText('no title recorded').first()).toBeVisible()
})

test('attaches to a tmux-hosted session in the browser and sends a keystroke', async ({ page }) => {
  await page.goto('/sessions', { waitUntil: 'domcontentloaded' })
  await page.getByRole('table', { name: 'Sessions in overdeck · obs-panel' }).waitFor()
  await page.getByRole('button', { name: 'Attach' }).click()

  const terminal = page.getByTestId('terminal-output')
  await expect(terminal).toContainText('fixture screen for sess-tmux-live')
  await expect(terminal).toHaveAttribute('data-terminal-state', 'attached')

  await page.getByRole('button', { name: 'Enter' }).click()
  await expect.poll(() => collector.actionCalls.filter((call) => call.verb === 'sessions.sendKeys').length).toBeGreaterThan(0)
  expect(collector.actionCalls.at(-1)?.body).toMatchObject({ args: { id: 'sess-tmux-live', key: 'Enter' } })
})

test('keeps an attached terminal open and polling while navigating, and lets the reader move it', async ({ page }) => {
  const screenRequests: string[] = []
  page.on('request', (request) => {
    if (request.url().includes('/sessions/sess-tmux-live/screen')) screenRequests.push(request.url())
  })

  await page.goto('/sessions', { waitUntil: 'domcontentloaded' })
  await page.getByRole('table', { name: 'Sessions in overdeck · obs-panel' }).waitFor()
  await page.getByRole('button', { name: 'Attach' }).click()

  const terminal = page.getByTestId('terminal-output')
  const dialog = page.getByRole('dialog')
  await expect(terminal).toContainText('fixture screen for sess-tmux-live')

  const before = await dialog.boundingBox()
  expect(before).not.toBeNull()
  const handle = await page.getByRole('button', { name: 'Drag window' }).boundingBox()
  expect(handle).not.toBeNull()
  await page.mouse.move(handle!.x + 12, handle!.y + 12)
  await page.mouse.down()
  await page.mouse.move(handle!.x - 240, handle!.y + 180)
  await page.mouse.up()
  // Directional, not merely different: a one-pixel jitter must not pass for a drag.
  // Only horizontally — a full-height terminal window is clamped to the top of the viewport.
  await expect.poll(async () => (await dialog.boundingBox())?.x).toBeLessThan(before!.x - 200)

  const requestCountBeforeNavigation = screenRequests.length
  await page.getByRole('link', { name: /^Overview$/ }).click()
  await page.waitForURL(/\/$/)
  await expect(terminal).toContainText('fixture screen for sess-tmux-live')
  await expect.poll(() => screenRequests.length, { timeout: 5_000 }).toBeGreaterThan(requestCountBeforeNavigation)
})

test('offers an account-aware resume command for a finished claude session', async ({ page }) => {
  await page.goto('/sessions', { waitUntil: 'domcontentloaded' })
  await openProject(page, 'security-gate')
  await page.getByRole('table', { name: 'Sessions in Projects/security-gate' })
    .getByRole('button', { name: 'Resume' }).click()

  await expect(page.getByText(/cld --account zync2 --resume c0c245b9/)).toBeVisible()
  await expect(page.getByTestId('terminal-output')).toHaveAttribute('data-terminal-state', 'ended')
})

test('states why an unreachable session cannot be reconnected instead of showing a dead button', async ({ page }) => {
  await page.goto('/sessions', { waitUntil: 'domcontentloaded' })
  await openProject(page, 'virtuac')

  const row = page.getByRole('row').filter({ hasText: 'sess-orphaned-noresume' })
  await expect(row.getByText(/no session id was recorded/)).toBeVisible()
  await expect(row.getByRole('button', { name: /Attach|Resume|Reopen/ })).toHaveCount(0)
})

test('renders the TerminalView gallery entry in the design system', async ({ page }) => {
  await page.goto('/design-system', { waitUntil: 'domcontentloaded' })
  await page.waitForSelector('text=TerminalView')
  await expect(page.getByTestId('terminal-output').first()).toBeVisible()
})
