/**
 * E2E tests for Bitable views — the standalone /bitable route hierarchy.
 *
 * Navigation: login via UI form → click TopNav "多维表格" icon (SPA navigate
 * to /bitable). This avoids the whoami cold-start bug that page.goto would
 * trigger on a full reload.
 *
 * ponytail: bitable backend may not be fully configured (no DATABASE_URL);
 * the view should still render its topbar and file list gracefully.
 */

import { test, expect, type Page } from '@playwright/test'
import { TEST_USER, clearAuth, waitForServer } from './helpers'

async function loginAndOpenBitable(page: Page): Promise<void> {
  await page.goto('/login')
  await clearAuth(page)
  await page.getByPlaceholder('请输入用户名').fill(TEST_USER.username)
  await page.getByPlaceholder('请输入密码').fill(TEST_USER.password)
  await page.getByRole('button', { name: /登\s*录/ }).click()
  await expect(page).toHaveURL(/\/agent/, { timeout: 15_000 })

  // SPA-navigate to /bitable via the TopNav "多维表格" button.
  await page.getByRole('button', { name: '多维表格' }).click()
  await expect(page).toHaveURL(/\/bitable/, { timeout: 15_000 })
  await expect(page.locator('.bitable-file-list-view')).toBeVisible({ timeout: 15_000 })
}

test.describe('Bitable View E2E', () => {
  test.beforeAll(async () => {
    try {
      await waitForServer(undefined, 5_000)
    } catch {
      test.skip(true, 'Backend not running — skipping bitable view E2E')
    }
  })

  test('B1: bitable file list loads without white screen or 401 redirect', async ({ page }) => {
    await loginAndOpenBitable(page)

    // URL should be /bitable, not redirected to /login.
    await expect(page).toHaveURL(/\/bitable/, { timeout: 10_000 })
    // File list view root element visible — no white screen.
    await expect(page.locator('.bitable-file-list-view')).toBeVisible()
  })

  test('B2: bitable file list core elements are visible', async ({ page }) => {
    await loginAndOpenBitable(page)

    // The topbar title "多维表格" should be visible.
    await expect(page.locator('.bitable-file-list-view__title')).toContainText('多维表格', {
      timeout: 15_000,
    })

    // Either the file grid (cards) or the empty state is rendered.
    await expect
      .poll(
        async () => {
          const grid = await page.locator('.bitable-file-list-view__grid').count()
          const empty = await page.locator('.bitable-file-list-view__empty').count()
          return grid + empty
        },
        { timeout: 15_000, intervals: [1_000] },
      )
      .toBeGreaterThan(0)
  })

  test('B3: new file button is visible and clickable', async ({ page }) => {
    await loginAndOpenBitable(page)

    const newButton = page.getByRole('button', { name: /新建文件/ })
    await expect(newButton).toBeVisible({ timeout: 10_000 })
  })
})

// ---------------------------------------------------------------------------
// U4 (R3): View type switcher — "新建视图" exposes 5 view types; only `grid`
// is enabled in v1, the rest are disabled with a "规划中" tooltip.
//
// These tests require a running backend (to create a file + table so the
// ViewSwitcher renders). They skip gracefully if the backend is down, mirroring
// the B1-B3 suite. The POST /views request is intercepted and mocked so the
// view-creation assertion is deterministic and does not depend on backend
// view persistence.
// ---------------------------------------------------------------------------

