import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
import { fileURLToPath } from 'node:url'
import path from 'node:path'
import { parse as parseTemplate } from '@vue/compiler-dom'
import { parse as parseSfc } from '@vue/compiler-sfc'

const scriptDir = path.dirname(fileURLToPath(import.meta.url))
const [globalStyles, routerSource, mainLayoutSource, trainingLogSource, trainingOverviewSource, fineTuneCreateSource] = await Promise.all([
  readFile(path.resolve(scriptDir, '../src/styles/index.scss'), 'utf8'),
  readFile(path.resolve(scriptDir, '../src/router/index.ts'), 'utf8'),
  readFile(path.resolve(scriptDir, '../src/layouts/MainLayout.vue'), 'utf8'),
  readFile(path.resolve(scriptDir, '../src/views/system/TrainingLogView.vue'), 'utf8'),
  readFile(path.resolve(scriptDir, '../src/views/system/training-log/TrainingTaskOverview.vue'), 'utf8'),
  readFile(path.resolve(scriptDir, '../src/views/fine-tune/FineTuneCreateView.vue'), 'utf8'),
])

function extractCssBlock(css, marker) {
  const markerIndex = css.indexOf(marker)
  assert.notEqual(markerIndex, -1, `未找到样式规则：${marker}`)
  const openBrace = css.indexOf('{', markerIndex)
  assert.notEqual(openBrace, -1, `样式规则缺少左花括号：${marker}`)

  let depth = 0
  for (let index = openBrace; index < css.length; index += 1) {
    if (css[index] === '{') depth += 1
    if (css[index] === '}') depth -= 1
    if (depth === 0) return css.slice(openBrace + 1, index)
  }

  assert.fail(`样式规则缺少右花括号：${marker}`)
}

function findElements(node, predicate, result = []) {
  if (node?.type === 1 && predicate(node)) result.push(node)
  for (const child of node?.children || []) findElements(child, predicate, result)
  return result
}

function staticAttribute(node, name) {
  const prop = node.props.find((item) => item.type === 6 && item.name === name)
  return prop?.value?.content
}

function boundAttribute(node, name) {
  const prop = node.props.find(
    (item) => item.type === 7 && item.name === 'bind' && item.arg?.content === name,
  )
  return prop?.exp?.content
}

function extractRouteBlock(source, routePath) {
  const pathPattern = new RegExp(`path:\\s*['"]${routePath.replaceAll('/', '\\/')}['"]`)
  const pathIndex = source.search(pathPattern)
  assert.notEqual(pathIndex, -1, `未找到路由 /${routePath}`)

  const openBrace = source.lastIndexOf('{', pathIndex)
  assert.notEqual(openBrace, -1, `路由 /${routePath} 缺少左花括号`)

  let depth = 0
  for (let index = openBrace; index < source.length; index += 1) {
    if (source[index] === '{') depth += 1
    if (source[index] === '}') depth -= 1
    if (depth === 0) return source.slice(openBrace + 1, index)
  }

  assert.fail(`路由 /${routePath} 缺少右花括号`)
}

