import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const repoRoot = path.resolve(__dirname, '..')
const decoder = new TextDecoder('utf-8', { fatal: true })

const textExtensions = new Set([
  '.js',
  '.cjs',
  '.mjs',
  '.ts',
  '.tsx',
  '.json',
  '.jsonc',
  '.css',
  '.less',
  '.scss',
  '.vue',
  '.html',
  '.htm',
  '.md',
  '.mdx',
  '.yml',
  '.yaml',
  '.svg'
])

const textFileNames = new Set([
  'Dockerfile',
  'AGENTS.md',
  'CLAUDE.md',
  'LICENSE',
  '.env',
  '.env.development',
  '.env.production',
  '.gitignore',
  '.gitattributes',
  '.prettierignore',
  '.prettierrc',
  '.stylelintignore',
  '.stylelintrc.cjs'
])

const ignoredDirs = new Set(['.git', 'node_modules', 'dist'])

function isTextFile(filePath) {
  const baseName = path.basename(filePath)

  if (textFileNames.has(baseName)) {
    return true
  }

  return textExtensions.has(path.extname(baseName).toLowerCase())
}

function collectFiles(dirPath) {
  const files = []

  for (const entry of fs.readdirSync(dirPath, { withFileTypes: true })) {
    if (entry.isDirectory()) {
      if (ignoredDirs.has(entry.name)) {
        continue
      }

      files.push(...collectFiles(path.join(dirPath, entry.name)))
      continue
    }

    const fullPath = path.join(dirPath, entry.name)

    if (isTextFile(fullPath)) {
      files.push(fullPath)
    }
  }

  return files
}

function toAbsolutePath(targetPath) {
  if (path.isAbsolute(targetPath)) {
    return targetPath
  }

  return path.resolve(process.cwd(), targetPath)
}

function describeIssue(relativePath, message) {
  return `${relativePath}: ${message}`
}

function checkFile(filePath) {
  const relativePath = path.relative(repoRoot, filePath)
  const buffer = fs.readFileSync(filePath)

  try {
    const text = decoder.decode(buffer)

    if (text.includes('\uFFFD')) {
      return describeIssue(relativePath, '包含 Unicode 替换字符，疑似已出现乱码')
    }

    return null
  }
  catch {
    return describeIssue(relativePath, '不是有效的 UTF-8 编码')
  }
}

const inputFiles = process.argv.slice(2)
const filesToCheck = inputFiles.length > 0
  ? inputFiles
    .map(toAbsolutePath)
    .filter((filePath) => fs.existsSync(filePath) && fs.statSync(filePath).isFile())
  : collectFiles(repoRoot)

const issues = filesToCheck
  .filter(isTextFile)
  .map(checkFile)
  .filter(Boolean)

if (issues.length > 0) {
  console.error('检测到编码问题：')
  for (const issue of issues) {
    console.error(`- ${issue}`)
  }
  process.exit(1)
}

console.log(`编码检查通过，共检查 ${filesToCheck.length} 个文件。`)
