import { createServer } from 'node:http'
import { createHash, randomUUID } from 'node:crypto'

const PORT = Number(process.env.PLAYWRIGHT_MOCK_PORT || 5159)
const FRONTEND_ORIGIN =
  process.env.PLAYWRIGHT_FRONTEND_ORIGIN || 'http://127.0.0.1:4321'
const ADMIN_ORIGIN =
  process.env.PLAYWRIGHT_ADMIN_ORIGIN || 'http://127.0.0.1:4322'
const MOCK_ORIGIN = `http://127.0.0.1:${PORT}`
const SESSION_COOKIE = 'termi_admin_session'
const SESSION_VALUE = 'mock-admin-session'
const CONTENT_TYPES = {
  json: 'application/json; charset=utf-8',
  text: 'text/plain; charset=utf-8',
  sse: 'text/event-stream; charset=utf-8',
  svg: 'image/svg+xml; charset=utf-8',
}
const VALID_LOGIN = {
  username: 'admin',
  password: 'admin123',
}
const BASE_TS = Date.parse('2026-04-01T09:00:00.000Z')

const categoryCatalog = [
  {
    name: '前端工程',
    slug: 'frontend-engineering',
    description: '围绕 Astro、Svelte、设计系统与终端风格交互的实现记录。',
    cover_image: `${MOCK_ORIGIN}/media-files/category-frontend.svg`,
    accent_color: '#2563eb',
    seo_title: '前端工程专题',
    seo_description: 'Astro / Svelte / 终端风格 UI 相关的实现与复盘。',
  },
  {
    name: '测试体系',
    slug: 'testing-systems',
    description: '记录 Playwright、CI、回归测试与稳定性治理的落地经验。',
    cover_image: `${MOCK_ORIGIN}/media-files/category-testing.svg`,
    accent_color: '#14b8a6',
    seo_title: '测试体系专题',
    seo_description: '端到端回归、CI 编排和测试工具链整理。',
  },
  {
    name: '运维值班',
    slug: 'operations-oncall',
    description: '备份、恢复、对象存储与上线清单等值班经验。',
    cover_image: `${MOCK_ORIGIN}/media-files/category-ops.svg`,
    accent_color: '#f97316',
    seo_title: '运维值班专题',
    seo_description: '备份、恢复、发布和值班操作的实践手册。',
  },
]

const tagCatalog = [
  {
    name: 'Astro',
    slug: 'astro',
    description: 'Astro 内容站与 SSR 落地笔记。',
    cover_image: `${MOCK_ORIGIN}/media-files/tag-astro.svg`,
    accent_color: '#8b5cf6',
    seo_title: 'Astro 标签页',
    seo_description: 'Astro、组件、内容站工程实践。',
  },
  {
    name: 'Playwright',
    slug: 'playwright',
    description: 'E2E 回归与浏览器自动化。',
    cover_image: `${MOCK_ORIGIN}/media-files/tag-playwright.svg`,
    accent_color: '#10b981',
    seo_title: 'Playwright 标签页',
    seo_description: 'Playwright、回归测试与自动化流程。',
  },
  {
    name: 'Svelte',
    slug: 'svelte',
    description: 'Svelte 状态管理、组件组织与交互。',
    cover_image: `${MOCK_ORIGIN}/media-files/tag-svelte.svg`,
    accent_color: '#f97316',
    seo_title: 'Svelte 标签页',
    seo_description: 'Svelte 页面状态和组件模式记录。',
  },
  {
    name: 'AI',
    slug: 'ai',
    description: '站内问答、提示词和索引配置实验。',
    cover_image: `${MOCK_ORIGIN}/media-files/tag-ai.svg`,
    accent_color: '#ec4899',
    seo_title: 'AI 标签页',
    seo_description: 'AI 问答、提示词和检索增强内容。',
  },
  {
    name: 'Docker',
    slug: 'docker',
    description: 'Docker 编排、发布和值班脚本。',
    cover_image: `${MOCK_ORIGIN}/media-files/tag-docker.svg`,
    accent_color: '#0ea5e9',
    seo_title: 'Docker 标签页',
    seo_description: 'Docker 发布、运行时与值班记录。',
  },
  {
    name: 'CI',
    slug: 'ci',
    description: 'CI 与工作流编排。',
    cover_image: `${MOCK_ORIGIN}/media-files/tag-ci.svg`,
    accent_color: '#22c55e',
    seo_title: 'CI 标签页',
    seo_description: 'CI、工作流和自动化执行策略。',
  },
]

const postCatalog = [
  {
    title: 'Astro 终端博客信息架构实战',
    slug: 'astro-terminal-blog',
    description: 'Termi 前台首页、过滤器、文章卡片和终端风格布局的整体拆解。',
    category: '前端工程',
    tags: ['Astro', 'Svelte', 'AI'],
    post_type: 'article',
    image: '/review-covers/the-long-season.svg',
    images: ['/review-covers/black-myth-wukong.svg', '/review-covers/thirteen-invites.svg'],
    pinned: true,
    status: 'published',
    visibility: 'public',
    created_at: '2026-03-28T09:00:00.000Z',
    updated_at: '2026-03-29T06:00:00.000Z',
    paragraphs: [
      'Termi 的首页不是纯展示页，而是把文章、评测、友链和订阅动作压进同一个终端式入口，确保用户第一次进入时就能感知站点的信息密度。',
      '为了让首页筛选足够顺滑，我们把文章类型、分类和标签都收敛到统一的状态模型里，任何切换都只改 URL 与最小可见集，而不是整页重新渲染。',
      '这一版改造也顺手补了阅读指标入口，让热门内容、完读率和阅读时长能在前台自然暴露，方便后续继续做推荐与 AI 问答。',
    ],
  },
  {
    title: 'Playwright 回归工作流设计',
    slug: 'playwright-regression-workflow',
    description: 'Termi 前后台共用一套 Playwright smoke 回归，覆盖 CI、mock server 和关键路径。',
    category: '测试体系',
    tags: ['Playwright', 'CI', 'Astro'],
    post_type: 'article',
    image: '/review-covers/placed-within.svg',
    images: [],
    pinned: false,
    status: 'published',
    visibility: 'public',
    created_at: '2026-03-20T10:30:00.000Z',
    updated_at: '2026-03-20T10:30:00.000Z',
    paragraphs: [
      'Playwright 套件会同时拉起前台、后台和 mock server，用独立的内存状态来模拟文章、评论、友链、评测和订阅流程。',
      '这样做的好处是 CI 不再依赖真实 Rust 后端或数据库，也能把前后台的大部分关键交互跑一遍。',
      '只要 mock 数据结构和接口契约稳定，这套 smoke 就能快速捕获组件改版、表单失效和路由错误。',
    ],
  },
  {
    title: 'Svelte 状态仓库拆分模式',
    slug: 'svelte-state-patterns',
    description: 'Termi 在前台筛选、搜索和订阅弹窗里如何拆分 Svelte 状态与副作用。',
    category: '前端工程',
    tags: ['Svelte', 'Astro', 'Playwright'],
    post_type: 'article',
    image: '/review-covers/journey-to-the-west-editorial.svg',
    images: [],
    pinned: false,
    status: 'published',
    visibility: 'public',
    created_at: '2026-02-18T08:10:00.000Z',
    updated_at: '2026-02-18T08:10:00.000Z',
    paragraphs: [
      '搜索模式切换、首页过滤器和订阅弹窗本质上都属于“短生命周期但高交互密度”的状态，适合被拆成局部仓库而不是抬到全局。',
      '这样每个功能块都能保留独立的 URL 同步和本地缓存逻辑，避免一个页面动作把整个前台状态带乱。',
    ],
  },
  {
    title: 'AI 搜索提示词调优记录',
    slug: 'ai-search-prompt-design',
    description: 'Termi 站内 AI 问答如何平衡检索结果、回答密度和提示词约束。',
    category: '测试体系',
    tags: ['AI', 'Playwright', 'CI'],
    post_type: 'article',
    image: '/review-covers/hero-dreams-in-tired-life.svg',
    images: [],
    pinned: false,
    status: 'published',
    visibility: 'public',
    created_at: '2025-12-11T08:00:00.000Z',
    updated_at: '2025-12-11T08:00:00.000Z',
    paragraphs: [
      'AI 问答页的目标不是单纯返回一段答案，而是把引用来源、索引时间和相关度一起暴露给用户。',
      '我们更关心“这个回答是否足够可追溯”，因此来源卡片、SSE 状态提示和缓存命中都被纳入同一条链路。',
    ],
  },
  {
    title: 'Docker 发布值班清单',
    slug: 'docker-rollout-checklist',
    description: 'Termi 的 CI/CD 与 Docker 发布手册：构建、上传、回滚和对象存储连通性检查。',
    category: '运维值班',
    tags: ['Docker', 'CI', 'AI'],
    post_type: 'article',
    image: '/review-covers/black-myth-wukong.svg',
    images: [],
    pinned: false,
    status: 'published',
    visibility: 'public',
    created_at: '2025-10-02T07:20:00.000Z',
    updated_at: '2025-10-02T07:20:00.000Z',
    paragraphs: [
      '值班手册的核心是把部署前检查、发布后验证和回滚策略写清楚，而不是只留下一个 docker compose up。',
      '在 Termi 里，R2 连通性、备份导出和前后台 smoke 都属于上线前必须执行的低成本检查。',
    ],
  },
  {
    title: '终端风格内容站封面系统',
    slug: 'terminal-cover-system',
    description: 'Termi 如何统一文章封面、评测海报和后台媒体库，减少重复素材治理成本。',
    category: '前端工程',
    tags: ['Astro', 'AI', 'Docker'],
    post_type: 'article',
    image: '/review-covers/the-long-season.svg',
    images: [],
    pinned: false,
    status: 'published',
    visibility: 'public',
    created_at: '2025-07-15T11:00:00.000Z',
    updated_at: '2025-07-16T04:00:00.000Z',
    paragraphs: [
      '无论是文章页 hero 图、评测封面还是后台素材库，最终都需要同一套 key、alt、caption 和标签模型。',
      '这让媒体库不仅能服务前台渲染，也能成为后台创建文章和评测时的统一素材来源。',
    ],
  },
  {
    title: '内容观测与阅读完成度',
    slug: 'content-observability-lab',
    description: 'Termi 的阅读进度、页面访问与完读事件如何反哺首页热门内容和后台分析页。',
    category: '测试体系',
    tags: ['AI', 'Playwright', 'Svelte'],
    post_type: 'article',
    image: '/review-covers/placed-within.svg',
    images: [],
    pinned: false,
    status: 'published',
    visibility: 'public',
    created_at: '2025-04-04T06:40:00.000Z',
    updated_at: '2025-04-05T09:20:00.000Z',
    paragraphs: [
      '阅读进度上报看似只是埋点，但如果没有统一会话标识和节流策略，后台分析很快就会被噪音淹没。',
      'Termi 的做法是把 page view、read progress 和 read complete 统一成同一个内容分析模型。',
    ],
  },
  {
    title: 'Markdown 导入与版本回滚',
    slug: 'markdown-import-and-revisions',
    description: 'Termi 后台的 Markdown 导入、版本快照和回滚流程，方便内容大批量迁移。',
    category: '运维值班',
    tags: ['CI', 'Docker', 'Playwright'],
    post_type: 'article',
    image: '/review-covers/thirteen-invites.svg',
    images: [],
    pinned: false,
    status: 'published',
    visibility: 'public',
    created_at: '2024-11-21T10:00:00.000Z',
    updated_at: '2024-11-21T10:00:00.000Z',
    paragraphs: [
      '批量导入 Markdown 时最容易漏掉的是 slug 冲突、元数据缺失和回滚策略，因此版本快照必须和写入动作绑定。',
      '只要恢复接口能按 full、markdown、metadata 三种模式工作，回归测试就能覆盖大部分内容变更风险。',
    ],
  },
  {
    title: '前台订阅弹窗节奏实验',
    slug: 'subscription-popup-experiments',
    description: 'Termi 订阅弹窗如何控制触发时机、收口文案和确认链路，避免一上来就打断阅读。',
    category: '测试体系',
    tags: ['AI', 'Svelte', 'Astro'],
    post_type: 'article',
    image: '/review-covers/journey-to-the-west-editorial.svg',
    images: [],
    pinned: false,
    status: 'published',
    visibility: 'public',
    created_at: '2024-08-08T12:00:00.000Z',
    updated_at: '2024-08-08T12:00:00.000Z',
    paragraphs: [
      '订阅弹窗如果没有上下文，就会被用户当成纯打断；因此我们只在滚动达到阈值或明确点击订阅按钮后再打开。',
      '邮箱确认页、偏好管理页和退订页也被纳入同一条测试链，确保状态切换能闭环。',
    ],
  },
  {
    title: '友链申请审核设计稿',
    slug: 'friend-link-review-workflow',
    description: 'Termi 友链从前台申请到后台审核的最小闭环，以及分类、状态与前台展示规则。',
    category: '前端工程',
    tags: ['Astro', 'Playwright', 'Svelte'],
    post_type: 'article',
    image: '/review-covers/hero-dreams-in-tired-life.svg',
    images: [],
    pinned: false,
    status: 'published',
    visibility: 'public',
    created_at: '2024-06-13T13:20:00.000Z',
    updated_at: '2024-06-13T13:20:00.000Z',
    paragraphs: [
      '友链系统看起来简单，但实际上同时涉及前台表单、后台审核、状态变更和分组展示。',
      '把这条链路纳入 Playwright 回归后，能很快发现表单字段映射和审核按钮失效的问题。',
    ],
  },
  {
    title: '值班手册：备份与恢复演练',
    slug: 'ops-backup-runbook',
    description: 'Termi 备份导出、导入、媒体清单和恢复演练的操作要点。',
    category: '运维值班',
    tags: ['Docker', 'AI', 'CI'],
    post_type: 'tweet',
    image: '/review-covers/black-myth-wukong.svg',
    images: [],
    pinned: false,
    status: 'published',
    visibility: 'public',
    created_at: '2024-03-03T09:10:00.000Z',
    updated_at: '2024-03-03T09:10:00.000Z',
    paragraphs: [
      '备份不是把 JSON 导出来就结束，恢复演练必须能证明分类、标签、评测、媒体与 Markdown 都能被重新装回。',
      '我们把这篇值班手册作为 tweet 类型内容保留，用来验证前台类型过滤与后台导出逻辑。',
    ],
  },
  {
    title: '私有草稿：季度回顾',
    slug: 'private-quarterly-retrospective',
    description: '仅后台可见的私有草稿，用于验证预览、保存和独立工作台。',
    category: '测试体系',
    tags: ['AI', 'Playwright'],
    post_type: 'article',
    image: '/review-covers/the-long-season.svg',
    images: [],
    pinned: false,
    status: 'draft',
    visibility: 'private',
    created_at: '2026-03-30T11:00:00.000Z',
    updated_at: '2026-03-30T11:00:00.000Z',
    paragraphs: [
      '这是一个私有草稿，只应出现在后台编辑器与独立预览窗口中。',
      '用它可以验证 include_private、preview 和 Markdown 更新链路。',
    ],
  },
  {
    title: '计划中的站点改版路线图',
    slug: 'scheduled-site-roadmap',
    description: '一篇已经排期但尚未到发布时间的路线图文章，用于校验后台状态统计。',
    category: '测试体系',
    tags: ['CI', 'Astro'],
    post_type: 'article',
    image: '/review-covers/placed-within.svg',
    images: [],
    pinned: false,
    status: 'scheduled',
    visibility: 'public',
    publish_at: '2026-04-15T03:00:00.000Z',
    unpublish_at: null,
    created_at: '2026-04-01T02:00:00.000Z',
    updated_at: '2026-04-01T02:00:00.000Z',
    paragraphs: [
      '这篇文章用于测试定时发布和后台概览统计，不应该出现在前台公开列表里。',
      '当发布时间未到时，文章仍可在后台独立预览与对比。',
    ],
  },
]

function iso(offsetMinutes = 0) {
  return new Date(BASE_TS + offsetMinutes * 60_000).toISOString()
}

function json(res, status, payload, headers = {}) {
  res.writeHead(status, {
    'content-type': CONTENT_TYPES.json,
    'cache-control': 'no-store',
    ...headers,
  })
  res.end(JSON.stringify(payload))
}

function text(res, status, body, headers = {}) {
  res.writeHead(status, {
    'content-type': CONTENT_TYPES.text,
    'cache-control': 'no-store',
    ...headers,
  })
  res.end(body)
}

