import assert from 'node:assert/strict'
import { readdirSync, readFileSync } from 'node:fs'
import { join, relative } from 'node:path'
import test from 'node:test'

const MAX_SOURCE_UNIT_LINES = 800
const REPO_ROOT = new URL('../..', import.meta.url).pathname

const FRONTEND_SOURCE_ROOTS = [
  'web/src/components',
  'web/src/composables',
  'web/src/utils',
  'web/src/views',
]

const FRONTEND_EXTENSIONS = new Set(['.html', '.js', '.mjs', '.ts', '.tsx', '.vue'])

function extensionOf(path) {
  const index = path.lastIndexOf('.')
  return index === -1 ? '' : path.slice(index)
}

function countLines(source) {
  return source.length === 0 ? 0 : source.split('\n').length
}

function walkFiles(root, shouldInclude) {
  const result = []
  const stack = [root]

  while (stack.length > 0) {
    const current = stack.pop()
    const entries = readdirSync(current, { withFileTypes: true })

    for (const entry of entries) {
      const path = join(current, entry.name)
      if (entry.isDirectory()) {
        if (!['node_modules', 'dist', 'build', 'coverage'].includes(entry.name)) {
          stack.push(path)
        }
        continue
      }

      if (shouldInclude(path)) {
        result.push(path)
      }
    }
  }

  return result
}

function listOversizedFrontendUnits() {
  return FRONTEND_SOURCE_ROOTS.flatMap((root) => {
    const absoluteRoot = join(REPO_ROOT, root)
    return walkFiles(
      absoluteRoot,
      (path) => FRONTEND_EXTENSIONS.has(extensionOf(path))
    )
  })
    .map((path) => ({
      path: relative(REPO_ROOT, path),
      lines: countLines(readFileSync(path, 'utf8')),
    }))
    .filter((item) => item.lines > MAX_SOURCE_UNIT_LINES)
    .sort((left, right) => right.lines - left.lines)
}

test('前端核心组件和模块不超过 800 行', () => {
  const oversized = listOversizedFrontendUnits()

  assert.deepEqual(
    oversized,
    [],
    `以下前端核心源文件超过 ${MAX_SOURCE_UNIT_LINES} 行：\n${
      oversized.map((item) => `- ${item.path}: ${item.lines}`).join('\n')
    }`
  )
})
