import { Hono } from 'hono'
import { cors } from 'hono/cors'
import { compress } from 'hono/compress'
import { authIdentity } from './middleware/auth.js'
import { auditMiddleware } from './middleware/audit.js'
import categoriesRoute from './routes/categories.js'
import skillsRoute from './routes/skills.js'
import uploadsRoute from './routes/uploads.js'
import auditRoute from './routes/audit.js'
import usersRoute from './routes/users.js'
import apikeysRoute from './routes/apikeys.js'
import providersRoute from './routes/providers.js'
import runtimesRoute from './routes/runtimes.js'
import evalRoute from './routes/eval.js'
import skillRunRoute from './routes/skill-run.js'
import systemSkillsRoute from './routes/system-skills.js'
import invokeRoute from './routes/invoke.js'
import opsRoute from './routes/ops.js'
import { apiGateway } from './middleware/api-gateway.js'
import { createReadStream, existsSync, statSync } from 'fs'
import { Readable } from 'stream'
import { fileURLToPath } from 'url'
import path from 'path'

const app = new Hono()

// 去除尾部斜杠：直接重写路径，不做 301 重定向（避免 nginx SSL 卸载后协议降级问题）
app.use('*', async (c, next) => {
  const path = c.req.path
  if (path !== '/' && path.endsWith('/')) {
    const proto = c.req.header('x-forwarded-proto') || 'https'
    const host = c.req.header('host') || 'zhenyuskill.com'
    const query = new URL(c.req.url).search || ''
    return c.redirect(`${proto}://${host}${path.slice(0, -1)}${query}`, 301)
  }
  await next()
})

// Gzip 压缩（减少传输体积，尤其对 categories 315 节点有明显效果）
app.use('*', compress())

// CORS：允许前端域名（环境变量配）
app.use('*', async (c, next) => {
  const origins = (process.env.ALLOW_ORIGINS || '*').split(',').map(s => s.trim()).filter(Boolean)
  const handler = cors({
    origin: (origin) => origins.includes(origin) ? origin : origins[0] || '*',
    allowMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
    allowHeaders: ['Content-Type', 'Authorization', 'X-User-Phone', 'X-User-Email', 'X-User-Name'],
    maxAge: 600,
    credentials: false,
  })
  return handler(c, next)
})

// 身份注入（从请求头提取 phone/email/name 到 c.get('identity')）
app.use('*', authIdentity)

// 操作日志写入（写库前先放到 c.set('audit')，写完后 ctx.executionCtx.waitUntil 落库）
app.use('*', auditMiddleware)

// 健康检查
app.get('/api/health', (c) => c.json({ ok: true, ts: Date.now() }))

// 路由组
app.route('/api/categories', categoriesRoute)
app.route('/api/skills', skillsRoute)
app.route('/api/uploads', uploadsRoute)
app.route('/api/audit', auditRoute)
app.route('/api/users', usersRoute)
app.route('/api/apikeys', apikeysRoute)
app.route('/api/providers', providersRoute)
app.route('/api/runtimes', runtimesRoute)
app.route('/api/eval', evalRoute)
app.route('/api/skill-run', skillRunRoute)
app.route('/api/system-skills', systemSkillsRoute)
app.route('/api/ops', opsRoute)

// 对外 OpenAPI（Bearer Token 鉴权 + 限流）
app.use('/v1/skills/*', apiGateway)
app.route('/v1/skills', invokeRoute)

// 管理员源码下载
const __idx_dirname = path.dirname(fileURLToPath(import.meta.url))
app.get('/api/download/source', async (c) => {
  const identity = c.get('identity') || {}
  if (identity.email !== '494161546@qq.com' && identity.phone !== '13136092523') {
    return c.json({ error: '无权限，仅超级管理员可下载源码' }, 403)
  }
  const zipPath = path.resolve(__idx_dirname, '../../source-code.zip')
  if (!existsSync(zipPath)) {
    return c.json({ error: '源码包不存在' }, 404)
  }
  const stat = statSync(zipPath)
  const nodeStream = createReadStream(zipPath)
  const webStream = Readable.toWeb(nodeStream)
  return new Response(webStream, {
    status: 200,
    headers: {
      'Content-Type': 'application/zip',
      'Content-Disposition': "attachment; filename*=UTF-8''aipros-source.zip",
      'Content-Length': String(stat.size),
      'Cache-Control': 'no-store',
    }
  })
})

// 兜底
app.notFound((c) => c.json({ error: 'not_found', path: c.req.path }, 404))
app.onError((err, c) => {
  console.error('[unhandled]', err)
  return c.json({ error: 'internal_error', message: '服务器内部错误' }, 500)
})

export default app
