import assert from 'node:assert/strict'
import { readFile, mkdir } from 'node:fs/promises'
import { extname, resolve } from 'node:path'
import { chromium, expect } from '@playwright/test'

// 使用构建产物和浏览器请求接缝验证交互；不启动服务、不访问开发数据库。
const dist = resolve(import.meta.dirname, '../dist')
const artifacts = resolve(import.meta.dirname, 'e2e/.artifacts/report-test-detail')
await mkdir(artifacts, { recursive: true })
const browser = await chromium.launch({ channel: 'chrome', headless: true })
const source = id => ({ selection: { testId: id, testBatteryId: id, cycleNo: 1, stepNo: 1, logicalNo: null },
  name: `测试${id}`, type: '化成', batteryGroup: `组${id}`, group: `组${id}`, groupId: id, batteryNo: '同名电池', process: '1-充电', startedAt: 1,
  consoleDetail: { experimentName: '控制台实验', batteryFields: ['额定容量'], specialFields: ['待删除'], fieldValues: { 额定容量: { 同名电池: `来源${id}` }, 待删除: { 同名电池: '旧值' } } } })
function fixture(type) {
  const sources = [source(1), source(2)]
  const sourceDetailOverrides = Object.fromEntries(sources.map(source => [`${source.selection.testId}|${source.selection.testBatteryId}`, {
    batteryFields: ['额定容量'], specialFields: ['待删除'], fieldValues: { 'battery|额定容量': source.selection.testId === 1 ? '' : '报表乙', 'special|待删除': '独立值' },
  }]))
  const settings = { metrics: [], seriesColors: type === 'formation' ? {} : ['#3978f6', '#109854'], sourceDetailOverrides,
    fields: [{ key: 'chargeCapacity', scope: 'cycle', kind: 'metric', displayName: '充电容量', unit: 'mAh' }],
    values: {}, chargeStepSelections: {}, batteryTemplateFields: [], batteryFields: [], specialFields: [], fieldStructure: [], detailFieldValues: {} }
  const items = sources.map(source => ({ source, records: [], zeroTime: 1000, logicalMapping: null, businessFields: [], cycleNos: [], cycles: [], metrics: {}, chargeSteps: {}, curves: {}, stepCurves: {} }))
  const segments = sources.map(source => ({ source, offset: 0, originalCycleNos: [], statistics: {}, businessFields: [],
    consoleDetail: { ...source.consoleDetail, fieldValues: { 额定容量: `来源${source.selection.testId}`, 待删除: '旧值' } } }))
  return { id: 7, name: `${type}详情`, description: '', versionNo: 1, creator: '操作员', operatorName: '操作员', createdAt: 1, versionCreatedAt: 1, changeSummary: '首次保存',
    settings, items, segments, cycleNos: [], curveMode: 'actual-steps', spliceMode: 'battery-segments', units: {}, displayUnits: {} }
}
try {
  for (const type of ['compare', 'formation', 'splice']) {
    const context = await browser.newContext({ viewport: { width: 1440, height: 1000 } })
    await context.addInitScript(() => { localStorage.setItem('battery_detection_auth_token', 'ui-fixture'); window.print = () => {} })
    let saved = fixture(type), submitted, failSave = true
    const requests = [], errors = []
    await context.route('http://report.test/**', async route => {
      const url = new URL(route.request().url()), path = url.pathname
      if (path.startsWith('/dev-api')) {
        requests.push({ path, method: route.request().method() })
        let response = { code: 200, msg: '成功', rows: [], total: 0 }
        if (path.endsWith('/getInfo')) Object.assign(response, { user: { userName: 'admin', nickName: '操作员' }, roles: ['admin'], permissions: ['*:*:*'] })
        else if (path.includes('/batteryGroupField/')) response.rows = [{ fieldId: 1, fieldName: '额定容量', fieldType: 'text', status: '0' }, { fieldId: 2, fieldName: '质量', fieldType: 'text', status: '0' }]
        else if (path.includes(`/${type}-report/7`)) {
          if (route.request().method() === 'POST') {
            if (failSave) { failSave = false; response = { code: 500, msg: '测试保存失败' } }
            else { submitted = route.request().postDataJSON(); saved = { ...saved, ...submitted, versionNo: saved.versionNo + 1 }; response.data = saved }
          } else response.data = saved
        }
        await route.fulfill({ json: response }); return
      }
      const file = path.startsWith('/assets/') ? resolve(dist, '.' + path) : resolve(dist, 'index.html')
      assert.ok(file.startsWith(dist))
      await route.fulfill({ body: await readFile(file), contentType: { '.html': 'text/html', '.js': 'text/javascript', '.css': 'text/css', '.svg': 'image/svg+xml' }[extname(file)] || 'application/octet-stream' })
    })
    const page = await context.newPage()
    page.on('pageerror', error => errors.push(error.message))
    await page.goto(`http://report.test/report-${type}/7/edit?mode=edit`)
    const detail = page.locator('.report-test-detail')
    await expect(detail).toBeVisible()
    await expect(detail.getByText('实验', { exact: true })).toHaveCount(0)
    await expect(detail.getByRole('button', { name: /保存/ })).toHaveCount(0)
    await expect(detail.getByRole('button', { name: '字段设置', exact: true })).toHaveCount(0)
    const row = name => detail.locator('.source-detail-row').filter({ has: page.locator('td:first-child', { hasText: name }) })
    await expect(row('额定容量').locator('input').nth(0)).toHaveValue('')
    await expect(row('额定容量').locator('input').nth(1)).toHaveValue('报表乙')
    await row('待删除').getByRole('button', { name: '删除', exact: true }).click()
    await expect(row('待删除')).toHaveCount(0)
    await detail.getByRole('button', { name: '添加项', exact: true }).click()
    await page.getByRole('dialog', { name: '添加项' }).getByRole('textbox').fill('新字段')
    await page.getByRole('button', { name: '添加', exact: true }).click()
    await row('新字段').locator('input').nth(0).fill('甲填写')
    await row('新字段').locator('input').nth(1).fill('乙填写')
    await detail.getByRole('button', { name: '电池组字段模板', exact: true }).click()
    let dialog = page.getByRole('dialog', { name: '选择电池组字段模板' })
    await dialog.locator('.el-checkbox').filter({ hasText: '额定容量' }).click()
    await dialog.getByRole('button', { name: '取消', exact: true }).click()
    await expect(row('额定容量')).toHaveCount(1)
    await detail.getByRole('button', { name: '电池组字段模板', exact: true }).click()
    await dialog.locator('.el-checkbox').filter({ hasText: '质量' }).click()
    await dialog.getByRole('button', { name: '应用模板', exact: true }).click()
    await expect(dialog).not.toBeVisible()
    await expect(row('额定容量').locator('input').nth(1)).toHaveValue('报表乙')
    await expect(row('新字段').locator('input').nth(0)).toHaveValue('甲填写')
    await detail.getByRole('button', { name: '选择特殊字段模板', exact: true }).click()
    dialog = page.getByRole('dialog', { name: '选择特殊字段模板' })
    await dialog.locator('.el-radio').filter({ hasText: '热特性模板' }).click()
    await dialog.getByRole('button', { name: '应用模板', exact: true }).click()
    await expect(dialog).not.toBeVisible()
    await expect(row('新字段')).toHaveCount(0)
    await row('最高温度').locator('input').nth(0).fill('报表最高温度')
    await row('最高温度').locator('input').nth(1).fill('清空前')
    await row('最高温度').locator('input').nth(1).fill('')
    await page.evaluate(() => window.scrollTo(0, 0))
    await page.screenshot({ path: resolve(artifacts, `${type}-desktop.png`), fullPage: true, animations: 'disabled' })
    await page.setViewportSize({ width: 390, height: 844 })
    await expect(detail.getByRole('button', { name: '选择特殊字段模板', exact: true })).toBeVisible()
    await expect.poll(async () => (await page.locator('.app-main').boundingBox()).x).toBe(0)
    await page.screenshot({ path: resolve(artifacts, `${type}-mobile.png`), fullPage: true, animations: 'disabled' })
    await page.setViewportSize({ width: 1440, height: 1000 })
    if (type !== 'splice') await page.getByPlaceholder('请输入修改说明', { exact: true }).fill('统一详情')
    const save = page.getByRole('button', { name: type === 'splice' ? '保存修改' : '保存新版本', exact: true })
    await save.click()
    await expect(page.getByText('测试保存失败', { exact: true })).toBeVisible()
    await expect(row('最高温度').locator('input').nth(0)).toHaveValue('报表最高温度')
    await save.click()
    await expect.poll(() => submitted).toBeTruthy()
    assert.deepEqual(submitted.settings.sourceDetailOverrides['1|1'], { batteryFields: ['额定容量', '质量'], specialFields: ['最高温度', '温差', '温升速率'], fieldValues: { 'battery|额定容量': '', 'special|最高温度': '报表最高温度' } })
    assert.equal(submitted.settings.sourceDetailOverrides['2|2'].fieldValues['special|最高温度'], '')
    if (type === 'formation') assert.ok(submitted.settings.fields.every(field => field.scope === 'cycle'))
    else assert.deepEqual(submitted.settings.fieldStructure, [])
    await page.goto(`http://report.test/report-${type}/7/edit?mode=edit`)
    await expect(row('待删除')).toHaveCount(0)
    await expect(row('新字段')).toHaveCount(0)
    await expect(row('最高温度').locator('input').nth(0)).toHaveValue('报表最高温度')
    const popupPromise = page.waitForEvent('popup')
    await page.getByRole('button', { name: '打印 / 另存 PDF', exact: true }).click()
    const popup = await popupPromise
    await expect(popup.locator('.source-detail-table')).toBeVisible()
    await expect(popup.locator('body')).toContainText('报表最高温度')
    await expect(popup.locator('body')).not.toContainText('控制台实验')
    await expect(popup.locator('body')).not.toContainText('报表字段')
    await expect(popup.locator('body')).not.toContainText('待删除')
    assert.ok(!requests.some(request => request.path.includes('console-detail')))
    assert.deepEqual(errors, [])
    await context.close()
    console.log(`${type}: 详情添加/删除、模板取消/应用、保存失败/成功、重开与打印通过`)
  }
} finally { await browser.close() }
