import { expect, test, type Locator, type Page } from '@playwright/test'
import { Ticket04Stack } from './support/ticket04-stack'

const stack = new Ticket04Stack()

test.use({ actionTimeout: 20_000 })
test.beforeAll(async () => stack.start())
test.afterAll(async () => stack.stop())

async function login(page: Page) {
  await page.goto('/login')
  await page.locator('input[autocomplete="username"]').fill('admin')
  await page.locator('input[autocomplete="current-password"]').fill('admin123')
  await page.getByRole('button', { name: '登录', exact: true }).click()
  await expect(page).toHaveURL(/\/dashboard$/)
}

async function selectOption(select: Locator, optionName: string) {
  const combobox = select.getByRole('combobox')
  await select.locator('.el-select__wrapper').click()
  const listboxId = await combobox.getAttribute('aria-controls')
  const option = select.page().locator(`#${listboxId}`).getByRole('option').filter({ hasText: optionName }).first()
  await expect(option).toBeVisible()
  await option.click()
}

async function registerDevice(
  page: Page,
  path: string,
  deviceCode: string,
  nameLabel: string,
  name: string,
) {
  await page.goto(path)
  await page.getByRole('button', { name: '选择设备' }).click()
  const candidate = page.locator('.device-table tbody tr').filter({ hasText: deviceCode })
  await expect(candidate).toBeVisible()
  await candidate.getByRole('button', { name: '选择' }).click()
  await page.locator('label').filter({ hasText: nameLabel }).locator('input').fill(name)
  if (nameLabel === '检测柜名称') {
    await page.locator('label').filter({ hasText: '规格/容量' }).locator('input').fill('120')
  }
  await page.getByRole('button', { name: '保存', exact: true }).click()
}

async function apiJson<T>(page: Page, path: string, init: RequestInit = {}) {
  return page.evaluate(async ({ requestPath, requestInit }) => {
    const token = localStorage.getItem('battery_detection_auth_token')
    const headers = new Headers(requestInit.headers)
    if (requestInit.body) headers.set('Content-Type', 'application/json')
    if (token) headers.set('Authorization', `Bearer ${token}`)
    const response = await fetch(`/dev-api${requestPath}`, { ...requestInit, headers })
    return response.json()
  }, { requestPath: path, requestInit: init }) as Promise<T>
}

