// admin-routes.mjs
//
// Back-office HTTP route factories for the standalone memind_adm service.
//
// Route logic lives here exactly once. The public server (server.mjs) no longer
// serves these surfaces; admin-server.mjs is the sole consumer. Services are
// injected so the same router can be wired against any container (tests, future
// read-replica pool, etc.).
//
//   createAdminApi  -> /admin-api/*      platform super-admin (role === 'admin')
//   createOpsApi    -> /api/ops/v1/*     plaza operations console (ops_role)
import express from 'express';
import { sendData, sendError } from './api-response.mjs';
import { PLAN_CATALOG, tokensToCallsApprox } from './billing-subscription.mjs';
import { hasOpsRole } from './plaza-ops.mjs';
import { mapPlazaError } from './plaza-posts.mjs';

function plazaRouteError(res, req, error) {
  const status = mapPlazaError(error);
  const code = error?.code ?? 'internal_error';
  const message = error instanceof Error ? error.message : 'Plaza 请求失败';
  return sendError(res, req, status, code, message, error?.details);
}

/**
 * Platform super-admin API (role === 'admin').
 * @param {object} deps
 * @param {import('express').RequestHandler} deps.jsonBody  JSON body parser middleware.
 * @param {(req: import('express').Request) => (string|undefined)} deps.getToken  Extract the user session token from a request.
 * @param {Promise<unknown>} [deps.ready]  Optional bootstrap gate awaited before the first request resolves auth.
 * @param {object} deps.userAuth
 * @param {object|null} deps.llmProviderService
 * @param {object|null} deps.plazaPosts
 * @param {object|null} deps.plazaOps
 * @param {object|null} deps.wechatAdmin
 */
