import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'
import { join } from 'node:path'

const root = process.cwd()
const scanRoots = ['web/src', 'miniapp/src', 'backend/src', 'backend/scripts', 'backend/db/migrations', 'deploy']
const forbiddenFiles = [
  'miniapp/src/dlbdi-logo.jpg',
  'miniapp/src/assets/dlbdi-logo-transparent.png',
  'miniapp/src/assets/._dlbdi-logo-transparent.png',
]
const allowedDemoCodeFiles = new Set([
  'web/src/lib/backend.ts',
  'miniapp/src/api/backend.ts',
  'backend/src/lite/business-pg.mjs',
])
const allowedComponentDataUrlFiles = new Set([
  'miniapp/src/pages/account/index.tsx',
])

const checks = [
  {
    name: 'dev-admin-token 字面量',
    pattern: /dev-admin-token/g,
    applies: () => true,
  },
  {
    name: '前端 demoCode 自动填充',
    pattern: /(if\s*\([^)\n]*demoCode|set[A-Za-z0-9_]*\([^)\n]*demoCode)/g,
    applies: (file) => file.startsWith('web/src/') || file.startsWith('miniapp/src/'),
  },
  {
    name: '用户可见待上传占位',
    pattern: /后台待上传|待上传|封面待上传|此处应放|请在管理端上传|暂无封面/g,
    applies: (file) => file.startsWith('web/src/') || file.startsWith('miniapp/src/'),
  },
  {
    name: '演示账号文案',
    pattern: /本地演示账号|演示账号|演示密码|demo\s*account/gi,
    applies: (file) => file.startsWith('web/src/') || file.startsWith('miniapp/src/') || file.startsWith('backend/src/'),
  },
  {
    name: '测试商品文案',
    pattern: /测试商品|测试智能体|测试报告/g,
    applies: (file) => file.startsWith('web/src/') || file.startsWith('miniapp/src/') || file.startsWith('backend/src/'),
  },
  {
    name: '生产页面 mock/fallback 轮播',
    pattern: /fallbackSlides|fallback-home-hero|home-hero-tech/g,
    applies: (file) => file.startsWith('web/src/') || file.startsWith('miniapp/src/'),
  },
  {
    name: '小程序本地打包品牌 logo',
    pattern: /@\/assets\/dlbdi-logo|dlbdi-logo-transparent|src\/dlbdi-logo\.jpg|import\s+\w+\s+from\s+['"][^'"]*dlbdi-logo/gi,
    applies: (file) => file.startsWith('miniapp/src/'),
  },
  {
    name: '生产源码内嵌文件 data/base64',
    pattern: /data:image|;base64,|brandLogoBase64|\/9j\/|content_base64\s*=\s*'[A-Za-z0-9+/=]{120,}'/g,
    applies: (file) => file.startsWith('web/src/')
      || file.startsWith('miniapp/src/')
      || file.startsWith('backend/src/')
      || file.startsWith('backend/scripts/')
      || file.startsWith('backend/db/migrations/'),
  },
]

function listFiles(dir, files = []) {
  for (const name of readdirSync(join(root, dir))) {
    if (name.startsWith('._')) continue
    const relative = join(dir, name)
    const absolute = join(root, relative)
    const stat = statSync(absolute)
    if (stat.isDirectory()) {
      if (['node_modules', '.next', 'dist', 'build', 'coverage'].includes(name)) continue
      listFiles(relative, files)
    } else if (/\.(ts|tsx|js|jsx|mjs|css|json|ya?ml|Caddyfile)$/.test(name) || name === 'Caddyfile') {
      files.push(relative)
    }
  }
  return files
}

const findings = []

for (const file of forbiddenFiles) {
  if (existsSync(join(root, file))) {
    findings.push({ file, line: 1, name: '禁止保留小程序本地品牌 logo 文件' })
  }
}

for (const dir of scanRoots) {
  for (const file of listFiles(dir)) {
    const normalized = file.replaceAll('\\', '/')
    const text = readFileSync(join(root, file), 'utf8')
    for (const check of checks) {
      if (!check.applies(normalized)) continue
      if (check.name.includes('demoCode') && allowedDemoCodeFiles.has(normalized)) continue
      if (check.name.includes('data/base64') && allowedComponentDataUrlFiles.has(normalized)) continue
      for (const match of text.matchAll(check.pattern)) {
        const line = text.slice(0, match.index).split('\n').length
        findings.push({ file: normalized, line, name: check.name })
      }
    }
  }
}

if (findings.length) {
  console.error('生产 sentinel 扫描失败：')
  for (const item of findings) {
    console.error(`- ${item.file}:${item.line} ${item.name}`)
  }
  process.exit(1)
}

console.log('生产 sentinel 扫描通过：未发现用户可见 mock/开发口径残留。')
