import { expect, test } from '@playwright/test'
import { mockNexusApi } from './support/nexusApi'

test.describe('task board query lifecycle', () => {
  test('shows authoritative data after refresh and appends Done pagination', async ({ page }) => {
    const api = await mockNexusApi(page, { role: 'owner' })
    await page.goto('/tasks')
    const board = page.locator('.board-columns')

    await expect(board.getByText('E2E Backlog task', { exact: true })).toBeVisible()
    await expect(board.getByText('E2E Done page one', { exact: true })).toBeVisible()
    await expect(board.getByText('E2E Done page two', { exact: true })).toHaveCount(0)
    await expect(page.getByText('Revision board-r1', { exact: true })).toBeVisible()

    await page.getByRole('button', { name: 'Weitere erledigte laden' }).click()
    await expect(board.getByText('E2E Done page two', { exact: true })).toBeVisible()
    await expect(page.getByRole('button', { name: 'Weitere erledigte laden' })).toHaveCount(0)
    expect(api.boardRequestUrls.some(url => url.includes('doneCursor=done-page-2'))).toBe(true)

    await page.reload()
    await expect(board.getByText('E2E Backlog task', { exact: true })).toBeVisible()
    await expect(board.getByText('E2E Done page one', { exact: true })).toBeVisible()
    await expect(page.getByRole('button', { name: 'Weitere erledigte laden' })).toBeVisible()
    expect(api.boardRequestUrls.filter(url => !url.includes('doneCursor=')).length).toBeGreaterThanOrEqual(2)
  })

  test('advances the SSE cursor and performs exactly one visible-data resync', async ({ page }) => {
    const api = await mockNexusApi(page, {
      role: 'owner',
      domainResyncSequence: 25,
      boardRefreshDelayMs: 700,
      boardRefreshTitle: 'E2E Backlog task refreshed',
    })
    await page.goto('/tasks')
    const board = page.locator('.board-columns')

    await expect(board.getByText('E2E Backlog task', { exact: true })).toBeVisible()
    await expect.poll(
      () => api.boardRequestUrls.filter(url => !url.includes('doneCursor=')).length,
    ).toBe(2)

    // A background refresh must retain the authoritative snapshot instead of
    // replacing it with a loading state.
    await expect(board.getByText('E2E Backlog task', { exact: true })).toBeVisible()
    await expect(board.getByText('E2E Backlog task refreshed', { exact: true })).toBeVisible()
    await expect(page.getByText('Revision board-r2', { exact: true })).toBeVisible()

    await expect.poll(() => api.domainEventRequests.length).toBeGreaterThanOrEqual(2)
    expect(api.domainEventRequests[0]?.lastEventId).toBeNull()
    expect(api.domainEventRequests.slice(1).every(item => item.lastEventId === '25')).toBe(true)
    expect(api.resyncResponseCount).toBe(1)

    // Give another reconnect enough time to prove the stale cursor does not
    // trigger a second resync or a second REST refresh.
    await page.waitForTimeout(1_200)
    expect(api.resyncResponseCount).toBe(1)
    expect(api.boardRequestUrls.filter(url => !url.includes('doneCursor='))).toHaveLength(2)
  })

  test('persists a drag-and-drop move and keeps the card in its new column', async ({ page }) => {
    const api = await mockNexusApi(page, { role: 'owner' })
    await page.goto('/tasks')

    const source = page.locator('.card').filter({ hasText: 'E2E Backlog task' }).first()
    const reviewColumn = page.locator('.col').filter({
      has: page.locator('.col-name', { hasText: 'Review' }),
    })
    await expect(source).toBeVisible()
    const dataTransfer = await page.evaluateHandle(() => new DataTransfer())
    await source.dispatchEvent('dragstart', { dataTransfer })
    await reviewColumn.dispatchEvent('dragover', { dataTransfer })
    await reviewColumn.dispatchEvent('drop', { dataTransfer })
    await source.dispatchEvent('dragend', { dataTransfer })

    await expect.poll(() => api.taskMutationRequests.length).toBe(1)
    expect(api.taskMutationRequests[0]?.path).toBe('/api/dashboard/tasks/task-open-1/move')
    expect(api.taskMutationRequests[0]?.body).toEqual({ state: 'review' })
    await expect(reviewColumn.getByText('E2E Backlog task', { exact: true })).toBeVisible()
    await expect(
      page.locator('.col').filter({
        has: page.locator('.col-name', { hasText: 'Offen' }),
      }).getByText('E2E Backlog task', { exact: true }),
    ).toHaveCount(0)

    const operationTray = page.getByRole('complementary', {
      name: 'Letztes Operationsergebnis',
    })
    await expect(operationTray.getByText('Task verschoben', { exact: true })).toBeVisible()
    const taskResultLink = operationTray.getByRole('link', { name: /E2E Backlog task/ })
    await expect(taskResultLink).toHaveAttribute('href', '/tasks/task-open-1')
    await taskResultLink.click()
    await expect(page).toHaveURL('/tasks/task-open-1')
  })
})

const viewports = [
  { width: 375, height: 812 },
  { width: 768, height: 900 },
  { width: 1024, height: 900 },
  { width: 1440, height: 900 },
  { width: 1920, height: 1080 },
]

for (const viewport of viewports) {
  test(`contains horizontal board scrolling at ${viewport.width}px without page overflow`, async ({ page }) => {
    await page.setViewportSize(viewport)
    await mockNexusApi(page, { role: 'owner' })
    await page.goto('/tasks')
    await expect(page.locator('.board-columns').getByText('E2E Backlog task', { exact: true })).toBeVisible()

    const geometry = await page.evaluate(() => {
      const root = document.documentElement
      const body = document.body
      const main = document.querySelector<HTMLElement>('.legacy-main')
      const scroller = document.querySelector<HTMLElement>('.board-columns')
      if (!main || !scroller) throw new Error('Expected Task Board geometry was not rendered.')
      const mainRect = main.getBoundingClientRect()
      const scrollerRect = scroller.getBoundingClientRect()
      scroller.scrollLeft = 100
      return {
        viewportWidth: root.clientWidth,
        rootOverflow: root.scrollWidth - root.clientWidth,
        bodyOverflow: body.scrollWidth - root.clientWidth,
        mainLeft: mainRect.left,
        mainRight: mainRect.right,
        scrollerLeft: scrollerRect.left,
        scrollerRight: scrollerRect.right,
        scrollerClientWidth: scroller.clientWidth,
        scrollerScrollWidth: scroller.scrollWidth,
        scrollerScrollLeft: scroller.scrollLeft,
        scrollerOverflowX: getComputedStyle(scroller).overflowX,
      }
    })

    expect(geometry.rootOverflow).toBeLessThanOrEqual(1)
    expect(geometry.bodyOverflow).toBeLessThanOrEqual(1)
    expect(geometry.mainLeft).toBeGreaterThanOrEqual(-1)
    expect(geometry.mainRight).toBeLessThanOrEqual(geometry.viewportWidth + 1)
    expect(geometry.scrollerLeft).toBeGreaterThanOrEqual(-1)
    expect(geometry.scrollerRight).toBeLessThanOrEqual(geometry.viewportWidth + 1)
    expect(geometry.scrollerScrollWidth).toBeGreaterThanOrEqual(geometry.scrollerClientWidth)
    expect(geometry.scrollerOverflowX).toBe('auto')
    if (viewport.width <= 1024) {
      expect(geometry.scrollerScrollWidth - geometry.scrollerClientWidth).toBeGreaterThan(1)
      expect(geometry.scrollerScrollLeft).toBeGreaterThan(0)
    }
  })
}