function reflectCors(req, res) {
  const origin = req.headers.origin
  if (!origin) {
    return
  }

  if ([FRONTEND_ORIGIN, ADMIN_ORIGIN].includes(origin)) {
    res.setHeader('access-control-allow-origin', origin)
    res.setHeader('vary', 'Origin')
    res.setHeader('access-control-allow-credentials', 'true')
    res.setHeader(
      'access-control-allow-headers',
      'Content-Type, Authorization, X-Requested-With',
    )
    res.setHeader(
      'access-control-allow-methods',
      'GET, POST, PUT, PATCH, DELETE, OPTIONS',
    )
  }
}

function readRequestBody(req) {
  return new Promise((resolve, reject) => {
    const chunks = []
    req.on('data', (chunk) => chunks.push(chunk))
    req.on('end', () => resolve(Buffer.concat(chunks)))
    req.on('error', reject)
  })
}

function safeJsonParse(raw, fallback = {}) {
  try {
    return JSON.parse(raw)
  } catch {
    return fallback
  }
}

function parseMultipartBody(buffer, contentType) {
  const boundaryMatch = contentType.match(/boundary=(?:"([^"]+)"|([^;]+))/i)
  const boundary = boundaryMatch?.[1] || boundaryMatch?.[2]
  if (!boundary) {
    return { fields: {}, files: [] }
  }

  const source = buffer.toString('latin1')
  const parts = source.split(`--${boundary}`)
  const fields = {}
  const files = []

  for (const part of parts) {
    const trimmed = part.trim()
    if (!trimmed || trimmed === '--') {
      continue
    }

    const [rawHeaders, ...bodyChunks] = part.split('\r\n\r\n')
    if (!rawHeaders || !bodyChunks.length) {
      continue
    }

    const body = bodyChunks.join('\r\n\r\n').replace(/\r\n$/, '')
    const headerLines = rawHeaders
      .split('\r\n')
      .map((line) => line.trim())
      .filter(Boolean)
    const disposition = headerLines.find((line) =>
      line.toLowerCase().startsWith('content-disposition:'),
    )

    if (!disposition) {
      continue
    }

    const nameMatch = disposition.match(/name="([^"]+)"/i)
    const filenameMatch = disposition.match(/filename="([^"]*)"/i)
    const fieldName = nameMatch?.[1]

    if (!fieldName) {
      continue
    }

    const contentTypeLine = headerLines.find((line) =>
      line.toLowerCase().startsWith('content-type:'),
    )
    const fileName = filenameMatch?.[1]

    if (fileName !== undefined) {
      files.push({
        fieldName,
        filename: fileName,
        contentType: contentTypeLine?.split(':')[1]?.trim() || 'application/octet-stream',
        size: Buffer.from(body, 'latin1').length,
        text: Buffer.from(body, 'latin1').toString('utf8'),
      })
      continue
    }

    if (fieldName in fields) {
      const current = fields[fieldName]
      if (Array.isArray(current)) {
        current.push(body)
      } else {
        fields[fieldName] = [current, body]
      }
    } else {
      fields[fieldName] = body
    }
  }

  return { fields, files }
}

async function parseRequest(req) {
  const body = await readRequestBody(req)
  const contentType = String(req.headers['content-type'] || '')

  if (!body.length) {
    return { body, json: {}, fields: {}, files: [] }
  }

  if (contentType.includes('application/json')) {
    return { body, json: safeJsonParse(body.toString('utf8'), {}), fields: {}, files: [] }
  }

  if (contentType.includes('multipart/form-data')) {
    const parsed = parseMultipartBody(body, contentType)
    return { body, json: {}, ...parsed }
  }

  return { body, json: {}, fields: {}, files: [] }
}

function getCookies(req) {
  return String(req.headers.cookie || '')
    .split(';')
    .map((item) => item.trim())
    .filter(Boolean)
    .reduce((acc, item) => {
      const index = item.indexOf('=')
      if (index === -1) {
        return acc
      }
      acc[item.slice(0, index)] = item.slice(index + 1)
      return acc
    }, {})
}

function isAuthenticated(req) {
  return getCookies(req)[SESSION_COOKIE] === SESSION_VALUE
}

function ensureAdmin(req, res) {
  if (isAuthenticated(req)) {
    return true
  }

  json(res, 401, {
    error: 'unauthorized',
    description: '当前未登录后台。',
  })
  return false
}

function slugify(value) {
  return String(value || '')
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9\u4e00-\u9fa5]+/g, '-')
    .replace(/^-+|-+$/g, '')
}

function normalizeText(value) {
  return String(value || '').trim()
}

function toBoolean(value, fallback = false) {
  if (value === undefined || value === null || value === '') {
    return fallback
  }
  if (typeof value === 'boolean') {
    return value
  }
  return ['1', 'true', 'yes', 'on'].includes(String(value).trim().toLowerCase())
}

function parseJsonArray(value) {
  if (Array.isArray(value)) {
    return value
  }

  if (!value) {
    return []
  }

  if (typeof value === 'string') {
    try {
      const parsed = JSON.parse(value)
      return Array.isArray(parsed) ? parsed : []
    } catch {
      return value
        .split(',')
        .map((item) => item.trim())
        .filter(Boolean)
    }
  }

  return []
}

function buildMarkdown(title, paragraphs) {
  return `# ${title}\n\n${paragraphs.join('\n\n')}\n`
}

function normalizeParagraphText(textValue) {
  return textValue.replace(/\s+/g, ' ').trim().toLowerCase()
}

function fnv1aHash(value) {
  let hash = 0x811c9dc5

  for (let index = 0; index < value.length; index += 1) {
    hash ^= value.charCodeAt(index)
    hash = Math.imul(hash, 0x01000193)
  }

  return (hash >>> 0).toString(16).padStart(8, '0')
}