const selfSurfaceRoutes = [
  'fine-tune',
  'model-eval',
  'model-inference',
  'model-inference/chat/:id',
  'model-manage',
  'data-process',
  'data-process/create',
  'dataset',
  'user-settings',
]
for (const routePath of selfSurfaceRoutes) {
  const routeBlock = extractRouteBlock(routerSource, routePath)
  assert.match(
    routeBlock,
    /meta:\s*\{[^}]*pageSurface:\s*['"]self['"][^}]*\}/,
    `列表路由 /${routePath} 未声明 pageSurface: 'self'`,
  )
}

assert.equal(
  routerSource.match(/pageSurface:\s*['"]self['"]/g)?.length,
  selfSurfaceRoutes.length,
  "只能给指定的自带白色表面的列表路由声明 pageSurface: 'self'",
)

const rootBlock = extractCssBlock(globalStyles, ':root')
assert.match(rootBlock, /--app-shell-bg:\s*#f3f5f8;/, '全局样式缺少灰色外层背景 token')
assert.match(rootBlock, /--app-page-bg:\s*#ffffff;/, '全局样式缺少白色页面画布 token')
assert.match(rootBlock, /--app-surface-bg:\s*#ffffff;/, '全局样式缺少统一内容表面 token')

const bodyBlock = extractCssBlock(globalStyles, '\nbody {')
assert.match(bodyBlock, /background-color:\s*var\(--app-shell-bg\);/, 'body 未使用灰色外层背景')

const cardBlock = extractCssBlock(globalStyles, '.el-card {')
assert.match(cardBlock, /background-color:\s*var\(--app-surface-bg\);/, '全局卡片未使用统一内容表面')

const mainLayoutDescriptor = parseSfc(mainLayoutSource).descriptor
const mainLayoutScript = mainLayoutDescriptor.scriptSetup?.content || ''
const mainLayoutStyle = mainLayoutDescriptor.styles.map((item) => item.content).join('\n')
const mainLayoutTemplate = mainLayoutDescriptor.template?.content || ''
const mainLayoutAst = parseTemplate(mainLayoutTemplate)

assert.match(mainLayoutScript, /import\s*\{[^}]*\buseRoute\b[^}]*\}\s*from\s*['"]vue-router['"]/, '主布局未引入 useRoute')
assert.match(mainLayoutScript, /const\s+route\s*=\s*useRoute\(\)/, '主布局未获取当前路由')

const pageCanvases = findElements(
  mainLayoutAst,
  (node) => staticAttribute(node, 'class')?.split(/\s+/).includes('page-canvas'),
)
assert.equal(pageCanvases.length, 1, '主布局必须且只能提供一个全局白色页面画布')
assert.match(
  boundAttribute(pageCanvases[0], 'class') || '',
  /['"]is-self-surface['"]\s*:\s*route\.meta\.pageSurface\s*===\s*['"]self['"]/,
  '主布局未根据 route.meta.pageSurface 为页面画布添加 is-self-surface 类',
)
assert.equal(
  findElements(pageCanvases[0], (node) => node.tag === 'router-view').length,
  1,
  '所有业务路由必须渲染在全局页面画布内部',
)
assert.equal(
  findElements(mainLayoutAst, (node) => node.tag.toLowerCase() === 'transition').length,
  0,
  '主布局仍包含会造成列表与二级页面表面错位的页面级 Transition',
)
assert.doesNotMatch(
  mainLayoutStyle,
  /\.fade-(?:enter|leave)-(?:active|from|to)/,
  '主布局仍包含页面级透明度转场样式',
)

const layoutContentBlock = extractCssBlock(mainLayoutStyle, '.layout-content')
assert.match(layoutContentBlock, /background-color:\s*var\(--app-shell-bg\);/, '主内容区未使用灰色外层背景')

const pageCanvasBlock = extractCssBlock(mainLayoutStyle, '\n.page-canvas {')
assert.match(pageCanvasBlock, /flex:\s*1 0 auto;/, '白色页面画布没有铺满可用高度')
assert.match(pageCanvasBlock, /padding:\s*24px;/, '白色页面画布缺少统一内容内边距')
assert.match(pageCanvasBlock, /border-radius:\s*16px;/, '白色页面画布圆角与参考不一致')
assert.match(pageCanvasBlock, /background-color:\s*var\(--app-page-bg\);/, '全局页面画布未使用白色背景')

const selfSurfaceBlock = extractCssBlock(mainLayoutStyle, '.page-canvas.is-self-surface')
assert.match(selfSurfaceBlock, /padding:\s*0;/, '自带表面的页面仍保留全局画布内边距')
assert.match(selfSurfaceBlock, /border-radius:\s*0;/, '自带表面的页面仍保留全局画布圆角')
assert.match(selfSurfaceBlock, /background-color:\s*transparent;/, '自带表面的页面未透出灰色应用背景')
assert.match(selfSurfaceBlock, /box-shadow:\s*none;/, '自带表面的页面仍保留全局画布阴影')

assert.match(
  mainLayoutStyle,
  /\.page-canvas:not\(\.is-self-surface\)\s*>\s*:deep\(\.page-card-host\s*>\s*\.page-card\)/,
  '显式根卡片宿主内的 PageCard 未被识别为页面根卡片',
)
assert.doesNotMatch(
  mainLayoutStyle,
  /:deep\(\*\s*>\s*\.page-card\)/,
  '通用层级选择器会误伤训练日志等页面的内部业务卡片',
)
assert.match(
  fineTuneCreateSource,
  /class=["'][^"']*\bfine-tune-create\b[^"']*\bpage-card-host\b[^"']*["']/,
  '创建训练任务页未显式标记根 PageCard 宿主',
)
const rootPageCardBlock = extractCssBlock(
  mainLayoutStyle,
  '.page-canvas:not(.is-self-surface) > :deep(.page-card)',
)
assert.match(rootPageCardBlock, /margin-bottom:\s*0;/, '页面根卡片仍在白色画布内保留额外外边距')
assert.match(rootPageCardBlock, /background-color:\s*transparent;/, '页面根卡片仍形成第二层白色背景')
assert.match(rootPageCardBlock, /border-radius:\s*0\s*!important;/, '页面根卡片仍形成第二层圆角边界')
assert.match(rootPageCardBlock, /box-shadow:\s*none\s*!important;/, '页面根卡片仍形成重复卡片层级')

const trainingLogStyle = [trainingLogSource, trainingOverviewSource]
  .flatMap((source) => parseSfc(source).descriptor.styles.map((item) => item.content))
  .join('\n')
const businessSurfaceBlock = extractCssBlock(trainingLogStyle, '.task-overview')
assert.match(
  businessSurfaceBlock,
  /background:\s*var\(--app-surface-bg\);/,
  '任务、数据集和运行概况未使用统一白色内容表面',
)

console.log('全局页面背景与内容表面回归检查通过')