export function createAdminApi({
  jsonBody,
  getToken,
  ready,
  userAuth,
  llmProviderService,
  plazaPosts,
  plazaOps,
  wechatAdmin,
  subscriptionService,
}) {
  function requireAdmin(req, res, next) {
    if (!req.currentUser || req.currentUser.role !== 'admin') {
      res.status(403).json({ message: '需要管理员权限' });
      return;
    }
    next();
  }

  const adminApi = express.Router();
  adminApi.use(jsonBody);

  adminApi.use(async (req, res, next) => {
    if (ready) await ready;
    if (!userAuth) return res.status(503).json({ message: '未启用用户系统' });
    const me = await userAuth.getMe(getToken(req));
    if (!me) return res.status(401).json({ message: '未登录' });
    req.currentUser = me;
    next();
  });

  adminApi.get('/users', requireAdmin, async (req, res) => {
    const { page, pageSize, search, role, status } = req.query;
    const result = await userAuth.listUsers({ page, pageSize, search, role, status });
    res.json(result);
  });

  adminApi.get('/summary', requireAdmin, async (_req, res) => {
    const summary = await userAuth.getAdminSummary();
    let llm = null;
    if (llmProviderService) {
      const keys = await llmProviderService.listKeys();
      const global = await llmProviderService.getGlobalSettings();
      const selected = keys.find((key) => key.isSelected);
      llm = {
        keyCount: keys.length,
        selectedKeyName: selected?.name ?? null,
        globalModel: global?.model ?? null,
      };
    }
    res.json({ summary: { ...summary, llm } });
  });

  adminApi.post('/users', requireAdmin, async (req, res) => {
    const result = await userAuth.createUser(req.body ?? {});
    if (!result.ok) return res.status(400).json({ message: result.message });
    res.status(201).json({ user: result.user });
  });

  adminApi.patch('/users/:userId', requireAdmin, async (req, res) => {
    const result = await userAuth.updateUser(req.params.userId, req.body ?? {});
    if (!result.ok) return res.status(400).json({ message: result.message });
    res.json({ user: result.user });
  });

  adminApi.post('/users/:userId/recharge', requireAdmin, async (req, res) => {
    const amountCents = Number(req.body?.amountCents);
    const note = typeof req.body?.note === 'string' ? req.body.note : '';
    const result = await userAuth.recharge(
      req.params.userId,
      amountCents,
      req.currentUser.id,
      note,
    );
    if (!result.ok) return res.status(400).json({ message: result.message });
    res.json({ user: result.user });
  });

  adminApi.get('/usage', requireAdmin, async (req, res) => {
    const userId = typeof req.query.userId === 'string' ? req.query.userId : null;
    const { page, pageSize } = req.query;
    const result = await userAuth.listUsageRecords({ userId, page, pageSize });
    res.json(result);
  });

  adminApi.get('/capabilities/catalog', requireAdmin, (_req, res) => {
    res.json({ catalog: userAuth.capabilityCatalog });
  });

  adminApi.get('/capabilities/role/:role', requireAdmin, async (req, res) => {
    const role = req.params.role === 'admin' ? 'admin' : 'user';
    if (role === 'admin') {
      return res.json({
        role,
        capabilities: Object.fromEntries(
          userAuth.capabilityCatalog.map((item) => [item.key, true]),
        ),
        unrestricted: true,
      });
    }
    res.json(await userAuth.getRoleCapabilities('user'));
  });

  adminApi.put('/capabilities/role/:role', requireAdmin, async (req, res) => {
    const role = req.params.role === 'admin' ? 'admin' : 'user';
    const result = await userAuth.setRoleCapabilities(role, req.body?.capabilities ?? {});
    if (!result.ok) return res.status(400).json({ message: result.message });
    res.json(result);
  });

  adminApi.get('/users/:userId/capabilities', requireAdmin, async (req, res) => {
    const result = await userAuth.getUserCapabilities(req.params.userId);
    if (!result.ok) return res.status(404).json({ message: result.message });
    res.json(result);
  });

  adminApi.put('/users/:userId/capabilities', requireAdmin, async (req, res) => {
    const result = await userAuth.setUserCapabilities(
      req.params.userId,
      req.body?.capabilities ?? {},
    );
    if (!result.ok) return res.status(400).json({ message: result.message });
    res.json(result);
  });

  adminApi.delete('/users/:userId/capabilities', requireAdmin, async (req, res) => {
    const result = await userAuth.clearUserCapabilityOverrides(req.params.userId);
    if (!result.ok) return res.status(404).json({ message: result.message });
    res.json(result);
  });

  adminApi.get('/policies/catalog', requireAdmin, (_req, res) => {
    res.json({ catalog: userAuth.policyCatalog });
  });

  adminApi.get('/policies/role/:role', requireAdmin, async (req, res) => {
    const role = req.params.role === 'admin' ? 'admin' : 'user';
    if (role === 'admin') {
      return res.json({ role, policies: {}, unrestricted: true });
    }
    res.json(await userAuth.getRolePolicies('user'));
  });

  adminApi.put('/policies/role/:role', requireAdmin, async (req, res) => {
    const role = req.params.role === 'admin' ? 'admin' : 'user';
    const result = await userAuth.setRolePolicies(role, req.body?.policies ?? {});
    if (!result.ok) return res.status(400).json({ message: result.message });
    res.json(result);
  });

  adminApi.get('/users/:userId/policies', requireAdmin, async (req, res) => {
    const result = await userAuth.getUserPolicies(req.params.userId);
    if (!result.ok) return res.status(404).json({ message: result.message });
    res.json(result);
  });

  adminApi.put('/users/:userId/policies', requireAdmin, async (req, res) => {
    const result = await userAuth.setUserPolicies(req.params.userId, req.body?.policies ?? {});
    if (!result.ok) return res.status(400).json({ message: result.message });
    res.json(result);
  });

  adminApi.delete('/users/:userId/policies', requireAdmin, async (req, res) => {
    const result = await userAuth.clearUserPolicyOverrides(req.params.userId);
    if (!result.ok) return res.status(404).json({ message: result.message });
    res.json(result);
  });

  adminApi.get('/skills/catalog', requireAdmin, (_req, res) => {
    res.json({ catalog: userAuth.skillCatalog });
  });

  adminApi.get('/skills/role/:role', requireAdmin, async (req, res) => {
    const role = req.params.role === 'admin' ? 'admin' : 'user';
    if (role === 'admin') {
      return res.json({
        role,
        skills: Object.fromEntries(userAuth.skillCatalog.map((item) => [item.name, true])),
      });
    }
    res.json(await userAuth.getRoleSkills('user'));
  });

  adminApi.put('/skills/role/:role', requireAdmin, async (req, res) => {
    const role = req.params.role === 'admin' ? 'admin' : 'user';
    const result = await userAuth.setRoleSkills(role, req.body?.skills ?? {});
    if (!result.ok) return res.status(400).json({ message: result.message });
    res.json(result);
  });

  adminApi.get('/users/:userId/skills', requireAdmin, async (req, res) => {
    const result = await userAuth.getUserSkills(req.params.userId);
    if (!result.ok) return res.status(404).json({ message: result.message });
    res.json(result);
  });

  adminApi.put('/users/:userId/skills', requireAdmin, async (req, res) => {
    const result = await userAuth.setUserSkills(req.params.userId, req.body?.skills ?? {});
    if (!result.ok) return res.status(400).json({ message: result.message });
    res.json(result);
  });

  adminApi.delete('/users/:userId/skills', requireAdmin, async (req, res) => {
    const result = await userAuth.clearUserSkillOverrides(req.params.userId);
    if (!result.ok) return res.status(404).json({ message: result.message });
    res.json(result);
  });

  adminApi.get('/ledger', requireAdmin, async (req, res) => {
    const userId = typeof req.query.userId === 'string' ? req.query.userId : null;
    const { page, pageSize } = req.query;
    const result = await userAuth.listBillingLedger({ userId, page, pageSize });
    res.json(result);
  });

  adminApi.get('/wechat/summary', requireAdmin, async (_req, res) => {
    if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
    res.json(await wechatAdmin.getSummary());
  });

  adminApi.get('/wechat/bindings', requireAdmin, async (req, res) => {
    if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
    res.json(await wechatAdmin.listBindings(req.query));
  });

  adminApi.get('/wechat/messages', requireAdmin, async (req, res) => {
    if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
    res.json(await wechatAdmin.listMessages(req.query));
  });

  adminApi.get('/wechat/digests', requireAdmin, async (req, res) => {
    if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
    res.json(await wechatAdmin.listDigests(req.query));
  });

  adminApi.get('/wechat/deliveries', requireAdmin, async (req, res) => {
    if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
    res.json(await wechatAdmin.listDeliveries(req.query));
  });

  adminApi.get('/wechat/balance-alerts', requireAdmin, async (req, res) => {
    if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
    res.json(await wechatAdmin.listBalanceAlerts(req.query));
  });

  adminApi.post('/wechat/balance-alerts', requireAdmin, async (req, res) => {
    if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
    const result = await wechatAdmin.createBalanceAlert(req.body ?? {});
    if (!result.ok) return res.status(400).json({ message: result.message });
    res.status(201).json(result);
  });

  adminApi.post('/wechat/balance-alerts/:id/cancel', requireAdmin, async (req, res) => {
    if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
    const result = await wechatAdmin.cancelBalanceAlert(req.params.id);
    if (!result.ok) return res.status(404).json({ message: result.message ?? '取消失败' });
    res.json(result);
  });

  adminApi.post('/wechat/balance-alerts/:id/resume', requireAdmin, async (req, res) => {
    if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
    const result = await wechatAdmin.resumeBalanceAlert(req.params.id);
    if (!result.ok) return res.status(404).json({ message: result.message ?? '恢复失败' });
    res.json(result);
  });

  adminApi.get('/wechat/web-notifications', requireAdmin, async (req, res) => {
    if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
    res.json(await wechatAdmin.listWebNotifications(req.query));
  });

  adminApi.post('/wechat/web-notifications', requireAdmin, async (req, res) => {
    if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
    const result = await wechatAdmin.createWebNotification(req.body ?? {});
    if (!result.ok) return res.status(400).json({ message: result.message });
    res.status(201).json(result);
  });

  adminApi.post('/wechat/users/:userId/route/clear', requireAdmin, async (req, res) => {
    if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
    const result = await wechatAdmin.clearRouteForUser(req.params.userId);
    if (!result.ok) return res.status(404).json({ message: result.message ?? '清除失败' });
    res.json(result);
  });

  adminApi.post('/wechat/digests/:id/cancel', requireAdmin, async (req, res) => {
    if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
    const result = await wechatAdmin.cancelDigest(req.params.id);
    if (!result.ok) return res.status(404).json({ message: '订阅不存在或已暂停' });
    res.json(result);
  });

  adminApi.post('/wechat/digests/:id/resume', requireAdmin, async (req, res) => {
    if (!wechatAdmin) return res.status(503).json({ message: '服务号管理未启用' });
    const result = await wechatAdmin.resumeDigest(req.params.id);
    if (!result.ok) return res.status(404).json({ message: result.message ?? '恢复失败' });
    res.json(result);
  });

  adminApi.get('/llm-providers/catalog', requireAdmin, (_req, res) => {
    if (!llmProviderService) return res.status(503).json({ message: '未启用 LLM 配置' });
    res.json({ catalog: llmProviderService.catalog });
  });

  adminApi.get('/llm-providers/keys', requireAdmin, async (_req, res) => {
    if (!llmProviderService) return res.status(503).json({ message: '未启用 LLM 配置' });
    const keys = await llmProviderService.listKeys();
    res.json({ keys });
  });

  adminApi.post('/llm-providers/keys', requireAdmin, async (req, res) => {
    if (!llmProviderService) return res.status(503).json({ message: '未启用 LLM 配置' });
    const result = await llmProviderService.createKey(req.body ?? {});
    if (!result.ok) return res.status(400).json({ message: result.message });
    res.status(201).json({ key: result.key });
  });

  adminApi.patch('/llm-providers/keys/:keyId', requireAdmin, async (req, res) => {
    if (!llmProviderService) return res.status(503).json({ message: '未启用 LLM 配置' });
    const result = await llmProviderService.updateKey(req.params.keyId, req.body ?? {});
    if (!result.ok) return res.status(400).json({ message: result.message });
    res.json({ key: result.key });
  });

  adminApi.post('/llm-providers/keys/:keyId/select', requireAdmin, async (req, res) => {
    if (!llmProviderService) return res.status(503).json({ message: '未启用 LLM 配置' });
    const result = await llmProviderService.selectKey(req.params.keyId);
    if (!result.ok) return res.status(400).json({ message: result.message });
    res.json({ key: result.key });
  });

  adminApi.delete('/llm-providers/keys/:keyId', requireAdmin, async (req, res) => {
    if (!llmProviderService) return res.status(503).json({ message: '未启用 LLM 配置' });
    const result = await llmProviderService.deleteKey(req.params.keyId);
    if (!result.ok) return res.status(400).json({ message: result.message });
    res.json({ ok: true });
  });

  adminApi.post('/llm-providers/sync', requireAdmin, async (_req, res) => {
    if (!llmProviderService) return res.status(503).json({ message: '未启用 LLM 配置' });
    try {
      const result = await llmProviderService.syncSelectedToGoosed();
      res.json(result);
    } catch (err) {
      res.status(500).json({
        message: err instanceof Error ? err.message : '同步失败',
      });
    }
  });

  adminApi.get('/llm-providers/global', requireAdmin, async (_req, res) => {
    if (!llmProviderService) return res.status(503).json({ message: '未启用 LLM 配置' });
    const global = await llmProviderService.getGlobalSettings();
    res.json({ global });
  });

  adminApi.put('/llm-providers/global', requireAdmin, async (req, res) => {
    if (!llmProviderService) return res.status(503).json({ message: '未启用 LLM 配置' });
    const result = await llmProviderService.setGlobalModel(req.body?.model);
    if (!result.ok) return res.status(400).json({ message: result.message });
    res.json(result);
  });

  adminApi.post('/llm-providers/test', requireAdmin, async (req, res) => {
    if (!llmProviderService) return res.status(503).json({ message: '未启用 LLM 配置' });
    try {
      const result = await llmProviderService.testDraft(req.body ?? {});
      res.json(result);
    } catch (err) {
      res.status(500).json({
        ok: false,
        message: err instanceof Error ? err.message : '联通测试失败',
      });
    }
  });

  adminApi.post('/llm-providers/keys/:keyId/test', requireAdmin, async (req, res) => {
    if (!llmProviderService) return res.status(503).json({ message: '未启用 LLM 配置' });
    try {
      const result = await llmProviderService.testKey(req.params.keyId, req.body?.model);
      res.json(result);
    } catch (err) {
      res.status(500).json({
        ok: false,
        message: err instanceof Error ? err.message : '联通测试失败',
      });
    }
  });

  adminApi.get('/plaza/pending', requireAdmin, async (_req, res) => {
    if (!plazaPosts) return res.status(503).json({ message: 'Plaza 未启用' });
    const posts = await plazaPosts.listPendingPosts();
    res.json({ data: { posts } });
  });

  adminApi.post('/plaza/posts/:id/review', requireAdmin, async (req, res) => {
    if (!plazaPosts) return res.status(503).json({ message: 'Plaza 未启用' });
    try {
      const action = String(req.body?.action ?? 'approve');
      const reason = req.body?.reason ?? null;
      const result =
        plazaOps && req.currentUser?.id
          ? await plazaOps.reviewPostAsOps(req.currentUser.id, req.params.id, action, { reason })
          : await plazaPosts.reviewPost(req.params.id, action, { reason });
      res.json({ data: result });
    } catch (error) {
      const status = mapPlazaError(error);
      res.status(status).json({
        error: { code: error?.code ?? 'review_failed', message: error.message },
      });
    }
  });

  // ── Subscription management ───────────────────────────────────────────────

  adminApi.get('/subscriptions', requireAdmin, async (req, res) => {
    if (!subscriptionService) return res.status(503).json({ message: '订阅服务未启用' });
    const { userId, status, page, pageSize } = req.query;
    const result = await subscriptionService.listSubscriptions({ userId, status, page, pageSize });
    res.json(result);
  });

  adminApi.get('/subscriptions/plans', requireAdmin, (_req, res) => {
    res.json(
      Object.entries(PLAN_CATALOG).map(([planType, def]) => ({
        planType,
        ...def,
        approxCalls: tokensToCallsApprox(def.periodTokens),
      })),
    );
  });

  adminApi.get('/users/:userId/subscription', requireAdmin, async (req, res) => {
    if (!subscriptionService) return res.status(503).json({ message: '订阅服务未启用' });
    const sub = await subscriptionService.getActiveSubscription(req.params.userId);
    res.json({ subscription: sub });
  });

  adminApi.post('/users/:userId/subscription', requireAdmin, async (req, res) => {
    if (!subscriptionService) return res.status(503).json({ message: '订阅服务未启用' });
    const { planType, durationDays, note } = req.body ?? {};
    if (!planType) return res.status(400).json({ message: '缺少 planType' });
    const result = await subscriptionService.grantSubscription(
      req.params.userId,
      planType,
      durationDays ? Number(durationDays) : undefined,
      req.currentUser.id,
      note,
    );
    if (!result.ok) return res.status(400).json({ message: result.message });
    res.json(result);
  });

  adminApi.delete('/users/:userId/subscription', requireAdmin, async (req, res) => {
    if (!subscriptionService) return res.status(503).json({ message: '订阅服务未启用' });
    const result = await subscriptionService.cancelSubscription(
      req.params.userId,
      req.currentUser.id,
    );
    res.json(result);
  });

  adminApi.post('/subscriptions/expire-stale', requireAdmin, async (_req, res) => {
    if (!subscriptionService) return res.status(503).json({ message: '订阅服务未启用' });
    const count = await subscriptionService.expireStaleSubscriptions();
    res.json({ expiredCount: count });
  });

  return adminApi;
}