function extractParagraphDescriptors(markdown) {
  const occurrences = new Map()
  const content = String(markdown || '')
    .replace(/^#\s+.+\n+/, '')
    .split(/\n{2,}/)
    .map((block) => block.trim())
    .filter((block) => block && !block.startsWith('#') && !block.startsWith('!['))

  return content.map((paragraph) => {
    const normalized = normalizeParagraphText(paragraph)
    const hash = fnv1aHash(normalized)
    const occurrence = (occurrences.get(hash) || 0) + 1
    occurrences.set(hash, occurrence)

    return {
      key: `p-${hash}-${occurrence}`,
      excerpt: paragraph.length <= 120 ? paragraph : `${paragraph.slice(0, 120).trimEnd()}...`,
      text: paragraph,
    }
  })
}

function clone(value) {
  return JSON.parse(JSON.stringify(value))
}

function createSessionResponse(authenticated) {
  return {
    authenticated,
    username: authenticated ? VALID_LOGIN.username : null,
    email: authenticated ? 'admin@termi.test' : null,
    auth_source: authenticated ? 'mock-local' : null,
    auth_provider: authenticated ? 'mock-session' : null,
    groups: authenticated ? ['admin'] : [],
    proxy_auth_enabled: false,
    local_login_enabled: true,
    can_logout: authenticated,
  }
}

const NOW_ISO = iso(0)

const reviewCatalog = [
  {
    title: '《漫长的季节》',
    review_type: 'anime',
    rating: 5,
    review_date: '2026-03-21',
    status: 'completed',
    description: '把东北工业景观、家庭裂痕和悬疑节奏揉进一起，后劲非常强。',
    tags: ['悬疑', '现实主义', '年度最佳'],
    cover: '/review-covers/the-long-season.svg',
    link_url: 'https://example.invalid/reviews/the-long-season',
  },
  {
    title: '《黑神话：悟空》',
    review_type: 'game',
    rating: 4,
    review_date: '2026-03-18',
    status: 'completed',
    description: '美术和动作系统都足够能打，流程细节还有继续打磨空间。',
    tags: ['动作', '国产', '年度观察'],
    cover: '/review-covers/black-myth-wukong.svg',
    link_url: 'https://example.invalid/reviews/black-myth-wukong',
  },
  {
    title: '《置身事内》',
    review_type: 'book',
    rating: 5,
    review_date: '2026-02-02',
    status: 'completed',
    description: '适合把财政、土地与地方治理如何互相牵引一次性理顺。',
    tags: ['财政', '中国', '社会观察'],
    cover: '/review-covers/placed-within.svg',
    link_url: '/articles/playwright-regression-workflow',
  },
  {
    title: '《宇宙探索编辑部》',
    review_type: 'movie',
    rating: 4,
    review_date: '2026-01-12',
    status: 'in-progress',
    description: '荒诞感和理想主义都很讨喜，适合放进终端风格站点做海报实验。',
    tags: ['电影', '公路片', '荒诞'],
    cover: '/review-covers/journey-to-the-west-editorial.svg',
    link_url: '',
  },
]

const friendLinkCatalog = [
  {
    site_name: 'InitCool Docs',
    site_url: 'https://docs.init.cool',
    description: '收纳部署、值班与研发工具链的工程手册站。',
    category: 'tech',
    status: 'approved',
  },
  {
    site_name: 'Svelte Terminal Lab',
    site_url: 'https://svelte-terminal.example',
    description: '记录 Svelte、Astro 与终端式交互实验。',
    category: 'design',
    status: 'approved',
  },
  {
    site_name: 'Pending Link Review',
    site_url: 'https://pending-link.example',
    description: '待后台审核的新友链申请。',
    category: 'other',
    status: 'pending',
  },
]

const mediaCatalog = [
  {
    key: 'covers/the-long-season.svg',
    title: '漫长的季节封面',
    alt_text: '终端风格的蓝色封面海报',
    caption: '首页推荐位封面',
    tags: ['cover', 'anime'],
    notes: '默认 mock 素材库',
    size_bytes: 1824,
  },
  {
    key: 'posts/playwright-workflow.svg',
    title: 'Playwright 工作流配图',
    alt_text: '回归测试流程图',
    caption: 'CI 与 smoke server 关系图',
    tags: ['diagram', 'playwright'],
    notes: '用于文章配图',
    size_bytes: 2048,
  },
]

function createSiteSettings() {
  return {
    id: 1,
    site_name: 'InitCool',
    site_short_name: 'Termi',
    site_url: FRONTEND_ORIGIN,
    site_title: 'InitCool - 终端风格的内容平台',
    site_description: '一个基于终端美学的个人内容站，记录代码、设计和生活。',
    hero_title: '欢迎来到我的极客终端博客',
    hero_subtitle: '这里记录技术、代码和内容运营的持续迭代。',
    owner_name: 'InitCool',
    owner_title: 'Rust / Go / Frontend Builder',
    owner_bio: '偏好用最小系统把内容、测试、值班和自动化串起来。',
    owner_avatar_url: null,
    social_github: 'https://github.com/initcool',
    social_twitter: '',
    social_email: 'mailto:initcool@example.com',
    location: 'Hong Kong',
    tech_stack: ['Astro', 'Svelte', 'React', 'Rust', 'Playwright', 'Docker'],
    music_playlist: [
      {
        title: '山中来信',
        artist: 'InitCool Radio',
        album: '站点默认歌单',
        url: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3',
        cover_image_url: `${MOCK_ORIGIN}/media-files/playlist-1.svg`,
        accent_color: '#2f6b5f',
        description: '适合文章阅读时循环播放的轻氛围曲。',
      },
    ],
    maintenance_mode_enabled: false,
    maintenance_access_code: null,
    ai_enabled: true,
    paragraph_comments_enabled: true,
    comment_verification_mode: 'captcha',
    comment_turnstile_enabled: false,
    subscription_verification_mode: 'off',
    subscription_turnstile_enabled: false,
    web_push_enabled: false,
    turnstile_site_key: null,
    turnstile_secret_key: null,
    web_push_vapid_public_key: null,
    web_push_vapid_private_key: null,
    web_push_vapid_subject: null,
    ai_provider: 'mock-openai',
    ai_api_base: 'https://api.mock.invalid/v1',
    ai_api_key: 'mock-key',
    ai_chat_model: 'gpt-mock-4.1',
    ai_image_provider: 'mock-images',
    ai_image_api_base: 'https://images.mock.invalid/v1',
    ai_image_api_key: 'mock-image-key',
    ai_image_model: 'mock-image-1',
    ai_providers: [
      {
        id: 'default',
        name: 'Mock Provider',
        provider: 'openai',
        api_base: 'https://api.mock.invalid/v1',
        api_key: 'mock-key',
        chat_model: 'gpt-mock-4.1',
        image_model: 'mock-image-1',
      },
    ],
    ai_active_provider_id: 'default',
    ai_embedding_model: 'text-embedding-mock-3',
    ai_system_prompt: '你是 Termi 的 mock AI 助手。',
    ai_top_k: 6,
    ai_chunk_size: 800,
    ai_last_indexed_at: iso(-90),
    ai_chunks_count: 128,
    ai_local_embedding: 'disabled',
    media_storage_provider: 'mock-r2',
    media_r2_account_id: 'mock-account',
    media_r2_bucket: 'termi-playwright',
    media_r2_public_base_url: `${MOCK_ORIGIN}/media`,
    media_r2_access_key_id: 'mock-access-key',
    media_r2_secret_access_key: 'mock-secret',
    seo_favicon_url: `${MOCK_ORIGIN}/favicon.svg`,
    seo_default_og_image: `${MOCK_ORIGIN}/media-files/default-og.svg`,
    seo_default_twitter_handle: '@initcool',
    seo_wechat_share_qr_enabled: false,
    notification_webhook_url: 'https://notify.mock.invalid/termi',
    notification_channel_type: 'webhook',
    notification_comment_enabled: true,
    notification_friend_link_enabled: true,
    subscription_popup_enabled: true,
    subscription_popup_title: '订阅更新',
    subscription_popup_description: '有新文章、周报和友链通知时，通过邮件第一时间收到提醒。',
    subscription_popup_delay_seconds: 2,
    search_synonyms: ['playwright,e2e,regression', 'ai,ask,assistant'],
  }
}

function maintenanceAccessTokenFromSecret(secret) {
  return createHash('sha256')
    .update('termi-maintenance-access:v1:')
    .update(String(secret))
    .digest('hex')
}

function parseHeadingAndDescription(markdown, fallbackTitle = 'Untitled') {
  const normalized = String(markdown || '').replace(/\r\n/g, '\n').trim()
  const titleMatch = normalized.match(/^#\s+(.+)$/m)
  const bodyText = normalized
    .replace(/^#\s+.+\n+/, '')
    .split(/\n{2,}/)
    .map((item) => item.trim())
    .find(Boolean)

  return {
    title: normalizeText(titleMatch?.[1] || fallbackTitle) || fallbackTitle,
    description: normalizeText(bodyText || ''),
  }
}

function makePostRecord(entry, id) {
  const markdown = buildMarkdown(entry.title, entry.paragraphs)
  const { title, description } = parseHeadingAndDescription(markdown, entry.title)
  return {
    id,
    title,
    slug: entry.slug,
    description,
    content: markdown,
    category: entry.category,
    tags: [...entry.tags],
    post_type: entry.post_type,
    image: entry.image || null,
    images: entry.images?.length ? [...entry.images] : [],
    pinned: Boolean(entry.pinned),
    status: entry.status || 'published',
    visibility: entry.visibility || 'public',
    publish_at: entry.publish_at || null,
    unpublish_at: entry.unpublish_at || null,
    canonical_url: null,
    noindex: false,
    og_image: null,
    redirect_from: [],
    redirect_to: null,
    created_at: entry.created_at || NOW_ISO,
    updated_at: entry.updated_at || entry.created_at || NOW_ISO,
  }
}

function buildReviewRecord(entry, id) {
  return {
    id,
    title: entry.title,
    review_type: entry.review_type,
    rating: entry.rating,
    review_date: entry.review_date,
    status: entry.status,
    description: entry.description,
    tags: JSON.stringify(entry.tags || []),
    cover: entry.cover || '',
    link_url: entry.link_url || null,
    created_at: iso(-id * 15),
    updated_at: iso(-id * 10),
  }
}

function isPublicReviewVisible(review) {
  return ['published', 'completed', 'done'].includes(
    normalizeText(review?.status).toLowerCase(),
  )
}

function createSubscriptionRecord(id, overrides = {}) {
  return {
    created_at: iso(-id * 6),
    updated_at: iso(-id * 5),
    id,
    channel_type: 'email',
    target: `user${id}@example.com`,
    display_name: `订阅用户 ${id}`,
    status: 'active',
    filters: {
      event_types: ['post.published', 'digest.weekly'],
      categories: ['前端工程'],
      tags: ['Playwright'],
    },
    metadata: {
      source: 'seed',
    },
    secret: null,
    notes: 'seed subscription',
    confirm_token: `confirm-token-${id}`,
    manage_token: `manage-token-${id}`,
    verified_at: iso(-id * 4),
    last_notified_at: iso(-id * 2),
    failure_count: 0,
    last_delivery_status: 'delivered',
    ...overrides,
  }
}

function createMediaRecord(item, index) {
  const url = `${MOCK_ORIGIN}/media/${encodeURIComponent(item.key)}`
  return {
    key: item.key,
    url,
    size_bytes: item.size_bytes,
    last_modified: iso(-index * 7),
    title: item.title,
    alt_text: item.alt_text,
    caption: item.caption,
    tags: [...item.tags],
    notes: item.notes,
    body: `mock media body for ${item.key}`,
    content_type: item.key.endsWith('.svg') ? CONTENT_TYPES.svg : CONTENT_TYPES.text,
  }
}

function createInitialState() {
  const site_settings = createSiteSettings()
  const categories = categoryCatalog.map((item, index) => ({
    id: index + 1,
    name: item.name,
    slug: item.slug,
    count: 0,
    description: item.description,
    cover_image: item.cover_image,
    accent_color: item.accent_color,
    seo_title: item.seo_title,
    seo_description: item.seo_description,
    created_at: iso(-(index + 1) * 20),
    updated_at: iso(-(index + 1) * 10),
  }))
  const tags = tagCatalog.map((item, index) => ({
    id: index + 1,
    name: item.name,
    slug: item.slug,
    count: 0,
    description: item.description,
    cover_image: item.cover_image,
    accent_color: item.accent_color,
    seo_title: item.seo_title,
    seo_description: item.seo_description,
    created_at: iso(-(index + 1) * 18),
    updated_at: iso(-(index + 1) * 8),
  }))
  const posts = postCatalog.map((item, index) => makePostRecord(item, index + 1))
  const reviews = reviewCatalog.map((item, index) => buildReviewRecord(item, index + 1))
  const friend_links = friendLinkCatalog.map((item, index) => ({
    id: index + 1,
    site_name: item.site_name,
    site_url: item.site_url,
    avatar_url: null,
    description: item.description,
    category: item.category,
    status: item.status,
    created_at: iso(-(index + 1) * 12),
    updated_at: iso(-(index + 1) * 6),
  }))
  const subscriptions = [
    createSubscriptionRecord(1, {
      target: 'watcher@example.com',
      display_name: '产品订阅',
      filters: {
        event_types: ['post.published', 'digest.weekly'],
        categories: ['测试体系'],
        tags: ['Playwright', 'CI'],
      },
    }),
    createSubscriptionRecord(2, {
      target: 'ops@example.com',
      display_name: '值班通知',
      filters: {
        event_types: ['digest.monthly', 'friend_link.created'],
        categories: ['运维值班'],
        tags: ['Docker'],
      },
    }),
  ]
  const media = mediaCatalog.map((item, index) => createMediaRecord(item, index + 1))
  const articlePost = posts.find((item) => item.slug === 'astro-terminal-blog') || posts[0]
  const paragraph = extractParagraphDescriptors(articlePost.content)[0]
  const comments = [
    {
      id: 1,
      post_id: String(articlePost.id),
      post_slug: articlePost.slug,
      author: 'Alice',
      email: 'alice@example.com',
      avatar: null,
      ip_address: '10.0.0.8',
      user_agent: 'MockBrowser/1.0',
      referer: `${FRONTEND_ORIGIN}/articles/${articlePost.slug}`,
      content: '首页筛选和终端风格结合得很好，想看更多实现细节。',
      reply_to: null,
      reply_to_comment_id: null,
      scope: 'article',
      paragraph_key: null,
      paragraph_excerpt: null,
      approved: true,
      created_at: iso(-40),
      updated_at: iso(-40),
    },
    {
      id: 2,
      post_id: String(articlePost.id),
      post_slug: articlePost.slug,
      author: 'Bob',
      email: 'bob@example.com',
      avatar: null,
      ip_address: '10.0.0.9',
      user_agent: 'MockBrowser/1.0',
      referer: `${FRONTEND_ORIGIN}/articles/${articlePost.slug}`,
      content: '这里的 URL 同步策略很实用。',
      reply_to: null,
      reply_to_comment_id: null,
      scope: 'paragraph',
      paragraph_key: paragraph?.key || null,
      paragraph_excerpt: paragraph?.excerpt || null,
      approved: true,
      created_at: iso(-35),
      updated_at: iso(-35),
    },
    {
      id: 3,
      post_id: String(articlePost.id),
      post_slug: articlePost.slug,
      author: 'Carol',
      email: 'carol@example.com',
      avatar: null,
      ip_address: '10.0.0.10',
      user_agent: 'MockBrowser/1.0',
      referer: `${FRONTEND_ORIGIN}/articles/${articlePost.slug}`,
      content: '这条评论默认待审核，用来验证后台审核流程。',
      reply_to: null,
      reply_to_comment_id: null,
      scope: 'article',
      paragraph_key: null,
      paragraph_excerpt: null,
      approved: false,
      created_at: iso(-25),
      updated_at: iso(-25),
    },
  ]

  return {
    site_settings,
    categories,
    tags,
    posts,
    reviews,
    friend_links,
    subscriptions,
    media,
    comments,
    audit_logs: [],
    post_revisions: [],
    comment_blacklist: [
      {
        id: 1,
        matcher_type: 'email',
        matcher_value: 'blocked@example.com',
        reason: 'seed rule',
        active: true,
        expires_at: null,
        created_at: iso(-12),
        updated_at: iso(-12),
        effective: true,
      },
    ],
    comment_persona_logs: [],
    deliveries: [],
    worker_jobs: [],
    ai_events: [],
    content_events: [],
    captcha_tokens: new Map(),
    next_ids: {
      post: posts.length + 1,
      category: categories.length + 1,
      tag: tags.length + 1,
      comment: comments.length + 1,
      friend_link: friend_links.length + 1,
      review: reviews.length + 1,
      subscription: subscriptions.length + 1,
      audit: 1,
      revision: 1,
      delivery: 1,
      worker_job: 1,
      blacklist: 2,
      persona_log: 1,
      ai_event: 1,
    },
  }
}

let state = createInitialState()

function nextId(bucket) {
  const value = state.next_ids[bucket]
  state.next_ids[bucket] += 1
  return value
}

function isPostVisible(post, options = {}) {
  const includePrivate = toBoolean(options.include_private, false)
  const preview = toBoolean(options.preview, false)
  const includeRedirects = toBoolean(options.include_redirects, false)
  const now = BASE_TS

  if (!post) return false
  if (!includeRedirects && post.redirect_to) return false
  if (preview) return true
  if (post.status !== 'published') return false
  if (post.visibility === 'private' && !includePrivate) return false
  if (post.visibility === 'private') return false
  if (post.publish_at && Date.parse(post.publish_at) > now) return false
  if (post.unpublish_at && Date.parse(post.unpublish_at) <= now) return false
  return true
}

function recalculateTaxonomyCounts() {
  const categoryCounts = new Map()
  const tagCounts = new Map()

  for (const post of state.posts.filter((item) => isPostVisible(item))) {
    if (post.category) {
      categoryCounts.set(post.category, (categoryCounts.get(post.category) || 0) + 1)
    }
    for (const tag of post.tags || []) {
      tagCounts.set(tag, (tagCounts.get(tag) || 0) + 1)
    }
  }

  for (const category of state.categories) {
    category.count = categoryCounts.get(category.name) || 0
  }
  for (const tag of state.tags) {
    tag.count = tagCounts.get(tag.name) || 0
  }
}

function addAuditLog(action, target_type, target_label, target_id = null, metadata = null) {
  state.audit_logs.unshift({
    id: nextId('audit'),
    created_at: iso(-1),
    updated_at: iso(-1),
    actor_username: VALID_LOGIN.username,
    actor_email: 'admin@termi.test',
    actor_source: 'mock-admin',
    action,
    target_type,
    target_id: target_id === null ? null : String(target_id),
    target_label: target_label || null,
    metadata,
  })
}

function addPostRevision(post, operation = 'update', reason = null) {
  state.post_revisions.unshift({
    id: nextId('revision'),
    post_slug: post.slug,
    post_title: post.title,
    operation,
    revision_reason: reason,
    actor_username: VALID_LOGIN.username,
    actor_email: 'admin@termi.test',
    actor_source: 'mock-admin',
    created_at: iso(-1),
    has_markdown: true,
    metadata: { category: post.category, tags: post.tags, status: post.status },
    markdown: post.content,
    snapshot: clone(post),
  })
}

function createNotificationDelivery({
  subscription,
  eventType,
  status = 'queued',
  responseText = 'queued by mock server',
  payload = null,
}) {
  return {
    created_at: iso(-1),
    updated_at: iso(-1),
    id: nextId('delivery'),
    subscription_id: subscription?.id ?? null,
    channel_type: subscription?.channel_type ?? 'email',
    target: subscription?.target ?? 'unknown@example.com',
    event_type: eventType,
    status,
    provider: 'mock-delivery',
    response_text: responseText,
    payload,
    attempts_count: status === 'queued' ? 0 : 1,
    next_retry_at: status === 'retry_pending' ? iso(10) : null,
    last_attempt_at: status === 'queued' ? null : iso(-1),
    delivered_at: status === 'sent' ? iso(-1) : null,
  }
}

function createWorkerJob({
  job_kind = 'worker',
  worker_name,
  display_name = null,
  status = 'queued',
  queue_name = null,
  requested_by = VALID_LOGIN.username,
  requested_source = 'mock-admin',
  trigger_mode = 'manual',
  payload = null,
  result = null,
  error_text = null,
  tags = [],
  related_entity_type = null,
  related_entity_id = null,
  parent_job_id = null,
  attempts_count = status === 'queued' ? 0 : 1,
  max_attempts = 1,
  cancel_requested = false,
  queued_at = iso(-1),
  started_at = status === 'queued' ? null : iso(-1),
  finished_at = status === 'queued' || status === 'running' ? null : iso(-1),
} = {}) {
  const record = {
    created_at: iso(-1),
    updated_at: iso(-1),
    id: nextId('worker_job'),
    parent_job_id,
    job_kind,
    worker_name,
    display_name,
    status,
    queue_name,
    requested_by,
    requested_source,
    trigger_mode,
    payload,
    result,
    error_text,
    tags: [...tags],
    related_entity_type,
    related_entity_id: related_entity_id === null ? null : String(related_entity_id),
    attempts_count,
    max_attempts,
    cancel_requested,
    queued_at,
    started_at,
    finished_at,
  }
  state.worker_jobs.unshift(record)
  return record
}

function canCancelWorkerJob(job) {
  return !job.cancel_requested && (job.status === 'queued' || job.status === 'running')
}

function canRetryWorkerJob(job) {
  return ['failed', 'cancelled', 'succeeded'].includes(job.status)
}

function normalizeWorkerJob(job) {
  return {
    ...clone(job),
    can_cancel: canCancelWorkerJob(job),
    can_retry: canRetryWorkerJob(job),
  }
}

function buildWorkerOverview() {
  const jobs = state.worker_jobs
  const counters = {
    total_jobs: jobs.length,
    queued: 0,
    running: 0,
    succeeded: 0,
    failed: 0,
    cancelled: 0,
    active_jobs: 0,
  }
  const grouped = new Map()
  const catalog = [
    ['worker.download_media', 'worker', '远程媒体下载', '抓取远程图片 / PDF 到媒体库，并回写媒体元数据。', 'media'],
    ['worker.notification_delivery', 'worker', '通知投递', '执行订阅通知、测试通知与 digest 投递。', 'notifications'],
    ['task.retry_deliveries', 'task', '重试待投递通知', '扫描 retry_pending 的通知记录并重新入队。', 'maintenance'],
    ['task.send_weekly_digest', 'task', '发送周报', '根据近期内容生成周报，并为活跃订阅目标入队。', 'digests'],
    ['task.send_monthly_digest', 'task', '发送月报', '根据近期内容生成月报，并为活跃订阅目标入队。', 'digests'],
  ].map(([worker_name, job_kind, label, description, queue_name]) => ({
    worker_name,
    job_kind,
    label,
    description,
    queue_name,
    supports_cancel: true,
    supports_retry: true,
  }))

  for (const job of jobs) {
    if (Object.hasOwn(counters, job.status)) {
      counters[job.status] += 1
    }
    const existing =
      grouped.get(job.worker_name) ||
      {
        worker_name: job.worker_name,
        job_kind: job.job_kind,
        label: catalog.find((item) => item.worker_name === job.worker_name)?.label || job.worker_name,
        queued: 0,
        running: 0,
        succeeded: 0,
        failed: 0,
        cancelled: 0,
        last_job_at: null,
      }
    if (Object.hasOwn(existing, job.status)) {
      existing[job.status] += 1
    }
    existing.last_job_at ||= job.created_at
    grouped.set(job.worker_name, existing)
  }

  counters.active_jobs = counters.queued + counters.running

  return {
    ...counters,
    worker_stats: Array.from(grouped.values()),
    catalog,
  }
}

function enqueueNotificationDeliveryJob(delivery, options = {}) {
  return createWorkerJob({
    job_kind: 'worker',
    worker_name: 'worker.notification_delivery',
    display_name: `${delivery.event_type} → ${delivery.target}`,
    status: options.status || 'succeeded',
    queue_name: 'notifications',
    payload: {
      delivery_id: delivery.id,
      job_id: null,
    },
    result: options.status === 'failed' ? null : { delivery_id: delivery.id },
    error_text: options.status === 'failed' ? options.error_text || 'mock delivery failed' : null,
    tags: ['notifications', 'delivery'],
    related_entity_type: 'notification_delivery',
    related_entity_id: delivery.id,
    parent_job_id: options.parent_job_id ?? null,
  })
}

function sanitizeFilename(value, fallback = 'upload.bin') {
  const normalized = String(value || '').split(/[\\/]/).pop() || fallback
  return normalized.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, '') || fallback
}

function makeMediaRecordFromUpload(key, file) {
  return {
    key,
    url: `${MOCK_ORIGIN}/media/${encodeURIComponent(key)}`,
    size_bytes: file?.size ?? Buffer.byteLength(file?.body || ''),
    last_modified: iso(-1),
    title: sanitizeFilename(file?.filename || key),
    alt_text: null,
    caption: null,
    tags: [],
    notes: 'uploaded by playwright mock',
    body: file?.body?.toString('utf8') || `mock media body for ${key}`,
    content_type: String(file?.contentType || '').includes('svg')
      ? CONTENT_TYPES.svg
      : String(file?.contentType || '').includes('image/')
        ? String(file.contentType)
        : CONTENT_TYPES.text,
  }
}

function queueDownloadWorkerJob(payload, mediaRecord) {
  return createWorkerJob({
    job_kind: 'worker',
    worker_name: 'worker.download_media',
    display_name: normalizeText(payload.title) || `download ${normalizeText(payload.source_url)}`,
    status: 'succeeded',
    queue_name: 'media',
    payload: {
      source_url: payload.source_url,
      prefix: payload.prefix || null,
      title: payload.title || null,
      alt_text: payload.alt_text || null,
      caption: payload.caption || null,
      tags: Array.isArray(payload.tags) ? payload.tags : [],
      notes: payload.notes || null,
      job_id: null,
    },
    result: mediaRecord
      ? {
          key: mediaRecord.key,
          url: mediaRecord.url,
          size_bytes: mediaRecord.size_bytes,
          source_url: normalizeText(payload.source_url),
          content_type: mediaRecord.content_type,
        }
      : null,
    tags: ['media', 'download'],
    related_entity_type: 'media_download',
    related_entity_id: normalizeText(payload.source_url),
  })
}

function upsertPostFromPayload(current, payload) {
  const next = current ? { ...current } : {}
  const title = normalizeText(payload.title) || normalizeText(current?.title) || '未命名文章'
  const slug = normalizeText(payload.slug) || normalizeText(current?.slug) || slugify(title)
  const content = payload.content ?? current?.content ?? buildMarkdown(title, ['待补充内容。'])
  const parsed = parseHeadingAndDescription(content, title)

  next.title = title || parsed.title
  next.slug = slug
  next.description = normalizeText(payload.description) || parsed.description || current?.description || ''
  next.content = content
  next.category = normalizeText(payload.category) || null
  next.tags = Array.isArray(payload.tags) ? payload.tags.filter(Boolean) : current?.tags || []
  next.post_type = normalizeText(payload.post_type || payload.postType) || current?.post_type || 'article'
  next.image = normalizeText(payload.image) || null
  next.images = Array.isArray(payload.images) ? payload.images.filter(Boolean) : current?.images || []
  next.pinned = Object.hasOwn(payload, 'pinned') ? Boolean(payload.pinned) : Boolean(current?.pinned)
  next.status =
    normalizeText(payload.status) ||
    (Object.hasOwn(payload, 'published') ? (payload.published ? 'published' : 'draft') : '') ||
    current?.status ||
    'draft'
  next.visibility = normalizeText(payload.visibility) || current?.visibility || 'public'
  next.publish_at = normalizeText(payload.publish_at || payload.publishAt) || null
  next.unpublish_at = normalizeText(payload.unpublish_at || payload.unpublishAt) || null
  next.canonical_url = normalizeText(payload.canonical_url || payload.canonicalUrl) || null
  next.noindex = Object.hasOwn(payload, 'noindex') ? Boolean(payload.noindex) : Boolean(current?.noindex)
  next.og_image = normalizeText(payload.og_image || payload.ogImage) || null
  next.redirect_from = Array.isArray(payload.redirect_from || payload.redirectFrom)
    ? (payload.redirect_from || payload.redirectFrom).filter(Boolean)
    : current?.redirect_from || []
  next.redirect_to = normalizeText(payload.redirect_to || payload.redirectTo) || null
  next.created_at = current?.created_at || iso(-1)
  next.updated_at = iso(-1)

  return next
}

function makeTaxonomyRecord(bucket, payload, current = null) {
  return {
    id: current?.id ?? nextId(bucket),
    name: normalizeText(payload.name) || current?.name || '',
    slug: normalizeText(payload.slug) || slugify(payload.name || current?.name || ''),
    count: current?.count ?? 0,
    description: normalizeText(payload.description) || null,
    cover_image: normalizeText(payload.cover_image || payload.coverImage) || null,
    accent_color: normalizeText(payload.accent_color || payload.accentColor) || null,
    seo_title: normalizeText(payload.seo_title || payload.seoTitle) || null,
    seo_description: normalizeText(payload.seo_description || payload.seoDescription) || null,
    created_at: current?.created_at || iso(-1),
    updated_at: iso(-1),
  }
}

function findRevisionById(id) {
  return state.post_revisions.find((item) => item.id === id) || null
}

function resetState() {
  state = createInitialState()
  recalculateTaxonomyCounts()
  addAuditLog('seed.bootstrap', 'workspace', 'playwright-smoke', 'seed', {
    posts: state.posts.length,
    reviews: state.reviews.length,
  })
  for (const post of state.posts.slice().reverse()) {
    addPostRevision(post, 'seed', '初始 mock 数据')
  }
}

resetState()

function normalizePostResponse(post) {
  return {
    created_at: post.created_at,
    updated_at: post.updated_at,
    id: post.id,
    title: post.title,
    slug: post.slug,
    description: post.description,
    content: post.content,
    category: post.category,
    tags: [...(post.tags || [])],
    post_type: post.post_type,
    image: post.image,
    images: [...(post.images || [])],
    pinned: post.pinned,
    status: post.status,
    visibility: post.visibility,
    publish_at: post.publish_at,
    unpublish_at: post.unpublish_at,
    canonical_url: post.canonical_url,
    noindex: post.noindex,
    og_image: post.og_image,
    redirect_from: [...(post.redirect_from || [])],
    redirect_to: post.redirect_to,
  }
}

function sortItems(items, sortBy = 'created_at', sortOrder = 'desc') {
  const direction = String(sortOrder || 'desc').toLowerCase() === 'asc' ? 1 : -1
  const key = sortBy || 'created_at'

  return [...items].sort((left, right) => {
    const leftValue = left[key] ?? ''
    const rightValue = right[key] ?? ''
    if (leftValue < rightValue) return -1 * direction
    if (leftValue > rightValue) return 1 * direction
    return (right.id || 0) - (left.id || 0)
  })
}

function filterPostsForQuery(items, searchParams) {
  const slug = normalizeText(searchParams.get('slug'))
  const category = normalizeText(searchParams.get('category'))
  const tag = normalizeText(searchParams.get('tag'))
  const search = normalizeText(searchParams.get('search'))
  const postType = normalizeText(searchParams.get('type'))
  const status = normalizeText(searchParams.get('status'))
  const visibility = normalizeText(searchParams.get('visibility'))
  const pinned = searchParams.get('pinned')

  return items.filter((post) => {
    if (slug && post.slug !== slug) return false
    if (category && normalizeText(post.category).toLowerCase() !== category.toLowerCase()) return false
    if (tag && !(post.tags || []).some((item) => normalizeText(item).toLowerCase() === tag.toLowerCase())) {
      return false
    }
    if (postType && normalizeText(post.post_type).toLowerCase() !== postType.toLowerCase()) return false
    if (status && normalizeText(post.status).toLowerCase() !== status.toLowerCase()) return false
    if (visibility && normalizeText(post.visibility).toLowerCase() !== visibility.toLowerCase()) return false
    if (pinned !== null && String(post.pinned) !== pinned) return false
    if (!search) return true

    const haystack = [
      post.title,
      post.slug,
      post.description,
      post.content,
      post.category,
      ...(post.tags || []),
    ]
      .filter(Boolean)
      .join('\n')
      .toLowerCase()

    return haystack.includes(search.toLowerCase())
  })
}

function buildPagedResponse(items, searchParams, mapper = (value) => value) {
  const page = Math.max(1, Number.parseInt(searchParams.get('page') || '1', 10) || 1)
  const pageSize = Math.max(1, Number.parseInt(searchParams.get('page_size') || '10', 10) || 10)
  const sortBy = searchParams.get('sort_by') || 'created_at'
  const sortOrder = searchParams.get('sort_order') || 'desc'
  const sorted = sortItems(items, sortBy, sortOrder)
  const total = sorted.length
  const totalPages = Math.max(1, Math.ceil(total / pageSize))
  const safePage = Math.min(page, totalPages)
  const start = (safePage - 1) * pageSize

  return {
    items: sorted.slice(start, start + pageSize).map(mapper),
    page: safePage,
    page_size: pageSize,
    total,
    total_pages: totalPages,
    sort_by: sortBy,
    sort_order: sortOrder,
  }
}

function makeSearchResult(post, rank) {
  return {
    id: post.id,
    title: post.title,
    slug: post.slug,
    description: post.description,
    content: post.content,
    category: post.category,
    tags: [...(post.tags || [])],
    post_type: post.post_type,
    image: post.image,
    pinned: post.pinned,
    created_at: post.created_at,
    updated_at: post.updated_at,
    rank,
  }
}

function searchPosts(query, filters = {}) {
  const normalizedQuery = normalizeText(query).toLowerCase()
  let items = state.posts.filter((post) => isPostVisible(post))
  if (filters.category) {
    items = items.filter((post) => normalizeText(post.category).toLowerCase() === normalizeText(filters.category).toLowerCase())
  }
  if (filters.tag) {
    items = items.filter((post) =>
      (post.tags || []).some((item) => normalizeText(item).toLowerCase() === normalizeText(filters.tag).toLowerCase()),
    )
  }
  if (filters.type) {
    items = items.filter((post) => normalizeText(post.post_type).toLowerCase() === normalizeText(filters.type).toLowerCase())
  }

  return items
    .map((post) => {
      const haystack = [post.title, post.description, post.content, post.category, ...(post.tags || [])]
        .filter(Boolean)
        .join('\n')
        .toLowerCase()
      let score = 0
      if (normalizedQuery && haystack.includes(normalizedQuery)) score += 20
      if (post.title.toLowerCase().includes(normalizedQuery)) score += 30
      if ((post.tags || []).some((tag) => tag.toLowerCase().includes(normalizedQuery))) score += 10
      if (post.pinned) score += 5
      return { post, score }
    })
    .filter((item) => item.score > 0)
    .sort((left, right) => right.score - left.score || right.post.id - left.post.id)
    .map((item) => makeSearchResult(item.post, item.score))
}

function issueCaptcha() {
  const token = randomUUID()
  state.captcha_tokens.set(token, { answer: '7', question: '3 + 4 = ?' })
  return { token, question: '3 + 4 = ?', expires_in_seconds: 300 }
}

function verifyCaptcha(token, answer) {
  const challenge = state.captcha_tokens.get(String(token || ''))
  return Boolean(challenge) && normalizeText(answer) === String(challenge.answer)
}

function getCommentMatcherValue(comment, matcherType) {
  if (matcherType === 'email') return normalizeText(comment.email).toLowerCase()
  if (matcherType === 'user_agent') return normalizeText(comment.user_agent).toLowerCase()
  return normalizeText(comment.ip_address).toLowerCase()
}

function isBlacklistedComment(comment) {
  return state.comment_blacklist.some((item) => {
    if (!item.active) return false
    return getCommentMatcherValue(comment, item.matcher_type) === normalizeText(item.matcher_value).toLowerCase()
  })
}

function createMockImageSvg(label, accent = '#14b8a6') {
  const safeLabel = String(label || 'mock asset')
  return `<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="675" viewBox="0 0 1200 675"><defs><linearGradient id="bg" x1="0" x2="1" y1="0" y2="1"><stop offset="0%" stop-color="#0f172a"/><stop offset="100%" stop-color="${accent}"/></linearGradient></defs><rect width="1200" height="675" fill="url(#bg)"/><text x="80" y="180" fill="#e2e8f0" font-family="monospace" font-size="44">${safeLabel}</text><text x="80" y="585" fill="#cbd5e1" font-family="monospace" font-size="24">playwright mock asset</text></svg>`
}

function sendSvg(res, label, accent) {
  text(res, 200, createMockImageSvg(label, accent), {
    'content-type': CONTENT_TYPES.svg,
    'cache-control': 'public, max-age=60',
  })
}

function writeEmpty(res, status = 204, headers = {}) {
  res.writeHead(status, headers)
  res.end()
}

function notFound(res, message = 'not found') {
  json(res, 404, { error: 'not_found', description: message })
}

function badRequest(res, message) {
  json(res, 400, { error: 'bad_request', description: message })
}

function latestDebugState() {
  return {
    site_settings: clone(state.site_settings),
    categories: state.categories.map((item) => clone(item)),
    tags: state.tags.map((item) => clone(item)),
    posts: state.posts.map((item) => ({
      id: item.id,
      slug: item.slug,
      title: item.title,
      category: item.category,
      tags: [...(item.tags || [])],
      status: item.status,
      visibility: item.visibility,
      content: item.content,
      updated_at: item.updated_at,
    })),
    reviews: state.reviews.map((item) => clone(item)),
    subscriptions: state.subscriptions.map((item) => ({
      id: item.id,
      target: item.target,
      display_name: item.display_name,
      channel_type: item.channel_type,
      confirm_token: item.confirm_token,
      manage_token: item.manage_token,
      status: item.status,
      filters: clone(item.filters),
    })),
    deliveries: state.deliveries.map((item) => clone(item)),
    worker_jobs: state.worker_jobs.map((item) => clone(item)),
    media: state.media.map((item) => ({
      key: item.key,
      title: item.title,
      alt_text: item.alt_text,
      tags: [...item.tags],
      url: item.url,
      size_bytes: item.size_bytes,
    })),
    friend_links: state.friend_links.map((item) => ({
      id: item.id,
      site_name: item.site_name,
      status: item.status,
    })),
    comments: state.comments.map((item) => ({
      id: item.id,
      post_slug: item.post_slug,
      scope: item.scope,
      approved: item.approved,
      author: item.author,
    })),
    comment_blacklist: state.comment_blacklist.map((item) => clone(item)),
    post_revisions: state.post_revisions.map((item) => ({
      id: item.id,
      post_slug: item.post_slug,
      operation: item.operation,
      revision_reason: item.revision_reason,
      created_at: item.created_at,
    })),
    audit_logs: state.audit_logs.map((item) => clone(item)),
  }
}

function getHomePayload() {
  const visiblePosts = state.posts.filter((post) => isPostVisible(post))
  const content_ranges = [
    {
      key: '24h',
      label: '24h',
      days: 1,
      overview: { page_views: 18, read_completes: 4, avg_read_progress: 61, avg_read_duration_ms: 42000 },
      popular_posts: [
        {
          slug: 'astro-terminal-blog',
          title: 'Astro 终端博客信息架构实战',
          page_views: 12,
          read_completes: 3,
          avg_progress_percent: 76,
          avg_duration_ms: 51000,
        },
      ],
    },
    {
      key: '7d',
      label: '7d',
      days: 7,
      overview: { page_views: 142, read_completes: 57, avg_read_progress: 74, avg_read_duration_ms: 58400 },
      popular_posts: [
        {
          slug: 'playwright-regression-workflow',
          title: 'Playwright 回归工作流设计',
          page_views: 48,
          read_completes: 25,
          avg_progress_percent: 83,
          avg_duration_ms: 61000,
        },
        {
          slug: 'astro-terminal-blog',
          title: 'Astro 终端博客信息架构实战',
          page_views: 38,
          read_completes: 18,
          avg_progress_percent: 71,
          avg_duration_ms: 54000,
        },
      ],
    },
    {
      key: '30d',
      label: '30d',
      days: 30,
      overview: { page_views: 580, read_completes: 223, avg_read_progress: 69, avg_read_duration_ms: 50000 },
      popular_posts: [
        {
          slug: 'playwright-regression-workflow',
          title: 'Playwright 回归工作流设计',
          page_views: 152,
          read_completes: 79,
          avg_progress_percent: 81,
          avg_duration_ms: 63000,
        },
        {
          slug: 'docker-rollout-checklist',
          title: 'Docker 发布值班清单',
          page_views: 118,
          read_completes: 42,
          avg_progress_percent: 64,
          avg_duration_ms: 47000,
        },
      ],
    },
  ]

  return {
    site_settings: clone(state.site_settings),
    posts: visiblePosts.map(normalizePostResponse),
    tags: state.tags.map((item) => clone(item)),
    friend_links: state.friend_links.map((item) => clone(item)),
    categories: state.categories.map((item) => clone(item)),
    content_overview: {
      total_page_views: 1642,
      page_views_last_24h: 18,
      page_views_last_7d: 142,
      total_read_completes: 628,
      read_completes_last_7d: 57,
      avg_read_progress_last_7d: 74,
      avg_read_duration_ms_last_7d: 58400,
    },
    popular_posts: content_ranges[1].popular_posts,
    content_ranges,
  }
}

const server = createServer(async (req, res) => {
  reflectCors(req, res)

  if (req.method === 'OPTIONS') {
    writeEmpty(res, 204)
    return
  }

  const url = new URL(req.url || '/', MOCK_ORIGIN)
  const { pathname, searchParams } = url

  if (pathname === '/__playwright/health') {
    json(res, 200, { ok: true })
    return
  }

  if (pathname === '/__playwright/reset' && req.method === 'POST') {
    resetState()
    json(res, 200, { ok: true })
    return
  }

  if (pathname === '/__playwright/state') {
    json(res, 200, latestDebugState())
    return
  }

  if (pathname.startsWith('/media-files/') || pathname.startsWith('/review-covers/') || pathname.startsWith('/generated/')) {
    sendSvg(res, pathname.split('/').pop()?.replace(/\.(svg|png|jpg|jpeg)$/i, '') || 'asset')
    return
  }

  if (pathname.startsWith('/media/')) {
    const key = decodeURIComponent(pathname.replace('/media/', ''))
    const media = state.media.find((item) => item.key === key)
    if (!media) {
      notFound(res, '媒体不存在。')
      return
    }
    if (media.content_type === CONTENT_TYPES.svg) {
      sendSvg(res, media.title || media.key, '#8b5cf6')
      return
    }
    text(res, 200, media.body, { 'content-type': media.content_type })
    return
  }

  if (pathname === '/api/admin/session' && req.method === 'GET') {
    json(res, 200, createSessionResponse(isAuthenticated(req)))
    return
  }

  if (pathname === '/api/admin/session/login' && req.method === 'POST') {
    const { json: payload } = await parseRequest(req)
    if (
      normalizeText(payload.username) !== VALID_LOGIN.username ||
      normalizeText(payload.password) !== VALID_LOGIN.password
    ) {
      badRequest(res, '用户名或密码错误。')
      return
    }
    json(res, 200, createSessionResponse(true), {
      'set-cookie': `${SESSION_COOKIE}=${SESSION_VALUE}; Path=/; HttpOnly; SameSite=Lax`,
    })
    return
  }

  if (pathname === '/api/admin/session' && req.method === 'DELETE') {
    json(res, 200, createSessionResponse(false), {
      'set-cookie': `${SESSION_COOKIE}=; Path=/; Max-Age=0; HttpOnly; SameSite=Lax`,
    })
    return
  }

  if (pathname === '/api/site_settings' && req.method === 'GET') {
    json(res, 200, clone(state.site_settings))
    return
  }

  if (pathname === '/api/site_settings/maintenance/status' && req.method === 'POST') {
    const { json: payload } = await parseRequest(req)
    const enabled = Boolean(state.site_settings.maintenance_mode_enabled)
    const secret = normalizeText(state.site_settings.maintenance_access_code)
    const expectedToken = secret ? maintenanceAccessTokenFromSecret(secret) : null
    const accessToken =
      normalizeText(payload.accessToken) ||
      normalizeText(payload.access_token)

    json(res, 200, {
      maintenance_mode_enabled: enabled,
      access_granted: !enabled || Boolean(expectedToken && accessToken === expectedToken),
    })
    return
  }

  if (pathname === '/api/site_settings/maintenance/verify' && req.method === 'POST') {
    const { json: payload } = await parseRequest(req)
    const enabled = Boolean(state.site_settings.maintenance_mode_enabled)
    const secret = normalizeText(state.site_settings.maintenance_access_code)
    const code = normalizeText(payload.code)

    if (!enabled) {
      json(res, 200, {
        maintenance_mode_enabled: false,
        access_granted: true,
        access_token: null,
      })
      return
    }

    const accessGranted = Boolean(secret && code && code === secret)
    json(res, 200, {
      maintenance_mode_enabled: true,
      access_granted: accessGranted,
      access_token: accessGranted ? maintenanceAccessTokenFromSecret(secret) : null,
    })
    return
  }

  if (pathname === '/api/site_settings/home' && req.method === 'GET') {
    json(res, 200, getHomePayload())
    return
  }

  if (pathname === '/api/categories' && req.method === 'GET') {
    json(res, 200, state.categories.map((item) => clone(item)))
    return
  }

  if (pathname === '/api/tags' && req.method === 'GET') {
    json(res, 200, state.tags.map((item) => clone(item)))
    return
  }

  if (pathname === '/api/reviews' && req.method === 'GET') {
    const items = isAuthenticated(req)
      ? state.reviews
      : state.reviews.filter((item) => isPublicReviewVisible(item))
    json(res, 200, items.map((item) => clone(item)))
    return
  }

  if (pathname === '/api/reviews' && req.method === 'POST') {
    const { json: payload } = await parseRequest(req)
    const title = normalizeText(payload.title)
    if (!title) {
      badRequest(res, '评测标题不能为空。')
      return
    }

    const record = {
      id: nextId('review'),
      title,
      review_type: normalizeText(payload.review_type) || 'book',
      rating: Number(payload.rating) || 4,
      review_date: normalizeText(payload.review_date) || '2026-04-01',
      status: normalizeText(payload.status) || 'draft',
      description: normalizeText(payload.description) || '',
      tags: JSON.stringify(Array.isArray(payload.tags) ? payload.tags.filter(Boolean) : []),
      cover: normalizeText(payload.cover) || '',
      link_url: normalizeText(payload.link_url) || null,
      created_at: iso(-1),
      updated_at: iso(-1),
    }

    state.reviews.unshift(record)
    addAuditLog('review.create', 'review', record.title, record.id)
    json(res, 201, clone(record))
    return
  }

  if (pathname.match(/^\/api\/reviews\/\d+$/) && req.method === 'GET') {
    const id = Number(pathname.split('/').pop())
    const review = state.reviews.find((item) => item.id === id)
    if (!review || (!isAuthenticated(req) && !isPublicReviewVisible(review))) {
      notFound(res, '评测不存在。')
      return
    }
    json(res, 200, clone(review))
    return
  }

  if (pathname.match(/^\/api\/reviews\/\d+$/) && req.method === 'PUT') {
    const id = Number(pathname.split('/').pop())
    const record = state.reviews.find((item) => item.id === id)
    if (!record) {
      notFound(res, '评测不存在。')
      return
    }
    const { json: payload } = await parseRequest(req)
    record.title = normalizeText(payload.title) || record.title
    record.review_type = normalizeText(payload.review_type) || record.review_type
    record.rating = Number(payload.rating) || record.rating
    record.review_date = normalizeText(payload.review_date) || record.review_date
    record.status = normalizeText(payload.status) || record.status
    record.description = normalizeText(payload.description) || ''
    record.tags = JSON.stringify(Array.isArray(payload.tags) ? payload.tags.filter(Boolean) : [])
    record.cover = normalizeText(payload.cover) || ''
    record.link_url = normalizeText(payload.link_url) || null
    record.updated_at = iso(-1)
    addAuditLog('review.update', 'review', record.title, record.id)
    json(res, 200, clone(record))
    return
  }

  if (pathname.match(/^\/api\/reviews\/\d+$/) && req.method === 'DELETE') {
    const id = Number(pathname.split('/').pop())
    const index = state.reviews.findIndex((item) => item.id === id)
    if (index === -1) {
      notFound(res, '评测不存在。')
      return
    }
    const [removed] = state.reviews.splice(index, 1)
    addAuditLog('review.delete', 'review', removed.title, removed.id)
    writeEmpty(res, 204)
    return
  }

  if (pathname === '/api/friend_links' && req.method === 'GET') {
    let items = [...state.friend_links]
    const status = normalizeText(searchParams.get('status'))
    const category = normalizeText(searchParams.get('category'))
    if (status) items = items.filter((item) => normalizeText(item.status).toLowerCase() === status.toLowerCase())
    if (category) items = items.filter((item) => normalizeText(item.category).toLowerCase() === category.toLowerCase())
    json(res, 200, items.map((item) => clone(item)))
    return
  }

  if (pathname === '/api/friend_links' && req.method === 'POST') {
    const { json: payload } = await parseRequest(req)
    const record = {
      id: nextId('friend_link'),
      site_name: normalizeText(payload.siteName || payload.site_name),
      site_url: normalizeText(payload.siteUrl || payload.site_url),
      avatar_url: null,
      description: normalizeText(payload.description) || null,
      category: normalizeText(payload.category) || 'other',
      status: normalizeText(payload.status) || 'pending',
      created_at: iso(-1),
      updated_at: iso(-1),
    }
    if (!record.site_name || !record.site_url) {
      badRequest(res, '站点名称和 URL 不能为空。')
      return
    }
    state.friend_links.unshift(record)
    addAuditLog('friend_link.create', 'friend_link', record.site_name, record.id, { status: record.status })
    json(res, 201, clone(record))
    return
  }

  if (pathname === '/api/comments/captcha' && req.method === 'GET') {
    json(res, 200, issueCaptcha())
    return
  }

  if (pathname === '/api/comments/paragraphs/summary' && req.method === 'GET') {
    const postSlug = normalizeText(searchParams.get('post_slug'))
    const counts = new Map()
    for (const comment of state.comments) {
      if (comment.post_slug !== postSlug || comment.scope !== 'paragraph' || !comment.approved) continue
      if (!comment.paragraph_key) continue
      counts.set(comment.paragraph_key, (counts.get(comment.paragraph_key) || 0) + 1)
    }
    json(res, 200, Array.from(counts.entries()).map(([paragraph_key, count]) => ({ paragraph_key, count })))
    return
  }

  if (pathname === '/api/comments' && req.method === 'GET') {
    let items = [...state.comments]
    const postSlug = normalizeText(searchParams.get('post_slug'))
    const scope = normalizeText(searchParams.get('scope'))
    const paragraphKey = normalizeText(searchParams.get('paragraph_key'))
    const approved = searchParams.get('approved')
    if (postSlug) items = items.filter((item) => item.post_slug === postSlug)
    if (scope) items = items.filter((item) => item.scope === scope)
    if (paragraphKey) items = items.filter((item) => item.paragraph_key === paragraphKey)
    if (approved !== null) items = items.filter((item) => String(Boolean(item.approved)) === approved)
    json(res, 200, items.map((item) => clone(item)))
    return
  }

  if (pathname === '/api/comments' && req.method === 'POST') {
    const { json: payload } = await parseRequest(req)
    const post = state.posts.find((item) => item.slug === normalizeText(payload.postSlug))
    if (!post) {
      notFound(res, '文章不存在。')
      return
    }
    if (state.site_settings.comment_verification_mode === 'captcha' && !verifyCaptcha(payload.captchaToken, payload.captchaAnswer)) {
      badRequest(res, '验证码错误。')
      return
    }
    const record = {
      id: nextId('comment'),
      post_id: String(post.id),
      post_slug: post.slug,
      author: normalizeText(payload.nickname) || '匿名访客',
      email: normalizeText(payload.email) || null,
      avatar: null,
      ip_address: '10.0.0.88',
      user_agent: String(req.headers['user-agent'] || 'Playwright'),
      referer: String(req.headers.referer || ''),
      content: normalizeText(payload.content),
      reply_to: null,
      reply_to_comment_id: payload.replyToCommentId === null || payload.replyToCommentId === undefined ? null : Number(payload.replyToCommentId),
      scope: normalizeText(payload.scope) || 'article',
      paragraph_key: normalizeText(payload.paragraphKey) || null,
      paragraph_excerpt: normalizeText(payload.paragraphExcerpt) || null,
      approved: false,
      created_at: iso(-1),
      updated_at: iso(-1),
    }
    if (!record.content) {
      badRequest(res, '评论内容不能为空。')
      return
    }
    if (isBlacklistedComment(record)) {
      json(res, 403, { error: 'comment_blocked', description: '该评论命中了黑名单规则，已被拒绝。' })
      return
    }
    state.comments.unshift(record)
    addAuditLog('comment.create', 'comment', record.author, record.id, { scope: record.scope, approved: record.approved })
    json(res, 201, clone(record))
    return
  }

  if (pathname === '/api/posts' && req.method === 'GET') {
    const candidates = state.posts.filter((post) =>
      isPostVisible(post, {
        include_private: searchParams.get('include_private'),
        preview: searchParams.get('preview'),
        include_redirects: searchParams.get('include_redirects'),
      }),
    )
    const filtered = filterPostsForQuery(candidates, searchParams)
    json(res, 200, filtered.map(normalizePostResponse))
    return
  }

  if (pathname === '/api/posts/page' && req.method === 'GET') {
    const candidates = state.posts.filter((post) =>
      isPostVisible(post, {
        include_private: searchParams.get('include_private'),
        preview: searchParams.get('preview'),
        include_redirects: searchParams.get('include_redirects'),
      }),
    )
    const filtered = filterPostsForQuery(candidates, searchParams)
    json(res, 200, buildPagedResponse(filtered, searchParams, normalizePostResponse))
    return
  }

  if (pathname.match(/^\/api\/posts\/\d+$/) && req.method === 'GET') {
    const id = Number(pathname.split('/').pop())
    const post = state.posts.find((item) => item.id === id)
    if (!post || !isPostVisible(post, { preview: true, include_private: true, include_redirects: true })) {
      notFound(res, '文章不存在。')
      return
    }
    json(res, 200, normalizePostResponse(post))
    return
  }

  if (pathname.match(/^\/api\/posts\/slug\/[^/]+$/) && req.method === 'GET') {
    const slug = decodeURIComponent(pathname.split('/')[4])
    const post = state.posts.find((item) => item.slug === slug)
    if (!post || !isPostVisible(post, {
      include_private: searchParams.get('include_private'),
      preview: searchParams.get('preview'),
      include_redirects: searchParams.get('include_redirects'),
    })) {
      notFound(res, '文章不存在。')
      return
    }
    json(res, 200, normalizePostResponse(post))
    return
  }

  if (pathname === '/api/posts/markdown' && req.method === 'POST') {
    const { json: payload } = await parseRequest(req)
    const record = upsertPostFromPayload(
      {
        id: nextId('post'),
        created_at: iso(-1),
        updated_at: iso(-1),
      },
      payload,
    )
    if (!record.slug || !record.title) {
      badRequest(res, '文章标题不能为空。')
      return
    }

    state.posts.unshift(record)
    recalculateTaxonomyCounts()
    addAuditLog('post.create', 'post', record.title, record.id, { slug: record.slug })
    addPostRevision(record, 'create', '创建草稿')
    json(res, 201, {
      slug: record.slug,
      path: `content/posts/${record.slug}.md`,
      markdown: record.content,
    })
    return
  }

  if (pathname === '/api/posts/markdown/import' && req.method === 'POST') {
    const { files } = await parseRequest(req)
    const slugs = []

    for (const file of files) {
      const source = file.body?.toString('utf8') || '# Imported Post\n\nImported by playwright.'
      const { title, description } = parseHeadingAndDescription(source, sanitizeFilename(file.filename, 'imported'))
      const slug = slugify(title || sanitizeFilename(file.filename, 'imported'))
      const record = {
        id: nextId('post'),
        title,
        slug,
        description,
        content: source,
        category: '测试体系',
        tags: ['Playwright'],
        post_type: 'article',
        image: null,
        images: [],
        pinned: false,
        status: 'draft',
        visibility: 'public',
        publish_at: null,
        unpublish_at: null,
        canonical_url: null,
        noindex: false,
        og_image: null,
        redirect_from: [],
        redirect_to: null,
        created_at: iso(-1),
        updated_at: iso(-1),
      }
      state.posts.unshift(record)
      addPostRevision(record, 'import', `导入 ${file.filename}`)
      slugs.push(slug)
    }

    recalculateTaxonomyCounts()
    addAuditLog('post.import', 'workspace', 'markdown import', String(slugs.length), { slugs })
    json(res, 200, { count: slugs.length, slugs })
    return
  }

  if (pathname.match(/^\/api\/posts\/\d+$/) && req.method === 'PATCH') {
    const id = Number(pathname.split('/').pop())
    const record = state.posts.find((item) => item.id === id)
    if (!record) {
      notFound(res, '文章不存在。')
      return
    }
    const { json: payload } = await parseRequest(req)
    const updated = upsertPostFromPayload(record, payload)
    Object.assign(record, updated)
    recalculateTaxonomyCounts()
    addAuditLog('post.update', 'post', record.title, record.id, { slug: record.slug })
    addPostRevision(record, 'update', '保存文章属性')
    json(res, 200, normalizePostResponse(record))
    return
  }

  if (pathname === '/api/search' && req.method === 'GET') {
    const q = searchParams.get('q') || ''
    const limit = Math.max(1, Number.parseInt(searchParams.get('limit') || '20', 10) || 20)
    json(res, 200, searchPosts(q).slice(0, limit))
    return
  }

  if (pathname === '/api/search/page' && req.method === 'GET') {
    const q = searchParams.get('q') || ''
    const results = searchPosts(q, {
      category: searchParams.get('category'),
      tag: searchParams.get('tag'),
      type: searchParams.get('type'),
    })
    json(res, 200, { query: q, ...buildPagedResponse(results, searchParams) })
    return
  }

  if (pathname === '/api/subscriptions' && req.method === 'POST') {
    const { json: payload } = await parseRequest(req)
    const id = nextId('subscription')
    const record = createSubscriptionRecord(id, {
      target: normalizeText(payload.email) || `subscriber-${id}@example.com`,
      display_name: normalizeText(payload.displayName) || null,
      status: 'pending_confirmation',
      filters: { event_types: ['post.published', 'digest.weekly'], categories: [], tags: [] },
      metadata: { source: normalizeText(payload.source) || 'popup' },
      verified_at: null,
      last_notified_at: null,
      last_delivery_status: null,
      created_at: iso(-1),
      updated_at: iso(-1),
    })
    state.subscriptions.unshift(record)
    addAuditLog('subscription.create', 'subscription', record.target, record.id, { status: record.status })
    json(res, 200, {
      ok: true,
      subscription_id: record.id,
      status: record.status,
      requires_confirmation: true,
      message: '订阅已登记，请前往确认页完成激活。',
    })
    return
  }

  if (pathname === '/api/subscriptions/confirm' && req.method === 'POST') {
    const { json: payload } = await parseRequest(req)
    const record = state.subscriptions.find((item) => item.confirm_token === normalizeText(payload.token))
    if (!record) {
      badRequest(res, '确认令牌无效。')
      return
    }
    record.status = 'active'
    record.verified_at = iso(-1)
    record.updated_at = iso(-1)
    json(res, 200, { ok: true, subscription: clone(record) })
    return
  }

  if (pathname === '/api/subscriptions/manage' && req.method === 'GET') {
    const token = normalizeText(searchParams.get('token'))
    const record = state.subscriptions.find((item) => item.manage_token === token)
    if (!record) {
      badRequest(res, '管理令牌无效。')
      return
    }
    json(res, 200, { ok: true, subscription: clone(record) })
    return
  }

  if (pathname === '/api/subscriptions/manage' && req.method === 'PATCH') {
    const { json: payload } = await parseRequest(req)
    const record = state.subscriptions.find((item) => item.manage_token === normalizeText(payload.token))
    if (!record) {
      badRequest(res, '管理令牌无效。')
      return
    }
    record.display_name = payload.displayName ?? record.display_name
    record.status = normalizeText(payload.status) || record.status
    record.filters = payload.filters ?? record.filters
    record.updated_at = iso(-1)
    json(res, 200, { ok: true, subscription: clone(record) })
    return
  }

  if (pathname === '/api/subscriptions/unsubscribe' && req.method === 'POST') {
    const { json: payload } = await parseRequest(req)
    const record = state.subscriptions.find((item) => item.manage_token === normalizeText(payload.token))
    if (!record) {
      badRequest(res, '管理令牌无效。')
      return
    }
    record.status = 'unsubscribed'
    record.updated_at = iso(-1)
    json(res, 200, { ok: true, subscription: clone(record) })
    return
  }

  if (pathname === '/api/analytics/content' && req.method === 'POST') {
    const { json: payload } = await parseRequest(req)
    state.content_events.unshift({ ...payload, created_at: iso(-1) })
    writeEmpty(res, 204)
    return
  }

  if (pathname === '/api/ai/ask/stream' && req.method === 'POST') {
    const { json: payload } = await parseRequest(req)
    const question = normalizeText(payload.question) || '未提供问题'
    const completePayload = {
      question,
      answer:
        'Termi 的内容主要集中在前端工程、回归测试、值班运维和 AI 工作流。\n\n你可以优先看 Playwright 回归工作流、Astro 终端博客信息架构，以及 Docker 发布值班清单这三篇。',
      sources: state.posts.filter((item) => isPostVisible(item)).slice(0, 3).map((item, index) => ({
        slug: item.slug,
        href: `${FRONTEND_ORIGIN}/articles/${item.slug}`,
        title: item.title,
        excerpt: item.description,
        score: 0.94 - index * 0.07,
        chunk_index: index,
      })),
      indexed_chunks: state.site_settings.ai_chunks_count,
      last_indexed_at: state.site_settings.ai_last_indexed_at,
    }
    res.writeHead(200, {
      'content-type': CONTENT_TYPES.sse,
      'cache-control': 'no-store',
      connection: 'keep-alive',
    })
    res.write(`event: status\ndata: ${JSON.stringify({ stage: 'searching' })}\n\n`)
    for (const chunk of [
      'Termi 的内容主要集中在前端工程、回归测试、值班运维和 AI 工作流。',
      '\n\n你可以优先看 Playwright 回归工作流、Astro 终端博客信息架构，',
      '以及 Docker 发布值班清单这三篇。',
    ]) {
      res.write(`event: delta\ndata: ${JSON.stringify({ delta: chunk })}\n\n`)
    }
    res.write(`event: complete\ndata: ${JSON.stringify(completePayload)}\n\n`)
    res.end()
    return
  }

  if (pathname.startsWith('/api/admin/')) {
    if (!ensureAdmin(req, res)) return

    if (pathname === '/api/admin/dashboard' && req.method === 'GET') {
      const pendingComments = state.comments.filter((item) => !item.approved)
      const pendingFriendLinks = state.friend_links.filter((item) => item.status === 'pending')
      json(res, 200, {
        stats: {
          total_posts: state.posts.length,
          total_comments: state.comments.length,
          pending_comments: pendingComments.length,
          draft_posts: state.posts.filter((item) => item.status === 'draft').length,
          scheduled_posts: state.posts.filter((item) => item.status === 'scheduled').length,
          offline_posts: 0,
          expired_posts: 0,
          private_posts: state.posts.filter((item) => item.visibility === 'private').length,
          unlisted_posts: state.posts.filter((item) => item.visibility === 'unlisted').length,
          total_categories: state.categories.length,
          total_tags: state.tags.length,
          total_reviews: state.reviews.length,
          total_links: state.friend_links.length,
          pending_links: pendingFriendLinks.length,
          ai_chunks: state.site_settings.ai_chunks_count,
          ai_enabled: state.site_settings.ai_enabled,
        },
        site: {
          site_name: state.site_settings.site_name,
          site_url: state.site_settings.site_url,
          ai_enabled: state.site_settings.ai_enabled,
          ai_chunks: state.site_settings.ai_chunks_count,
          ai_last_indexed_at: state.site_settings.ai_last_indexed_at,
        },
        recent_posts: state.posts.slice(0, 5).map((item) => ({
          id: item.id,
          title: item.title,
          slug: item.slug,
          category: item.category,
          post_type: item.post_type,
          pinned: item.pinned,
          status: item.status,
          visibility: item.visibility,
          created_at: item.created_at,
        })),
        pending_comments: pendingComments.slice(0, 5).map((item) => ({
          id: item.id,
          author: item.author,
          post_slug: item.post_slug,
          scope: item.scope,
          excerpt: item.content?.slice(0, 80) || '',
          approved: Boolean(item.approved),
          created_at: item.created_at,
        })),
        pending_friend_links: pendingFriendLinks.slice(0, 5).map((item) => ({
          id: item.id,
          site_name: item.site_name,
          site_url: item.site_url,
          category: item.category,
          status: item.status,
          created_at: item.created_at,
        })),
        recent_reviews: state.reviews.slice(0, 5).map((item) => ({
          id: item.id,
          title: item.title,
          review_type: item.review_type,
          rating: item.rating,
          status: item.status,
          review_date: item.review_date,
        })),
      })
      return
    }

    if (pathname === '/api/admin/analytics' && req.method === 'GET') {
      json(res, 200, {
        overview: {
          total_searches: 218,
          total_ai_questions: 64,
          searches_last_24h: 12,
          ai_questions_last_24h: 5,
          searches_last_7d: 66,
          ai_questions_last_7d: 18,
          unique_search_terms_last_7d: 21,
          unique_ai_questions_last_7d: 9,
          avg_search_results_last_7d: 5.4,
          avg_ai_latency_ms_last_7d: 286,
        },
        content_overview: {
          total_page_views: 1642,
          page_views_last_24h: 18,
          page_views_last_7d: 142,
          total_read_completes: 628,
          read_completes_last_7d: 57,
          avg_read_progress_last_7d: 74,
          avg_read_duration_ms_last_7d: 58400,
        },
        top_search_terms: [{ query: 'playwright', count: 22, last_seen_at: iso(-8) }],
        top_ai_questions: [{ query: '这个博客主要写什么内容？', count: 8, last_seen_at: iso(-6) }],
        recent_events: [],
        providers_last_7d: [{ provider: 'mock-openai', count: 18 }],
        top_referrers: [{ referrer: 'homepage', count: 44 }],
        ai_referrers_last_7d: [
          { referrer: 'chatgpt-search', count: 21 },
          { referrer: 'perplexity', count: 9 },
          { referrer: 'copilot-bing', count: 6 },
        ],
        ai_discovery_page_views_last_7d: 36,
        popular_posts: getHomePayload().popular_posts,
        daily_activity: [
          { date: '2026-03-29', searches: 6, ai_questions: 3 },
          { date: '2026-03-30', searches: 7, ai_questions: 4 },
          { date: '2026-03-31', searches: 11, ai_questions: 6 },
          { date: '2026-04-01', searches: 9, ai_questions: 5 },
        ],
      })
      return
    }

    if (pathname === '/api/admin/categories' && req.method === 'GET') {
      json(res, 200, state.categories.map((item) => clone(item)))
      return
    }

    if (pathname === '/api/admin/categories' && req.method === 'POST') {
      const { json: payload } = await parseRequest(req)
      const record = makeTaxonomyRecord('category', payload)
      state.categories.unshift(record)
      recalculateTaxonomyCounts()
      addAuditLog('category.create', 'category', record.name, record.id)
      json(res, 201, clone(record))
      return
    }

    if (pathname.match(/^\/api\/admin\/categories\/\d+$/) && req.method === 'PATCH') {
      const id = Number(pathname.split('/').pop())
      const index = state.categories.findIndex((item) => item.id === id)
      if (index === -1) {
        notFound(res, '分类不存在。')
        return
      }
      const current = state.categories[index]
      const { json: payload } = await parseRequest(req)
      const previousName = current.name
      const updated = makeTaxonomyRecord('category', payload, current)
      state.categories[index] = updated
      if (previousName !== updated.name) {
        state.posts.forEach((post) => {
          if (post.category === previousName) {
            post.category = updated.name
          }
        })
      }
      recalculateTaxonomyCounts()
      addAuditLog('category.update', 'category', updated.name, updated.id)
      json(res, 200, clone(updated))
      return
    }

    if (pathname.match(/^\/api\/admin\/categories\/\d+$/) && req.method === 'DELETE') {
      const id = Number(pathname.split('/').pop())
      const index = state.categories.findIndex((item) => item.id === id)
      if (index === -1) {
        notFound(res, '分类不存在。')
        return
      }
      const [removed] = state.categories.splice(index, 1)
      state.posts.forEach((post) => {
        if (post.category === removed.name) {
          post.category = null
        }
      })
      recalculateTaxonomyCounts()
      addAuditLog('category.delete', 'category', removed.name, removed.id)
      writeEmpty(res, 204)
      return
    }

    if (pathname === '/api/admin/tags' && req.method === 'GET') {
      json(res, 200, state.tags.map((item) => clone(item)))
      return
    }

    if (pathname === '/api/admin/tags' && req.method === 'POST') {
      const { json: payload } = await parseRequest(req)
      const record = makeTaxonomyRecord('tag', payload)
      state.tags.unshift(record)
      recalculateTaxonomyCounts()
      addAuditLog('tag.create', 'tag', record.name, record.id)
      json(res, 201, clone(record))
      return
    }

    if (pathname.match(/^\/api\/admin\/tags\/\d+$/) && req.method === 'PATCH') {
      const id = Number(pathname.split('/').pop())
      const index = state.tags.findIndex((item) => item.id === id)
      if (index === -1) {
        notFound(res, '标签不存在。')
        return
      }
      const current = state.tags[index]
      const { json: payload } = await parseRequest(req)
      const previousName = current.name
      const updated = makeTaxonomyRecord('tag', payload, current)
      state.tags[index] = updated
      if (previousName !== updated.name) {
        state.posts.forEach((post) => {
          post.tags = (post.tags || []).map((tag) => (tag === previousName ? updated.name : tag))
        })
      }
      recalculateTaxonomyCounts()
      addAuditLog('tag.update', 'tag', updated.name, updated.id)
      json(res, 200, clone(updated))
      return
    }

    if (pathname.match(/^\/api\/admin\/tags\/\d+$/) && req.method === 'DELETE') {
      const id = Number(pathname.split('/').pop())
      const index = state.tags.findIndex((item) => item.id === id)
      if (index === -1) {
        notFound(res, '标签不存在。')
        return
      }
      const [removed] = state.tags.splice(index, 1)
      state.posts.forEach((post) => {
        post.tags = (post.tags || []).filter((tag) => tag !== removed.name)
      })
      recalculateTaxonomyCounts()
      addAuditLog('tag.delete', 'tag', removed.name, removed.id)
      writeEmpty(res, 204)
      return
    }

    if (pathname === '/api/admin/site-settings' && req.method === 'GET') {
      json(res, 200, clone(state.site_settings))
      return
    }

    if (pathname === '/api/admin/site-settings' && req.method === 'PATCH') {
      const { json: payload } = await parseRequest(req)
      const fieldMap = {
        siteName: 'site_name',
        siteShortName: 'site_short_name',
        siteUrl: 'site_url',
        siteTitle: 'site_title',
        siteDescription: 'site_description',
        heroTitle: 'hero_title',
        heroSubtitle: 'hero_subtitle',
        ownerName: 'owner_name',
        ownerTitle: 'owner_title',
        ownerBio: 'owner_bio',
        ownerAvatarUrl: 'owner_avatar_url',
        socialGithub: 'social_github',
        socialTwitter: 'social_twitter',
        socialEmail: 'social_email',
        location: 'location',
        techStack: 'tech_stack',
        musicPlaylist: 'music_playlist',
        aiEnabled: 'ai_enabled',
        maintenanceModeEnabled: 'maintenance_mode_enabled',
        maintenanceAccessCode: 'maintenance_access_code',
        paragraphCommentsEnabled: 'paragraph_comments_enabled',
        commentVerificationMode: 'comment_verification_mode',
        commentTurnstileEnabled: 'comment_turnstile_enabled',
        subscriptionVerificationMode: 'subscription_verification_mode',
        subscriptionTurnstileEnabled: 'subscription_turnstile_enabled',
        webPushEnabled: 'web_push_enabled',
        turnstileSiteKey: 'turnstile_site_key',
        turnstileSecretKey: 'turnstile_secret_key',
        webPushVapidPublicKey: 'web_push_vapid_public_key',
        webPushVapidPrivateKey: 'web_push_vapid_private_key',
        webPushVapidSubject: 'web_push_vapid_subject',
        aiProvider: 'ai_provider',
        aiApiBase: 'ai_api_base',
        aiApiKey: 'ai_api_key',
        aiChatModel: 'ai_chat_model',
        aiImageProvider: 'ai_image_provider',
        aiImageApiBase: 'ai_image_api_base',
        aiImageApiKey: 'ai_image_api_key',
        aiImageModel: 'ai_image_model',
        aiProviders: 'ai_providers',
        aiActiveProviderId: 'ai_active_provider_id',
        aiEmbeddingModel: 'ai_embedding_model',
        aiSystemPrompt: 'ai_system_prompt',
        aiTopK: 'ai_top_k',
        aiChunkSize: 'ai_chunk_size',
        mediaStorageProvider: 'media_storage_provider',
        mediaR2AccountId: 'media_r2_account_id',
        mediaR2Bucket: 'media_r2_bucket',
        mediaR2PublicBaseUrl: 'media_r2_public_base_url',
        mediaR2AccessKeyId: 'media_r2_access_key_id',
        mediaR2SecretAccessKey: 'media_r2_secret_access_key',
        seoFaviconUrl: 'seo_favicon_url',
        seoDefaultOgImage: 'seo_default_og_image',
        seoDefaultTwitterHandle: 'seo_default_twitter_handle',
        seoWechatShareQrEnabled: 'seo_wechat_share_qr_enabled',
        notificationWebhookUrl: 'notification_webhook_url',
        notificationChannelType: 'notification_channel_type',
        notificationCommentEnabled: 'notification_comment_enabled',
        notificationFriendLinkEnabled: 'notification_friend_link_enabled',
        subscriptionPopupEnabled: 'subscription_popup_enabled',
        subscriptionPopupTitle: 'subscription_popup_title',
        subscriptionPopupDescription: 'subscription_popup_description',
        subscriptionPopupDelaySeconds: 'subscription_popup_delay_seconds',
        searchSynonyms: 'search_synonyms',
      }

      for (const [sourceKey, targetKey] of Object.entries(fieldMap)) {
        if (Object.hasOwn(payload, sourceKey)) {
          state.site_settings[targetKey] = payload[sourceKey]
        }
      }
      addAuditLog('site_settings.update', 'site_settings', state.site_settings.site_name, '1')
      json(res, 200, clone(state.site_settings))
      return
    }

    if (pathname === '/api/admin/audit-logs' && req.method === 'GET') {
      json(res, 200, state.audit_logs.map((item) => clone(item)))
      return
    }

    if (pathname === '/api/admin/post-revisions' && req.method === 'GET') {
      let items = [...state.post_revisions]
      const slug = normalizeText(searchParams.get('slug'))
      const limit = Number.parseInt(searchParams.get('limit') || '0', 10) || 0
      if (slug) {
        items = items.filter((item) => item.post_slug === slug)
      }
      if (limit > 0) {
        items = items.slice(0, limit)
      }
      json(res, 200, items.map((item) => ({
        id: item.id,
        post_slug: item.post_slug,
        post_title: item.post_title,
        operation: item.operation,
        revision_reason: item.revision_reason,
        actor_username: item.actor_username,
        actor_email: item.actor_email,
        actor_source: item.actor_source,
        created_at: item.created_at,
        has_markdown: item.has_markdown,
        metadata: clone(item.metadata),
      })))
      return
    }

    if (pathname.match(/^\/api\/admin\/post-revisions\/\d+$/) && req.method === 'GET') {
      const id = Number(pathname.split('/').pop())
      const revision = findRevisionById(id)
      if (!revision) {
        notFound(res, '版本不存在。')
        return
      }
      json(res, 200, {
        item: {
          id: revision.id,
          post_slug: revision.post_slug,
          post_title: revision.post_title,
          operation: revision.operation,
          revision_reason: revision.revision_reason,
          actor_username: revision.actor_username,
          actor_email: revision.actor_email,
          actor_source: revision.actor_source,
          created_at: revision.created_at,
          has_markdown: revision.has_markdown,
          metadata: clone(revision.metadata),
        },
        markdown: revision.markdown,
      })
      return
    }

    if (pathname.match(/^\/api\/admin\/post-revisions\/\d+\/restore$/) && req.method === 'POST') {
      const id = Number(pathname.split('/')[4])
      const revision = findRevisionById(id)
      if (!revision) {
        notFound(res, '版本不存在。')
        return
      }
      const { json: payload } = await parseRequest(req)
      const mode = normalizeText(payload.mode) || 'full'
      const post = state.posts.find((item) => item.slug === revision.post_slug)
      if (!post) {
        notFound(res, '原文章不存在。')
        return
      }
      if (mode === 'full') {
        Object.assign(post, clone(revision.snapshot))
      }
      if (mode === 'metadata') {
        const snapshot = revision.snapshot || {}
        Object.assign(post, {
          title: snapshot.title,
          description: snapshot.description,
          category: snapshot.category,
          tags: clone(snapshot.tags || []),
          post_type: snapshot.post_type,
          image: snapshot.image,
          images: clone(snapshot.images || []),
          pinned: snapshot.pinned,
          status: snapshot.status,
          visibility: snapshot.visibility,
          publish_at: snapshot.publish_at,
          unpublish_at: snapshot.unpublish_at,
          canonical_url: snapshot.canonical_url,
          noindex: snapshot.noindex,
          og_image: snapshot.og_image,
          redirect_from: clone(snapshot.redirect_from || []),
          redirect_to: snapshot.redirect_to,
        })
      }
      if (mode === 'markdown') {
        post.content = revision.markdown
      }
      post.updated_at = iso(-1)
      recalculateTaxonomyCounts()
      addAuditLog('post.restore', 'post', post.title, post.id, { revision_id: id, mode })
      addPostRevision(post, 'restore', `从版本 #${id} 恢复（${mode}）`)
      json(res, 200, { restored: true, revision_id: id, post_slug: post.slug, mode })
      return
    }

    if (pathname === '/api/admin/comments/blacklist' && req.method === 'GET') {
      json(res, 200, state.comment_blacklist.map((item) => clone(item)))
      return
    }

    if (pathname === '/api/admin/comments/blacklist' && req.method === 'POST') {
      const { json: payload } = await parseRequest(req)
      const record = {
        id: nextId('blacklist'),
        matcher_type: normalizeText(payload.matcher_type) || 'ip',
        matcher_value: normalizeText(payload.matcher_value),
        reason: normalizeText(payload.reason) || null,
        active: Object.hasOwn(payload, 'active') ? Boolean(payload.active) : true,
        expires_at: normalizeText(payload.expires_at) || null,
        created_at: iso(-1),
        updated_at: iso(-1),
        effective: Object.hasOwn(payload, 'active') ? Boolean(payload.active) : true,
      }
      state.comment_blacklist.unshift(record)
      addAuditLog('comment_blacklist.create', 'comment_blacklist', record.matcher_value, record.id)
      json(res, 201, clone(record))
      return
    }

    if (pathname.match(/^\/api\/admin\/comments\/blacklist\/\d+$/) && req.method === 'PATCH') {
      const id = Number(pathname.split('/').pop())
      const record = state.comment_blacklist.find((item) => item.id === id)
      if (!record) {
        notFound(res, '黑名单规则不存在。')
        return
      }
      const { json: payload } = await parseRequest(req)
      if (Object.hasOwn(payload, 'reason')) record.reason = normalizeText(payload.reason) || null
      if (Object.hasOwn(payload, 'active')) record.active = Boolean(payload.active)
      if (Object.hasOwn(payload, 'expires_at')) record.expires_at = normalizeText(payload.expires_at) || null
      if (payload.clear_expires_at) record.expires_at = null
      record.updated_at = iso(-1)
      record.effective = Boolean(record.active) && !record.expires_at
      addAuditLog('comment_blacklist.update', 'comment_blacklist', record.matcher_value, record.id)
      json(res, 200, clone(record))
      return
    }

    if (pathname.match(/^\/api\/admin\/comments\/blacklist\/\d+$/) && req.method === 'DELETE') {
      const id = Number(pathname.split('/').pop())
      const index = state.comment_blacklist.findIndex((item) => item.id === id)
      if (index === -1) {
        notFound(res, '黑名单规则不存在。')
        return
      }
      const [removed] = state.comment_blacklist.splice(index, 1)
      addAuditLog('comment_blacklist.delete', 'comment_blacklist', removed.matcher_value, removed.id)
      json(res, 200, { deleted: true, id })
      return
    }

    if (pathname === '/api/admin/comments/analyze' && req.method === 'POST') {
      const { json: payload } = await parseRequest(req)
      const matcherType = normalizeText(payload.matcher_type) || 'ip'
      const matcherValue = normalizeText(payload.matcher_value)
      const matches = state.comments.filter((comment) =>
        getCommentMatcherValue(comment, matcherType) === matcherValue.toLowerCase(),
      )
      const analysis = `该来源共出现 ${matches.length} 条评论，其中待审核 ${
        matches.filter((item) => !item.approved).length
      } 条；建议保持观察并视情况加入黑名单。`
      const log = {
        id: nextId('persona_log'),
        matcher_type: matcherType,
        matcher_value: matcherValue,
        from_at: matches.at(-1)?.created_at ?? null,
        to_at: matches[0]?.created_at ?? null,
        total_comments: matches.length,
        pending_comments: matches.filter((item) => !item.approved).length,
        distinct_posts: new Set(matches.map((item) => item.post_slug)).size,
        analysis,
        samples: matches.slice(0, 3).map((item) => ({
          id: item.id,
          created_at: item.created_at,
          post_slug: item.post_slug,
          author: item.author,
          email: item.email,
          approved: Boolean(item.approved),
          content_preview: String(item.content || '').slice(0, 80),
        })),
        created_at: iso(-1),
      }
      state.comment_persona_logs.unshift(log)
      addAuditLog('comment_persona.analyze', 'comment_persona', matcherValue, log.id)
      json(res, 200, {
        matcher_type: log.matcher_type,
        matcher_value: log.matcher_value,
        total_comments: log.total_comments,
        pending_comments: log.pending_comments,
        first_seen_at: log.from_at,
        latest_seen_at: log.to_at,
        distinct_posts: log.distinct_posts,
        analysis: log.analysis,
        samples: clone(log.samples),
      })
      return
    }

    if (pathname === '/api/admin/comments/analyze/logs' && req.method === 'GET') {
      const matcherType = normalizeText(searchParams.get('matcher_type'))
      const matcherValue = normalizeText(searchParams.get('matcher_value'))
      const limit = Number.parseInt(searchParams.get('limit') || '0', 10) || 0
      let items = [...state.comment_persona_logs]
      if (matcherType) {
        items = items.filter((item) => item.matcher_type === matcherType)
      }
      if (matcherValue) {
        items = items.filter((item) => item.matcher_value === matcherValue)
      }
      if (limit > 0) {
        items = items.slice(0, limit)
      }
      json(res, 200, items.map((item) => clone(item)))
      return
    }

    if (pathname === '/api/admin/subscriptions' && req.method === 'GET') {
      json(res, 200, { subscriptions: state.subscriptions.map((item) => clone(item)) })
      return
    }

    if (pathname === '/api/admin/subscriptions' && req.method === 'POST') {
      const { json: payload } = await parseRequest(req)
      const id = nextId('subscription')
      const record = createSubscriptionRecord(id, {
        channel_type: normalizeText(payload.channelType) || 'email',
        target: normalizeText(payload.target) || `user${id}@example.com`,
        display_name: normalizeText(payload.displayName) || null,
        status: normalizeText(payload.status) || 'active',
        filters: payload.filters ?? null,
        metadata: payload.metadata ?? null,
        secret: normalizeText(payload.secret) || null,
        notes: normalizeText(payload.notes) || null,
        created_at: iso(-1),
        updated_at: iso(-1),
      })
      state.subscriptions.unshift(record)
      addAuditLog('subscription.admin_create', 'subscription', record.target, record.id)
      json(res, 201, clone(record))
      return
    }

    if (pathname.match(/^\/api\/admin\/subscriptions\/\d+$/) && req.method === 'PATCH') {
      const id = Number(pathname.split('/').pop())
      const record = state.subscriptions.find((item) => item.id === id)
      if (!record) {
        notFound(res, '订阅目标不存在。')
        return
      }
      const { json: payload } = await parseRequest(req)
      if (Object.hasOwn(payload, 'channelType')) record.channel_type = normalizeText(payload.channelType) || record.channel_type
      if (Object.hasOwn(payload, 'target')) record.target = normalizeText(payload.target) || record.target
      if (Object.hasOwn(payload, 'displayName')) record.display_name = normalizeText(payload.displayName) || null
      if (Object.hasOwn(payload, 'status')) record.status = normalizeText(payload.status) || record.status
      if (Object.hasOwn(payload, 'filters')) record.filters = payload.filters ?? null
      if (Object.hasOwn(payload, 'metadata')) record.metadata = payload.metadata ?? null
      if (Object.hasOwn(payload, 'secret')) record.secret = normalizeText(payload.secret) || null
      if (Object.hasOwn(payload, 'notes')) record.notes = normalizeText(payload.notes) || null
      record.updated_at = iso(-1)
      addAuditLog('subscription.update', 'subscription', record.target, record.id)
      json(res, 200, clone(record))
      return
    }

    if (pathname.match(/^\/api\/admin\/subscriptions\/\d+$/) && req.method === 'DELETE') {
      const id = Number(pathname.split('/').pop())
      const index = state.subscriptions.findIndex((item) => item.id === id)
      if (index === -1) {
        notFound(res, '订阅目标不存在。')
        return
      }
      const [removed] = state.subscriptions.splice(index, 1)
      addAuditLog('subscription.delete', 'subscription', removed.target, removed.id)
      writeEmpty(res, 204)
      return
    }

    if (pathname.match(/^\/api\/admin\/subscriptions\/\d+\/test$/) && req.method === 'POST') {
      const id = Number(pathname.split('/')[4])
      const record = state.subscriptions.find((item) => item.id === id)
      if (!record) {
        notFound(res, '订阅目标不存在。')
        return
      }
      const delivery = createNotificationDelivery({
        subscription: record,
        eventType: 'subscription.test',
        status: 'queued',
      })
      state.deliveries.unshift(delivery)
      const job = enqueueNotificationDeliveryJob(delivery)
      record.last_delivery_status = delivery.status
      record.last_notified_at = iso(-1)
      addAuditLog('subscription.test', 'subscription', record.target, record.id)
      json(res, 200, { queued: true, id: record.id, delivery_id: delivery.id, job_id: job.id })
      return
    }

    if (pathname === '/api/admin/subscriptions/deliveries' && req.method === 'GET') {
      const limit = Number.parseInt(searchParams.get('limit') || '0', 10) || 0
      const items = limit > 0 ? state.deliveries.slice(0, limit) : state.deliveries
      json(res, 200, { deliveries: items.map((item) => clone(item)) })
      return
    }

    if (pathname === '/api/admin/subscriptions/digest' && req.method === 'POST') {
      const { json: payload } = await parseRequest(req)
      const period = normalizeText(payload.period) || 'weekly'
      const activeSubscriptions = state.subscriptions.filter((item) => item.status === 'active')
      activeSubscriptions.forEach((subscription) => {
        const delivery = createNotificationDelivery({
          subscription,
          eventType: `digest.${period}`,
          status: 'queued',
          payload: { period },
        })
        state.deliveries.unshift(delivery)
        enqueueNotificationDeliveryJob(delivery)
        subscription.last_delivery_status = 'queued'
        subscription.last_notified_at = iso(-1)
      })
      addAuditLog('subscription.digest', 'subscription', period, String(activeSubscriptions.length))
      json(res, 200, {
        period,
        post_count: state.posts.filter((item) => item.status === 'published').length,
        queued: activeSubscriptions.length,
        skipped: state.subscriptions.length - activeSubscriptions.length,
      })
      return
    }

    if (pathname === '/api/admin/workers/overview' && req.method === 'GET') {
      json(res, 200, buildWorkerOverview())
      return
    }

    if (pathname === '/api/admin/workers/jobs' && req.method === 'GET') {
      const status = normalizeText(searchParams.get('status'))
      const jobKind = normalizeText(searchParams.get('job_kind'))
      const workerName = normalizeText(searchParams.get('worker_name'))
      const keyword = normalizeText(searchParams.get('search'))
      const limit = Number.parseInt(searchParams.get('limit') || '0', 10) || 0
      let items = [...state.worker_jobs]
      if (status) {
        items = items.filter((item) => item.status === status)
      }
      if (jobKind) {
        items = items.filter((item) => item.job_kind === jobKind)
      }
      if (workerName) {
        items = items.filter((item) => item.worker_name === workerName)
      }
      if (keyword) {
        items = items.filter((item) =>
          [item.worker_name, item.display_name, item.related_entity_type, item.related_entity_id]
            .filter(Boolean)
            .some((value) => String(value).toLowerCase().includes(keyword.toLowerCase())),
        )
      }
      const total = items.length
      if (limit > 0) {
        items = items.slice(0, limit)
      }
      json(res, 200, { total, jobs: items.map((item) => normalizeWorkerJob(item)) })
      return
    }

    if (pathname.match(/^\/api\/admin\/workers\/jobs\/\d+$/) && req.method === 'GET') {
      const id = Number(pathname.split('/').pop())
      const job = state.worker_jobs.find((item) => item.id === id)
      if (!job) {
        notFound(res, 'worker job 不存在。')
        return
      }
      json(res, 200, normalizeWorkerJob(job))
      return
    }

    if (pathname.match(/^\/api\/admin\/workers\/jobs\/\d+\/cancel$/) && req.method === 'POST') {
      const id = Number(pathname.split('/')[5])
      const job = state.worker_jobs.find((item) => item.id === id)
      if (!job) {
        notFound(res, 'worker job 不存在。')
        return
      }
      job.cancel_requested = true
      if (job.status === 'queued') {
        job.status = 'cancelled'
        job.finished_at = iso(-1)
        job.error_text = 'job cancelled before start'
      }
      job.updated_at = iso(-1)
      addAuditLog('worker.cancel', 'worker_job', job.worker_name, job.id)
      json(res, 200, normalizeWorkerJob(job))
      return
    }

    if (pathname.match(/^\/api\/admin\/workers\/jobs\/\d+\/retry$/) && req.method === 'POST') {
      const id = Number(pathname.split('/')[5])
      const job = state.worker_jobs.find((item) => item.id === id)
      if (!job) {
        notFound(res, 'worker job 不存在。')
        return
      }

      let nextJob = null
      if (job.worker_name === 'task.send_weekly_digest' || job.worker_name === 'task.send_monthly_digest') {
        const period = job.worker_name === 'task.send_monthly_digest' ? 'monthly' : 'weekly'
        nextJob = createWorkerJob({
          job_kind: 'task',
          worker_name: job.worker_name,
          display_name: period === 'monthly' ? '发送月报' : '发送周报',
          status: 'succeeded',
          queue_name: 'digests',
          payload: { period },
          result: {
            period,
            post_count: state.posts.filter((item) => item.status === 'published').length,
            queued: state.subscriptions.filter((item) => item.status === 'active').length,
            skipped: state.subscriptions.filter((item) => item.status !== 'active').length,
          },
          tags: ['digest', period],
          related_entity_type: 'subscription_digest',
          related_entity_id: period,
          parent_job_id: job.id,
          trigger_mode: 'retry',
        })
      } else if (job.worker_name === 'worker.download_media') {
        const payload = job.payload || {}
        nextJob = createWorkerJob({
          job_kind: 'worker',
          worker_name: 'worker.download_media',
          display_name: job.display_name,
          status: 'succeeded',
          queue_name: 'media',
          payload,
          result: job.result,
          tags: ['media', 'download'],
          related_entity_type: job.related_entity_type,
          related_entity_id: job.related_entity_id,
          parent_job_id: job.id,
          trigger_mode: 'retry',
        })
      } else {
        nextJob = createWorkerJob({
          job_kind: job.job_kind,
          worker_name: job.worker_name,
          display_name: job.display_name,
          status: 'succeeded',
          queue_name: job.queue_name,
          payload: clone(job.payload),
          result: clone(job.result),
          tags: Array.isArray(job.tags) ? job.tags : [],
          related_entity_type: job.related_entity_type,
          related_entity_id: job.related_entity_id,
          parent_job_id: job.id,
          trigger_mode: 'retry',
        })
      }

      addAuditLog('worker.retry', 'worker_job', nextJob.worker_name, nextJob.id, { source_job_id: job.id })
      json(res, 200, { queued: true, job: normalizeWorkerJob(nextJob) })
      return
    }

    if (pathname === '/api/admin/workers/tasks/retry-deliveries' && req.method === 'POST') {
      const { json: payload } = await parseRequest(req)
      const limit = Number.parseInt(String(payload.limit || '80'), 10) || 80
      const retryable = state.deliveries
        .filter((item) => item.status === 'retry_pending')
        .slice(0, limit)
      retryable.forEach((delivery) => {
        delivery.status = 'queued'
        delivery.updated_at = iso(-1)
        delivery.next_retry_at = null
        enqueueNotificationDeliveryJob(delivery)
      })
      const job = createWorkerJob({
        job_kind: 'task',
        worker_name: 'task.retry_deliveries',
        display_name: '重试待投递通知',
        status: 'succeeded',
        queue_name: 'maintenance',
        payload: { limit },
        result: { limit, queued: retryable.length },
        tags: ['maintenance', 'retry'],
        related_entity_type: 'notification_delivery',
        related_entity_id: null,
      })
      addAuditLog('worker.task.retry_deliveries', 'worker_job', job.worker_name, job.id, { limit })
      json(res, 200, { queued: true, job: normalizeWorkerJob(job) })
      return
    }

    if (pathname === '/api/admin/workers/tasks/digest' && req.method === 'POST') {
      const { json: payload } = await parseRequest(req)
      const period = normalizeText(payload.period) === 'monthly' ? 'monthly' : 'weekly'
      const activeSubscriptions = state.subscriptions.filter((item) => item.status === 'active')
      activeSubscriptions.forEach((subscription) => {
        const delivery = createNotificationDelivery({
          subscription,
          eventType: `digest.${period}`,
          status: 'queued',
          payload: { period },
        })
        state.deliveries.unshift(delivery)
        enqueueNotificationDeliveryJob(delivery)
      })
      const job = createWorkerJob({
        job_kind: 'task',
        worker_name: period === 'monthly' ? 'task.send_monthly_digest' : 'task.send_weekly_digest',
        display_name: period === 'monthly' ? '发送月报' : '发送周报',
        status: 'succeeded',
        queue_name: 'digests',
        payload: { period },
        result: {
          period,
          post_count: state.posts.filter((item) => item.status === 'published').length,
          queued: activeSubscriptions.length,
          skipped: state.subscriptions.length - activeSubscriptions.length,
        },
        tags: ['digest', period],
        related_entity_type: 'subscription_digest',
        related_entity_id: period,
      })
      addAuditLog('worker.task.digest', 'worker_job', job.worker_name, job.id, { period })
      json(res, 200, { queued: true, job: normalizeWorkerJob(job) })
      return
    }

    if (pathname === '/api/admin/storage/media' && req.method === 'GET') {
      const prefix = normalizeText(searchParams.get('prefix'))
      const limit = Number.parseInt(searchParams.get('limit') || '0', 10) || 0
      const items = state.media
        .filter((item) => !prefix || item.key.startsWith(prefix))
        .slice(0, limit > 0 ? limit : undefined)
      json(res, 200, {
        provider: state.site_settings.media_storage_provider,
        bucket: state.site_settings.media_r2_bucket,
        public_base_url: state.site_settings.media_r2_public_base_url,
        items: items.map((item) => ({
          key: item.key,
          url: item.url,
          size_bytes: item.size_bytes,
          last_modified: item.last_modified,
          title: item.title,
          alt_text: item.alt_text,
          caption: item.caption,
          tags: [...item.tags],
          notes: item.notes,
        })),
      })
      return
    }

    if (pathname === '/api/admin/storage/media' && req.method === 'POST') {
      const { fields, files } = await parseRequest(req)
      const prefix = normalizeText(fields.prefix) || 'uploads/'
      const uploaded = files.map((file, index) => {
        const key = `${prefix}${Date.now()}-${index}-${sanitizeFilename(file.filename)}`
        const record = makeMediaRecordFromUpload(key, file)
        state.media.unshift(record)
        return {
          key: record.key,
          url: record.url,
          size_bytes: record.size_bytes,
        }
      })
      addAuditLog('media.upload', 'media', prefix, String(uploaded.length))
      json(res, 200, { uploaded })
      return
    }

    if (pathname === '/api/admin/storage/media' && req.method === 'DELETE') {
      const key = decodeURIComponent(searchParams.get('key') || '')
      const index = state.media.findIndex((item) => item.key === key)
      if (index === -1) {
        notFound(res, '媒体对象不存在。')
        return
      }
      state.media.splice(index, 1)
      addAuditLog('media.delete', 'media', key, key)
      json(res, 200, { deleted: true, key })
      return
    }

    if (pathname === '/api/admin/storage/media/batch-delete' && req.method === 'POST') {
      const { json: payload } = await parseRequest(req)
      const deleted = []
      const failed = []
      for (const key of Array.isArray(payload.keys) ? payload.keys : []) {
        const index = state.media.findIndex((item) => item.key === key)
        if (index === -1) {
          failed.push(key)
          continue
        }
        state.media.splice(index, 1)
        deleted.push(key)
      }
      addAuditLog('media.batch_delete', 'media', `${deleted.length} deleted`, String(deleted.length), { deleted, failed })
      json(res, 200, { deleted, failed })
      return
    }

    if (pathname === '/api/admin/storage/media/replace' && req.method === 'POST') {
      const { fields, files } = await parseRequest(req)
      const key = normalizeText(fields.key)
      const record = state.media.find((item) => item.key === key)
      if (!record) {
        notFound(res, '媒体对象不存在。')
        return
      }
      const file = files[0]
      if (file) {
        record.body = file.body?.toString('utf8') || record.body
        record.size_bytes = file.size ?? record.size_bytes
        record.content_type = String(file.contentType || record.content_type)
      }
      record.last_modified = iso(-1)
      addAuditLog('media.replace', 'media', key, key)
      json(res, 200, { key: record.key, url: `${record.url}?v=${Date.now()}` })
      return
    }

    if (pathname === '/api/admin/storage/media/download' && req.method === 'POST') {
      const { json: payload } = await parseRequest(req)
      const sourceUrl = normalizeText(payload.source_url)
      if (!sourceUrl) {
        badRequest(res, '缺少远程素材地址。')
        return
      }

      const prefix = normalizeText(payload.prefix) || 'uploads/'
      const fileName = sanitizeFilename(sourceUrl.split('/').pop(), 'remote-asset.svg')
      const key = `${prefix}${Date.now()}-${fileName}`
      const title = normalizeText(payload.title) || sanitizeFilename(fileName, 'remote-asset')
      const record = {
        key,
        url: `${MOCK_ORIGIN}/media/${encodeURIComponent(key)}`,
        size_bytes: 2048,
        last_modified: iso(-1),
        title,
        alt_text: normalizeText(payload.alt_text) || null,
        caption: normalizeText(payload.caption) || null,
        tags: Array.isArray(payload.tags) ? payload.tags.filter(Boolean) : [],
        notes: normalizeText(payload.notes) || `downloaded from ${sourceUrl}`,
        body: `<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="675" viewBox="0 0 1200 675"><rect width="1200" height="675" fill="#111827"/><text x="72" y="200" fill="#f8fafc" font-family="monospace" font-size="40">${title}</text><text x="72" y="280" fill="#94a3b8" font-family="monospace" font-size="24">${sourceUrl}</text></svg>`,
        content_type: CONTENT_TYPES.svg,
      }
      state.media.unshift(record)
      const job = queueDownloadWorkerJob(payload, record)
      addAuditLog('media.download', 'media', sourceUrl, job.id, { key })
      json(res, 200, {
        queued: true,
        job_id: job.id,
        status: job.status,
      })
      return
    }

    if (pathname === '/api/admin/storage/media/metadata' && req.method === 'PATCH') {
      const { json: payload } = await parseRequest(req)
      const key = normalizeText(payload.key)
      const record = state.media.find((item) => item.key === key)
      if (!record) {
        notFound(res, '媒体对象不存在。')
        return
      }
      record.title = normalizeText(payload.title) || null
      record.alt_text = normalizeText(payload.alt_text) || null
      record.caption = normalizeText(payload.caption) || null
      record.tags = Array.isArray(payload.tags) ? payload.tags.filter(Boolean) : []
      record.notes = normalizeText(payload.notes) || null
      record.last_modified = iso(-1)
      addAuditLog('media.metadata.update', 'media', key, key)
      json(res, 200, {
        saved: true,
        key: record.key,
        title: record.title,
        alt_text: record.alt_text,
        caption: record.caption,
        tags: [...record.tags],
        notes: record.notes,
      })
      return
    }

    if (pathname === '/api/admin/storage/review-cover' && req.method === 'POST') {
      const { files } = await parseRequest(req)
      const file = files[0]
      if (!file) {
        badRequest(res, '缺少封面文件。')
        return
      }
      const key = `review-covers/${Date.now()}-${sanitizeFilename(file.filename, 'review-cover.svg')}`
      const record = makeMediaRecordFromUpload(key, file)
      state.media.unshift(record)
      addAuditLog('review.cover.upload', 'media', key, key)
      json(res, 200, { key: record.key, url: record.url })
      return
    }

    if (pathname === '/api/admin/ai/reindex' && req.method === 'POST') {
      state.site_settings.ai_last_indexed_at = iso(-1)
      state.site_settings.ai_chunks_count += 3
      json(res, 200, {
        indexed_chunks: state.site_settings.ai_chunks_count,
        last_indexed_at: state.site_settings.ai_last_indexed_at,
      })
      return
    }

    if (pathname === '/api/admin/ai/post-metadata' && req.method === 'POST') {
      const { json: payload } = await parseRequest(req)
      const markdown = String(payload.markdown || '')
      const { title, description } = parseHeadingAndDescription(markdown, 'Playwright Mock Draft')
      json(res, 200, {
        title,
        description,
        category: '测试体系',
        tags: ['Playwright', 'CI'],
        slug: slugify(title),
      })
      return
    }

    if (pathname === '/api/admin/ai/polish-post' && req.method === 'POST') {
      const { json: payload } = await parseRequest(req)
      const markdown = String(payload.markdown || '').trim()
      const polishedMarkdown = markdown.includes('【AI 润色】')
        ? markdown
        : `${markdown}\n\n【AI 润色】这是一段由 mock server 追加的润色说明。`
      json(res, 200, { polished_markdown: polishedMarkdown })
      return
    }

    if (pathname === '/api/admin/ai/polish-review' && req.method === 'POST') {
      const { json: payload } = await parseRequest(req)
      const description = normalizeText(payload.description) || '暂无点评。'
      json(res, 200, {
        polished_description: `${description}\n\n（AI 润色）整体表达更凝练，也更适合作为首页卡片摘要。`,
      })
      return
    }

    if (pathname === '/api/admin/ai/post-cover' && req.method === 'POST') {
      const { json: payload } = await parseRequest(req)
      const slug = normalizeText(payload.slug) || slugify(payload.title || 'mock-cover')
      json(res, 200, {
        image_url: `${MOCK_ORIGIN}/generated/${slug}-cover.svg`,
        prompt: `Generate a mock cover for ${normalizeText(payload.title) || slug}`,
      })
      return
    }

    if (pathname === '/api/admin/ai/test-provider' && req.method === 'POST') {
      json(res, 200, {
        provider: 'openai',
        endpoint: 'https://api.mock.invalid/v1',
        chat_model: 'gpt-mock-4.1',
        reply_preview: 'Mock provider connection looks good.',
      })
      return
    }

    if (pathname === '/api/admin/ai/test-image-provider' && req.method === 'POST') {
      json(res, 200, {
        provider: 'mock-images',
        endpoint: 'https://images.mock.invalid/v1',
        image_model: 'mock-image-1',
        result_preview: `${MOCK_ORIGIN}/generated/image-provider-preview.svg`,
      })
      return
    }

    if (pathname === '/api/admin/storage/r2/test' && req.method === 'POST') {
      json(res, 200, {
        bucket: state.site_settings.media_r2_bucket,
        public_base_url: state.site_settings.media_r2_public_base_url,
      })
      return
    }
  }

  if (pathname.match(/^\/api\/posts\/slug\/[^/]+\/markdown$/) && req.method === 'GET') {
    const slug = decodeURIComponent(pathname.split('/')[4])
    const post = state.posts.find((item) => item.slug === slug)
    if (!post) {
      notFound(res, '文章不存在。')
      return
    }
    json(res, 200, { slug: post.slug, path: `content/posts/${post.slug}.md`, markdown: post.content })
    return
  }

  if (pathname.match(/^\/api\/posts\/slug\/[^/]+\/markdown$/) && req.method === 'PATCH') {
    const slug = decodeURIComponent(pathname.split('/')[4])
    const post = state.posts.find((item) => item.slug === slug)
    if (!post) {
      notFound(res, '文章不存在。')
      return
    }
    const { json: payload } = await parseRequest(req)
    post.content = String(payload.markdown || post.content || '')
    const parsed = parseHeadingAndDescription(post.content, post.title || post.slug)
    post.title = post.title || parsed.title
    if (!normalizeText(post.description)) {
      post.description = parsed.description
    }
    post.updated_at = iso(-1)
    addAuditLog('post.markdown.update', 'post', post.title, post.id, { slug: post.slug })
    addPostRevision(post, 'markdown', '保存 Markdown')
    json(res, 200, { slug: post.slug, path: `content/posts/${post.slug}.md`, markdown: post.content })
    return
  }

  if (pathname.match(/^\/api\/posts\/slug\/[^/]+\/markdown$/) && req.method === 'DELETE') {
    const slug = decodeURIComponent(pathname.split('/')[4])
    const index = state.posts.findIndex((item) => item.slug === slug)
    if (index === -1) {
      notFound(res, '文章不存在。')
      return
    }
    const [removed] = state.posts.splice(index, 1)
    recalculateTaxonomyCounts()
    addAuditLog('post.delete', 'post', removed.title, removed.id, { slug: removed.slug })
    addPostRevision(removed, 'delete', '删除文章')
    json(res, 200, { slug: removed.slug, deleted: true })
    return
  }

  if (pathname.match(/^\/api\/comments\/\d+$/) && req.method === 'PATCH') {
    if (!ensureAdmin(req, res)) return
    const id = Number(pathname.split('/').pop())
    const record = state.comments.find((item) => item.id === id)
    if (!record) {
      notFound(res, '评论不存在。')
      return
    }
    const { json: payload } = await parseRequest(req)
    if (Object.hasOwn(payload, 'approved')) record.approved = Boolean(payload.approved)
    record.updated_at = iso(-1)
    addAuditLog('comment.update', 'comment', record.author, record.id, { approved: record.approved })
    json(res, 200, clone(record))
    return
  }

  if (pathname.match(/^\/api\/comments\/\d+$/) && req.method === 'DELETE') {
    if (!ensureAdmin(req, res)) return
    const id = Number(pathname.split('/').pop())
    const index = state.comments.findIndex((item) => item.id === id)
    if (index === -1) {
      notFound(res, '评论不存在。')
      return
    }
    const [record] = state.comments.splice(index, 1)
    addAuditLog('comment.delete', 'comment', record.author, record.id)
    writeEmpty(res, 204)
    return
  }

  if (pathname.match(/^\/api\/friend_links\/\d+$/) && req.method === 'PATCH') {
    if (!ensureAdmin(req, res)) return
    const id = Number(pathname.split('/').pop())
    const record = state.friend_links.find((item) => item.id === id)
    if (!record) {
      notFound(res, '友链不存在。')
      return
    }
    const { json: payload } = await parseRequest(req)
    record.site_name = normalizeText(payload.site_name || payload.siteName) || record.site_name
    record.site_url = normalizeText(payload.site_url || payload.siteUrl) || record.site_url
    record.description = normalizeText(payload.description) || record.description
    record.category = normalizeText(payload.category) || record.category
    record.status = normalizeText(payload.status) || record.status
    record.updated_at = iso(-1)
    addAuditLog('friend_link.update', 'friend_link', record.site_name, record.id, { status: record.status })
    json(res, 200, clone(record))
    return
  }

  if (pathname.match(/^\/api\/friend_links\/\d+$/) && req.method === 'DELETE') {
    if (!ensureAdmin(req, res)) return
    const id = Number(pathname.split('/').pop())
    const index = state.friend_links.findIndex((item) => item.id === id)
    if (index === -1) {
      notFound(res, '友链不存在。')
      return
    }
    const [record] = state.friend_links.splice(index, 1)
    addAuditLog('friend_link.delete', 'friend_link', record.site_name, record.id)
    writeEmpty(res, 204)
    return
  }

  notFound(res, `未匹配到 mock 接口：${req.method} ${pathname}`)
})

server.listen(PORT, '127.0.0.1')