test('测试创建保存必填双设备绑定、可选映射并读取电池组当前字段', async ({ page }) => {
  const suffix = Date.now()
  const cabinetName = `Ticket07检测柜${suffix}`
  const collectorName = `Ticket07采集器${suffix}`
  const testTypeName = `Ticket07类型${suffix}`
  const fieldName = `额定容量${suffix}`
  const templateName = `Ticket07模板${suffix}`
  const groupName = `Ticket07电池组${suffix}`
  const batteryNo = `BAT-${suffix}`
  const testName = `Ticket07测试${suffix}`
  const dynamicMeasurement = `壳体压力${suffix}`

  await test.step('配置模拟器并登记检测柜和六通道采集器', async () => {
    await page.goto(`${stack.backendUrl}/dev/bts-simulator.html`)
    page.once('dialog', dialog => dialog.accept())
    const reset = page.waitForResponse(response => response.url().endsWith('/dev/bts-simulator/reset') && response.ok())
    await page.getByRole('button', { name: '重置模拟器' }).click()
    await reset
    await page.locator('#new-collector-ip').fill('127.0.1.7')
    await page.locator('#new-collector-channel-count').fill('6')
    await page.locator('#add-collector').getByRole('button', { name: '添加采集器' }).click()
    await expect(page.getByRole('heading', { name: 'dev-sim 采集器 2' })).toBeVisible()

    await login(page)
    await registerDevice(page, '/device-cabinet/new', 'dev-sim-22-1', '检测柜名称', cabinetName)
    await expect(page).toHaveURL(/\/device-cabinet$/)
    await registerDevice(page, '/device-collector/new', 'dev-sim-collector-2', '采集器名称', collectorName)
    await expect(page).toHaveURL(/\/device-collector$/)
  })

  await test.step('通过正式页面创建测试类型、共享字段和电池组', async () => {
    await page.goto('/test-type/new')
    await page.getByPlaceholder('输入测试类型名称').fill(testTypeName)
    await page.getByRole('button', { name: '保存', exact: true }).click()
    await expect(page.locator('tbody tr').filter({ hasText: testTypeName })).toBeVisible()

    await page.goto('/battery-group/fields')
    await page.getByRole('button', { name: '新增字段' }).click()
    await page.locator('.field-dialog label').filter({ hasText: '字段名称' }).locator('input').fill(fieldName)
    await page.locator('.field-dialog label').filter({ hasText: '字段类型' }).locator('.el-select__wrapper').click()
    await page.getByRole('option', { name: '数字', exact: true }).click()
    await page.getByRole('button', { name: '保存', exact: true }).last().click()
    await expect(page.locator('tbody tr').filter({ hasText: fieldName })).toBeVisible()

    await page.goto('/battery-group/new')
    await page.getByRole('button', { name: '编辑当前模板' }).click()
    await page.locator('.field-checkbox-grid .el-checkbox').filter({ hasText: fieldName }).click()
    await page.getByRole('button', { name: '另存模板' }).click()
    await page.locator('.el-message-box__input input').fill(templateName)
    await page.getByRole('button', { name: '保存', exact: true }).last().click()
    await page.locator('label').filter({ hasText: '电池组名称' }).locator('input').fill(groupName)
    await page.locator('label').filter({ hasText: '型号' }).locator('input').fill('TICKET07')
    await page.locator('label').filter({ hasText: '目的' }).locator('textarea').fill('验证测试设备绑定')
    await page.locator('.battery-row input').first().fill(batteryNo)
    await page.getByPlaceholder(`输入${fieldName}`).fill('72')
    await page.getByRole('button', { name: '保存新建电池组', exact: true }).click()
    await expect(page.locator('tbody tr').filter({ hasText: groupName })).toBeVisible()
  })

  await test.step('缺少必填映射不能保存，完整映射可创建', async () => {
    await page.goto('/test-list/new')
    await page.getByPlaceholder('输入测试名称').fill(testName)
    await selectOption(page.locator('.parameter-grid label').filter({ hasText: '测试负责人' }).locator('.el-select'), '若依')
    await selectOption(page.locator('.parameter-grid label').filter({ hasText: '测试类型' }).locator('.el-select'), testTypeName)
    await page.getByPlaceholder('输入测试目的').fill('验证 Ticket07 必填双设备绑定')

    await page.getByRole('button', { name: '添加电池' }).click()
    await selectOption(page.locator('.battery-group-search .el-select'), groupName)
    await page.getByLabel(`选择电池 ${batteryNo}`).click()
    await page.locator('.battery-dialog').getByRole('button', { name: '提交', exact: true }).click()
    await expect(page.locator('.battery-dialog')).toBeHidden()

    const row = page.locator('.battery-table tbody tr').first()
    const selects = row.locator('.el-select')
    await selectOption(selects.nth(0), cabinetName)
    const channelCombobox = selects.nth(1).getByRole('combobox')
    await selects.nth(1).locator('.el-select__wrapper').click()
    const channelListboxId = await channelCombobox.getAttribute('aria-controls')
    const cabinetChannel = page.locator(`#${channelListboxId}`).getByRole('option').first()
    await expect(cabinetChannel).toBeVisible()
    await cabinetChannel.click()
    await selectOption(selects.nth(2), collectorName)
    await selectOption(selects.nth(3), 'S-01')
    await selectOption(selects.nth(6), 'S-02')
    await page.getByRole('button', { name: '提交测试', exact: true }).click()
    await expect(page.getByRole('alert').filter({ hasText: '第 1 行请选择负电位主通道' })).toBeVisible()

    await selectOption(selects.nth(4), 'S-04')
    await selectOption(selects.nth(8), 'S-03')
    await page.getByRole('button', { name: '添加项' }).click()
    await page.locator('.el-message-box__input input').fill(dynamicMeasurement)
    await page.getByRole('button', { name: '添加', exact: true }).click()
    await selectOption(row.locator('.el-select').last(), 'S-05')

    const thresholdRows = page.locator('.threshold-table tbody tr')
    await thresholdRows.nth(0).locator('input[type="number"]').nth(0).fill('0')
    await thresholdRows.nth(0).locator('input[type="number"]').nth(1).fill('80')
    await thresholdRows.nth(1).locator('input[type="number"]').nth(0).fill('2.5')
    await thresholdRows.nth(1).locator('input[type="number"]').nth(1).fill('4.3')
    await page.getByRole('button', { name: '提交测试', exact: true }).click()
    await expect(page).toHaveURL(/\/test-list$/)
    const listRow = page.locator('tbody tr').filter({ hasText: testName })
    await expect(listRow).toContainText(batteryNo)
    await expect(listRow).toContainText('待测试')
  })

  let testId = 0
  await test.step('编辑与刷新从后端恢复全部映射', async () => {
    const listRow = page.locator('tbody tr').filter({ hasText: testName })
    testId = Number(((await listRow.locator('.id-cell').textContent()) || '').replace('#', ''))
    await listRow.getByRole('button', { name: '编辑' }).click()
    await expect(page.getByPlaceholder('输入测试名称')).toHaveValue(testName)
    const row = page.locator('.battery-table tbody tr').first()
    await expect(page.locator('.battery-table .custom-channel-header')).toContainText(dynamicMeasurement)
    await expect(row.locator('.el-select').nth(3)).toContainText('S-01')
    await expect(row.locator('.el-select').nth(4)).toContainText('S-04')
    await expect(row.locator('.el-select').nth(6)).toContainText('S-02')
    await expect(row.locator('.el-select').nth(8)).toContainText('S-03')
    await expect(row.locator('.el-select').last()).toContainText('S-05')
    await page.reload()
    await expect(page.getByPlaceholder('输入测试名称')).toHaveValue(testName)
    await expect(page.locator('.battery-table tbody tr').first().locator('.el-select').last()).toContainText('S-05')
  })

  await test.step('后端拒绝重复映射并按测试电池读取共享字段当前值', async () => {
    const detail = await apiJson<{ code: number; data: Record<string, any> }>(page, `/system/test/${testId}`)
    expect(detail.code).toBe(200)
    expect(detail.data.batteries.find((battery: Record<string, any>) => battery.batteryNo === batteryNo)).toMatchObject({
      batteryNo, fields: expect.arrayContaining([expect.objectContaining({ fieldName, fieldValue: '72' })]),
    })

    const duplicatePayload = {
      ...detail.data,
      testId: undefined,
      testName: `${testName}-重复映射`,
      batteries: detail.data.batteries.map((battery: Record<string, any>) => ({
        ...battery,
        collectorChannels: battery.collectorChannels.map((mapping: Record<string, any>, index: number) => ({
          ...mapping,
          channelCode: index === 1 ? battery.collectorChannels[0].channelCode : mapping.channelCode,
        })),
      })),
    }
    const rejected = await apiJson<{ code: number; msg: string }>(page, '/system/test', {
      method: 'POST', body: JSON.stringify(duplicatePayload),
    })
    expect(rejected.code).not.toBe(200)
    expect(rejected.msg).toContain('同一采集器通道不可重复映射')

    await page.goto('/battery-group')
    await page.locator('tbody tr').filter({ hasText: groupName }).getByRole('button', { name: '编辑' }).click()
    await page.getByPlaceholder(`输入${fieldName}`).fill('99')
    await page.getByRole('button', { name: '保存编辑电池组', exact: true }).click()
    await expect(page).toHaveURL(/\/battery-group$/)
    const afterGroupChange = await apiJson<{ code: number; data: Record<string, any> }>(page, `/system/test/${testId}`)
    expect(afterGroupChange.code).toBe(200)
    expect(afterGroupChange.data.batteries.find((battery: Record<string, any>) => battery.batteryNo === batteryNo)).toMatchObject({
      batteryNo, fields: expect.arrayContaining([expect.objectContaining({ fieldName, fieldValue: '99' })]),
    })
  })

  await test.step('未开始接收的测试可确认删除并在刷新后保持消失', async () => {
    await page.goto('/test-list')
    const listRow = page.locator('tbody tr').filter({ hasText: testName })
    await expect(listRow).toBeVisible()
    await listRow.getByRole('button', { name: '删除', exact: true }).click()
    await expect(page.getByRole('dialog', { name: '确认删除测试' })).toBeVisible()
    await page.getByRole('button', { name: '删除', exact: true }).last().click()
    await expect(page.getByRole('alert').filter({ hasText: '测试已删除' })).toBeVisible()
    await expect(listRow).toBeHidden()
    await page.reload()
    await expect(page.locator('tbody tr').filter({ hasText: testName })).toHaveCount(0)
  })

  const businessKeys = await page.evaluate(() => Object.keys(localStorage)
    .filter(key => key.includes('battery_detection_tests')))
  expect(businessKeys).toEqual([])
})