test.describe('Bitable View Type Switcher E2E (U4)', () => {
  test.beforeAll(async () => {
    try {
      await waitForServer(undefined, 5_000)
    } catch {
      test.skip(true, 'Backend not running — skipping view type switcher E2E')
    }
  })

  /**
   * Log in, create a bitable file + table via the UI, and wait for the
   * ViewSwitcher ("新建视图" button) to render. Returns once the grid header
   * is visible so callers can interact with the view switcher.
   *
   * Each test uses a unique file name to avoid collisions with parallel runs.
   */
  async function openFileDetailWithTable(page: Page, label: string): Promise<void> {
    await loginAndOpenBitable(page)

    // Create a file — opens the file detail view.
    await page.getByRole('button', { name: /新建文件/ }).click()
    await page.getByPlaceholder('请输入文件名').fill(`U4-${label}`)
    await page.getByRole('button', { name: /确\s*定/ }).click()
    await expect(page).toHaveURL(/\/bitable\/[^/]+/, { timeout: 10_000 })

    // Create a table — required for the ViewSwitcher to render.
    await page.locator('.table-view-list__header .ant-btn').click()
    await expect(page.getByText('新建数据表')).toBeVisible({ timeout: 5_000 })
    await page.getByPlaceholder('请输入表名').fill(`U4表-${label}`)
    await page.getByRole('button', { name: /确\s*定/ }).click()

    // Wait for the grid header (and thus the ViewSwitcher) to render.
    await expect(page.locator('.bitable-file-detail-view__table-name')).toContainText(
      `U4表-${label}`,
      { timeout: 10_000 },
    )
  }

  /** Open the "新建视图" dropdown and return the menu item locator list. */
  async function openViewTypeDropdown(page: Page): Promise<import('@playwright/test').Locator> {
    await page.getByRole('button', { name: /新建视图/ }).click()
    // The dropdown overlay menu items.
    return page.locator('.ant-dropdown-menu-item')
  }

  test('E1: "新建视图" dropdown exposes all 5 view types', async ({ page }) => {
    await openFileDetailWithTable(page, 'E1-types')

    const items = await openViewTypeDropdown(page)
    await expect(items).toHaveCount(5, { timeout: 5_000 })

    // Labels in spec order: 表格 / 看板 / 画廊 / 甘特 / 表单
    const labels = await items.allTextContents()
    expect(labels.map((t) => t.trim())).toEqual(['表格', '看板', '画廊', '甘特', '表单'])
  })

  test('E2: kanban/gallery/gantt/form are disabled with "规划中" tooltip', async ({ page }) => {
    await openFileDetailWithTable(page, 'E2-disabled')

    const items = await openViewTypeDropdown(page)

    // The 4 unimplemented types are disabled and carry title="规划中".
    for (const label of ['看板', '画廊', '甘特', '表单']) {
      const item = items.filter({ hasText: label })
      await expect(item).toHaveClass(/ant-dropdown-menu-item-disabled/, { timeout: 5_000 })
      await expect(item).toHaveAttribute('title', '规划中')
    }

    // grid is enabled and has no "规划中" title.
    const gridItem = items.filter({ hasText: '表格' })
    await expect(gridItem).not.toHaveClass(/ant-dropdown-menu-item-disabled/)
    await expect(gridItem).not.toHaveAttribute('title', '规划中')
  })

  test('E3: selecting grid sends POST /views with view_type=grid', async ({ page }) => {
    await openFileDetailWithTable(page, 'E3-create-grid')

    let capturedBody: { name?: string; view_type?: string } | null = null
    await page.route('**/api/v1/bitable/tables/*/views', (route) => {
      if (route.request().method() !== 'POST') return route.continue()
      capturedBody = route.request().postDataJSON()
      const name = capturedBody?.name ?? '测试视图'
      return route.fulfill({
        status: 201,
        contentType: 'application/json',
        body: JSON.stringify({
          success: true,
          view: {
            id: 'view-e3-mock',
            table_id: 'tbl-e3',
            name,
            view_type: 'grid',
            config: {},
            created_at: new Date().toISOString(),
          },
        }),
      })
    })

    const items = await openViewTypeDropdown(page)
    await items.filter({ hasText: '表格' }).click()

    // Name modal (AModal.confirm) appears — fill + confirm.
    const nameInput = page.getByPlaceholder('请输入视图名称')
    await expect(nameInput).toBeVisible({ timeout: 5_000 })
    await nameInput.fill('网格视图E3')
    await page.getByRole('button', { name: /确\s*定/ }).click()

    // Assert the POST body carried view_type=grid (no longer hardcoded elsewhere).
    await expect
      .poll(async () => capturedBody, { timeout: 5_000 })
      .toMatchObject({ name: '网格视图E3', view_type: 'grid' })
  })

  test('E4: clicking a disabled type does not open the name modal or fire POST', async ({ page }) => {
    await openFileDetailWithTable(page, 'E4-disabled-click')

    let postFired = false
    await page.route('**/api/v1/bitable/tables/*/views', (route) => {
      if (route.request().method() === 'POST') {
        postFired = true
        return route.fulfill({
          status: 201,
          contentType: 'application/json',
          body: JSON.stringify({ success: true, view: { id: 'x', table_id: 'x', name: 'x', view_type: 'kanban', config: {}, created_at: '' } }),
        })
      }
      return route.continue()
    })

    const items = await openViewTypeDropdown(page)
    // Click the disabled "看板" item — antd will not emit a click event for it.
    await items.filter({ hasText: '看板' }).click({ force: true })

    // The name modal must NOT appear, and no POST /views must have fired.
    await expect(page.getByPlaceholder('请输入视图名称')).not.toBeVisible({ timeout: 1_500 })
    expect(postFired).toBe(false)
  })

  test('E5: created grid view is added to the view tab list (round-trip contract)', async ({ page }) => {
    await openFileDetailWithTable(page, 'E5-roundtrip')

    await page.route('**/api/v1/bitable/tables/*/views', (route) => {
      if (route.request().method() !== 'POST') return route.continue()
      const body = route.request().postDataJSON() ?? {}
      return route.fulfill({
        status: 201,
        contentType: 'application/json',
        body: JSON.stringify({
          success: true,
          view: {
            id: 'view-e5-mock',
            table_id: 'tbl-e5',
            name: body.name ?? 'E5视图',
            view_type: body.view_type ?? 'grid',
            config: {},
            created_at: new Date().toISOString(),
          },
        }),
      })
    })

    const items = await openViewTypeDropdown(page)
    await items.filter({ hasText: '表格' }).click()

    const nameInput = page.getByPlaceholder('请输入视图名称')
    await expect(nameInput).toBeVisible({ timeout: 5_000 })
    await nameInput.fill('E5网格视图')
    await page.getByRole('button', { name: /确\s*定/ }).click()

    // The mock response is pushed into the store; a new tab with the view
    // name appears in the ViewSwitcher tabs.
    await expect(page.locator('.ant-tabs-tab', { hasText: 'E5网格视图' })).toBeVisible({
      timeout: 5_000,
    })
  })
})