/**
 * Plaza operations console API (ops_role: reviewer < editor < ops_admin).
 * @param {object} deps
 * @param {import('express').RequestHandler} deps.jsonBody
 * @param {object|null} deps.plazaOps
 */
export function createOpsApi({ jsonBody, plazaOps }) {
  function makeRequireOps(minRole = 'reviewer') {
    return async (req, res, next) => {
      if (!plazaOps) return sendError(res, req, 503, 'plaza_unavailable', 'Plaza 未启用');
      if (!req.currentUser) return sendError(res, req, 401, 'unauthorized', '未授权，请重新登录');
      const role = await plazaOps.loadOperatorRole(req.currentUser.id);
      if (!hasOpsRole(role, minRole)) {
        return sendError(res, req, 403, 'OPS_PERMISSION_DENIED', '无运营权限');
      }
      req.opsRole = role;
      return next();
    };
  }

  const opsApi = express.Router();
  opsApi.use(jsonBody);
  opsApi.use(makeRequireOps('reviewer'));

  opsApi.get('/review/queue', async (req, res) => {
    try {
      const queue = await plazaOps.listReviewQueue({
        status: req.query.status ?? 'pending_review',
        cursor: req.query.cursor ?? null,
        limit: req.query.limit,
        keyword: req.query.keyword ?? null,
      });
      return sendData(res, req, queue);
    } catch (error) {
      return plazaRouteError(res, req, error);
    }
  });

  opsApi.post('/review/posts/:id', async (req, res) => {
    try {
      const result = await plazaOps.reviewPostAsOps(req.currentUser.id, req.params.id, req.body?.action, {
        reason: req.body?.reason ?? null,
      });
      return sendData(res, req, { post: result });
    } catch (error) {
      return plazaRouteError(res, req, error);
    }
  });

  opsApi.post('/review/batch', makeRequireOps('editor'), async (req, res) => {
    try {
      const result = await plazaOps.batchReviewPosts(req.currentUser.id, req.body ?? {});
      return sendData(res, req, result);
    } catch (error) {
      return plazaRouteError(res, req, error);
    }
  });

  opsApi.get('/reports', async (req, res) => {
    try {
      return sendData(res, req, await plazaOps.listReports({ status: req.query.status ?? 'pending' }));
    } catch (error) {
      return plazaRouteError(res, req, error);
    }
  });

  opsApi.post('/reports/:id/process', async (req, res) => {
    try {
      const result = await plazaOps.processReport(req.currentUser.id, req.params.id, req.body ?? {});
      return sendData(res, req, { report: result });
    } catch (error) {
      return plazaRouteError(res, req, error);
    }
  });

  opsApi.get('/featured', makeRequireOps('editor'), async (req, res) => {
    try {
      return sendData(res, req, await plazaOps.listFeatured());
    } catch (error) {
      return plazaRouteError(res, req, error);
    }
  });

  opsApi.post('/featured', makeRequireOps('editor'), async (req, res) => {
    try {
      const result = await plazaOps.setFeatured(req.currentUser.id, req.body ?? {});
      return sendData(res, req, { featured: result }, 201);
    } catch (error) {
      return plazaRouteError(res, req, error);
    }
  });

  opsApi.delete('/featured/:id', makeRequireOps('editor'), async (req, res) => {
    try {
      const result = await plazaOps.removeFeatured(req.currentUser.id, req.params.id);
      return sendData(res, req, { featured: result });
    } catch (error) {
      return plazaRouteError(res, req, error);
    }
  });

  opsApi.get('/analytics/overview', makeRequireOps('ops_admin'), async (req, res) => {
    try {
      return sendData(res, req, await plazaOps.getAnalyticsOverview());
    } catch (error) {
      return plazaRouteError(res, req, error);
    }
  });

  opsApi.get('/creators', async (req, res) => {
    try {
      return sendData(res, req, await plazaOps.listCreators({ keyword: req.query.keyword ?? null }));
    } catch (error) {
      return plazaRouteError(res, req, error);
    }
  });

  opsApi.patch('/creators/:userId', async (req, res) => {
    try {
      const result = await plazaOps.updateCreator(req.currentUser.id, req.params.userId, req.body ?? {});
      return sendData(res, req, { creator: result });
    } catch (error) {
      return plazaRouteError(res, req, error);
    }
  });

  return opsApi;
}
