import express from 'express';
import crypto from 'node:crypto';
import { createPlazaPostService, formatPostRow, mapPlazaError } from './plaza-posts.mjs';
import { createPlazaEventService } from './plaza-events.mjs';
import { createPlazaRecommendService } from './plaza-recommend.mjs';
import { createPlazaInteractionService } from './plaza-interactions.mjs';
import { parseCookies } from './auth.mjs';
import {
  ensureAlgorithmConfig,
  loadAlgorithmConfig,
  recalculateHotScores,
} from './plaza-algorithm.mjs';
import { createPlazaRedis, createNoopPlazaRedis } from './plaza-redis.mjs';
import { startPlazaTasks, writebackPublications } from './plaza-tasks.mjs';
import { createPlazaSeoService } from './plaza-seo.mjs';
import { createPlazaOpsService, hasOpsRole } from './plaza-ops.mjs';

export async function bootstrapPlaza(pool) {
  await ensureAlgorithmConfig(pool);
  const algorithmConfig = await loadAlgorithmConfig(pool);
  const redis = await createPlazaRedis(process.env.PLAZA_REDIS_URL, pool);
  if (redis.enabled) {
    console.log('Plaza Redis enabled');
  }
  const seo = createPlazaSeoService(pool);
  const interactions = createPlazaInteractionService(pool, {
    formatPostRow,
    plazaRedis: redis,
  });
  const events = createPlazaEventService(pool);
  const recommend = createPlazaRecommendService(pool, {
    eventService: events,
    formatPostRow,
    loadViewerReactions: (viewerId, postIds) =>
      interactions.loadViewerReactions(viewerId, postIds),
    algorithmConfig,
  });

  let ops = null;
  const posts = createPlazaPostService(pool, {
    loadViewerReactions: (viewerId, postIds) =>
      interactions.loadViewerReactions(viewerId, postIds),
    plazaRedis: redis,
    algorithmConfig,
    recommendService: recommend,
    onPostPublished: (postId) => seo?.notifyPostPublished(postId),
    loadFeaturedPosts: async (viewerId) => {
      if (!ops) return { homepage_banner: [], trending: [], category_top: {} };
      return ops.loadActiveFeaturedPosts(viewerId);
    },
  });
  ops = createPlazaOpsService(pool, {
    formatPostRow,
    reviewPost: (...args) => posts.reviewPost(...args),
    invalidateFeedCaches: () => redis?.invalidateFeedCaches?.(),
  });
  startPlazaTasks({
    pool,
    plazaRedis: redis,
    recalculateHotScores,
    writebackPublications,
  });
  return { posts, interactions, events, recommend, seo, ops, redis };
}

export function createNoopPlaza() {
  return { posts: null, interactions: null, seo: null, ops: null, redis: createNoopPlazaRedis() };
}

export function isPlazaPublicRead(path, method) {
  if (!path.startsWith('/plaza/v1/')) return false;
  if (method === 'POST' && path === '/plaza/v1/events') return true;
  if (method !== 'GET') return false;
  return (
    path === '/plaza/v1/feed' ||
    path === '/plaza/v1/categories' ||
    path === '/plaza/v1/seo/sitemap' ||
    /^\/plaza\/v1\/posts\/[^/]+$/.test(path) ||
    /^\/plaza\/v1\/posts\/[^/]+\/comments$/.test(path) ||
    /^\/plaza\/v1\/users\/[^/]+$/.test(path) ||
    /^\/plaza\/v1\/users\/[^/]+\/posts$/.test(path)
  );
}

export function plazaClientIp(req) {
  const forwarded = req.headers['x-forwarded-for'];
  if (typeof forwarded === 'string' && forwarded.length > 0) {
    return forwarded.split(',')[0].trim();
  }
  return req.ip;
}

const PLAZA_SID_COOKIE = 'plaza_sid';

function resolvePlazaSessionId(req, res) {
  const cookies = parseCookies(req.get('cookie'));
  let sessionId = cookies[PLAZA_SID_COOKIE];
  if (!sessionId) {
    sessionId = crypto.randomUUID();
    res.append(
      'Set-Cookie',
      `${PLAZA_SID_COOKIE}=${sessionId}; Path=/; Max-Age=31536000; SameSite=Lax; HttpOnly`,
    );
  }
  return sessionId;
}

function recordPlazaEventsAsync(eventsService, req, res, events) {
  if (!eventsService || !Array.isArray(events) || events.length === 0) return;
  const sessionId = resolvePlazaSessionId(req, res);
  void eventsService
    .recordEvents({
      userId: req.currentUser?.id ?? null,
      sessionId,
      events,
    })
    .catch(() => {});
}

function reactionEventType(type) {
  if (type === 'like' || type === 'collect' || type === 'share') return type;
  return null;
}

function plazaRouteError(res, req, error, { sendError }) {
  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);
}

export function attachPlazaApiRoutes(api, plaza, { sendData, sendError }) {
  const { posts, interactions, events, seo, ops, redis } = plaza;

  function ensurePosts(res, req) {
    if (!posts) { sendError(res, req, 503, 'plaza_unavailable', 'Plaza 未启用'); return false; }
    return true;
  }
  function ensureInteractions(res, req) {
    if (!interactions) { sendError(res, req, 503, 'plaza_unavailable', 'Plaza 未启用'); return false; }
    return true;
  }
  function routeError(res, req, error) {
    return plazaRouteError(res, req, error, { sendError });
  }

  function makeRequireOps(minRole = 'reviewer') {
    return async (req, res, next) => {
      if (!ops) return sendError(res, req, 503, 'plaza_unavailable', 'Plaza 未启用');
      if (!req.currentUser) return sendError(res, req, 401, 'unauthorized', '未授权，请重新登录');
      const role = await ops.loadOperatorRole(req.currentUser.id);
      if (!hasOpsRole(role, minRole)) {
        return sendError(res, req, 403, 'OPS_PERMISSION_DENIED', '无运营权限');
      }
      req.opsRole = role;
      return next();
    };
  }

  // ---- Plaza feed & posts ----

  api.get('/plaza/v1/categories', async (req, res) => {
    if (!ensurePosts(res, req)) return;
    try {
      return sendData(res, req, { categories: await posts.listCategories() });
    } catch (error) {
      return routeError(res, req, error);
    }
  });

  api.get('/plaza/v1/seo/sitemap', async (req, res) => {
    if (!ensurePosts(res, req)) return;
    if (!seo) return sendError(res, req, 503, 'plaza_unavailable', 'Plaza SEO 未启用');
    try {
      const data = await seo.listSitemapData({
        postLimit: req.query.post_limit,
        userLimit: req.query.user_limit,
      });
      return sendData(res, req, data);
    } catch (error) {
      return routeError(res, req, error);
    }
  });

  api.post('/plaza/v1/attribution/events', async (req, res) => {
    if (!seo) return sendError(res, req, 503, 'plaza_unavailable', 'Plaza 未启用');
    try {
      const result = await seo.recordAttribution(req.body ?? {}, plazaClientIp(req));
      return sendData(res, req, result, 201);
    } catch (error) {
      return routeError(res, req, error);
    }
  });

  api.post('/plaza/v1/posts/:id/reports', async (req, res) => {
    if (!ops) return sendError(res, req, 503, 'plaza_unavailable', 'Plaza 未启用');
    if (!req.currentUser) return sendError(res, req, 401, 'unauthorized', '未授权，请重新登录');
    try {
      const report = await ops.createReport(req.currentUser.id, {
        target_type: 'post',
        target_id: req.params.id,
        reason: req.body?.reason,
        detail: req.body?.detail,
      });
      return sendData(res, req, { report }, 201);
    } catch (error) {
      return routeError(res, req, error);
    }
  });

  api.post('/plaza/v1/comments/:id/reports', async (req, res) => {
    if (!ops) return sendError(res, req, 503, 'plaza_unavailable', 'Plaza 未启用');
    if (!req.currentUser) return sendError(res, req, 401, 'unauthorized', '未授权，请重新登录');
    try {
      const report = await ops.createReport(req.currentUser.id, {
        target_type: 'comment',
        target_id: req.params.id,
        reason: req.body?.reason,
        detail: req.body?.detail,
      });
      return sendData(res, req, { report }, 201);
    } catch (error) {
      return routeError(res, req, error);
    }
  });

  api.get('/plaza/v1/feed', async (req, res) => {
    if (!ensurePosts(res, req)) return;
    try {
      const sessionId = resolvePlazaSessionId(req, res);
      const feed = await posts.listFeed({
        sort: req.query.sort,
        categorySlug: req.query.category ?? null,
        cursor: req.query.cursor ?? null,
        limit: req.query.limit,
        viewerId: req.currentUser?.id ?? null,
        sessionId,
      });
      return sendData(res, req, feed);
    } catch (error) {
      return routeError(res, req, error);
    }
  });

  api.post('/plaza/v1/events', async (req, res) => {
    if (!ensurePosts(res, req)) return;
    if (!events) return sendError(res, req, 503, 'plaza_unavailable', 'Plaza 未启用');
    try {
      const sessionId =
        String(req.body?.session_id ?? '').trim() || resolvePlazaSessionId(req, res);
      const result = await events.recordEvents({
        userId: req.currentUser?.id ?? null,
        sessionId,
        events: req.body?.events ?? [],
      });
      return sendData(res, req, { ...result, session_id: sessionId }, 201);
    } catch (error) {
      return routeError(res, req, error);
    }
  });

  api.post('/plaza/v1/posts/:id/reactions', async (req, res) => {
    if (!ensureInteractions(res, req)) return;
    if (!req.currentUser) return sendError(res, req, 401, 'unauthorized', '未授权，请重新登录');
    try {
      const result = await interactions.addReaction(req.currentUser.id, req.params.id, req.body?.type);
      const eventType = reactionEventType(result.type);
      if (eventType) {
        recordPlazaEventsAsync(events, req, res, [{ event_type: eventType, post_id: req.params.id }]);
      }
      return sendData(res, req, result);
    } catch (error) {
      return routeError(res, req, error);
    }
  });

  api.delete('/plaza/v1/posts/:id/reactions/:type', async (req, res) => {
    if (!ensureInteractions(res, req)) return;
    if (!req.currentUser) return sendError(res, req, 401, 'unauthorized', '未授权，请重新登录');
    try {
      const result = await interactions.removeReaction(req.currentUser.id, req.params.id, req.params.type);
      return sendData(res, req, result);
    } catch (error) {
      return routeError(res, req, error);
    }
  });

  api.get('/plaza/v1/posts/:id/comments', async (req, res) => {
    if (!ensureInteractions(res, req)) return;
    try {
      const comments = await interactions.listComments(req.params.id, {
        cursor: req.query.cursor ?? null,
        limit: req.query.limit,
        parentId: req.query.parent_id ?? null,
        viewerId: req.currentUser?.id ?? null,
      });
      return sendData(res, req, comments);
    } catch (error) {
      return routeError(res, req, error);
    }
  });

  api.post('/plaza/v1/posts/:id/comments', async (req, res) => {
    if (!ensureInteractions(res, req)) return;
    if (!req.currentUser) return sendError(res, req, 401, 'unauthorized', '未授权，请重新登录');
    try {
      const comment = await interactions.createComment(req.currentUser.id, req.params.id, req.body ?? {});
      recordPlazaEventsAsync(events, req, res, [{ event_type: 'comment', post_id: req.params.id }]);
      return sendData(res, req, { comment }, 201);
    } catch (error) {
      return routeError(res, req, error);
    }
  });

  api.delete('/plaza/v1/comments/:id', async (req, res) => {
    if (!ensureInteractions(res, req)) return;
    if (!req.currentUser) return sendError(res, req, 401, 'unauthorized', '未授权，请重新登录');
    try {
      const comment = await interactions.deleteComment(req.currentUser.id, req.params.id);
      return sendData(res, req, { comment });
    } catch (error) {
      return routeError(res, req, error);
    }
  });

  api.post('/plaza/v1/comments/:id/reactions', async (req, res) => {
    if (!ensureInteractions(res, req)) return;
    if (!req.currentUser) return sendError(res, req, 401, 'unauthorized', '未授权，请重新登录');
    try {
      const liked = req.body?.liked !== false;
      const result = await interactions.toggleCommentLike(req.currentUser.id, req.params.id, liked);
      return sendData(res, req, result);
    } catch (error) {
      return routeError(res, req, error);
    }
  });

  api.get('/plaza/v1/users/:slug', async (req, res) => {
    if (!ensureInteractions(res, req)) return;
    try {
      const profile = await interactions.getUserProfile(req.params.slug, req.currentUser?.id ?? null);
      return sendData(res, req, profile);
    } catch (error) {
      return routeError(res, req, error);
    }
  });

  api.get('/plaza/v1/users/:slug/posts', async (req, res) => {
    if (!ensureInteractions(res, req)) return;
    try {
      const feed = await interactions.listUserPosts(req.params.slug, {
        cursor: req.query.cursor ?? null,
        limit: req.query.limit,
        viewerId: req.currentUser?.id ?? null,
      });
      return sendData(res, req, feed);
    } catch (error) {
      return routeError(res, req, error);
    }
  });

  api.post('/plaza/v1/users/:slug/follow', async (req, res) => {
    if (!ensureInteractions(res, req)) return;
    if (!req.currentUser) return sendError(res, req, 401, 'unauthorized', '未授权，请重新登录');
    try {
      const result = await interactions.followUser(req.currentUser.id, req.params.slug);
      return sendData(res, req, result);
    } catch (error) {
      return routeError(res, req, error);
    }
  });

  api.delete('/plaza/v1/users/:slug/follow', async (req, res) => {
    if (!ensureInteractions(res, req)) return;
    if (!req.currentUser) return sendError(res, req, 401, 'unauthorized', '未授权，请重新登录');
    try {
      const result = await interactions.unfollowUser(req.currentUser.id, req.params.slug);
      return sendData(res, req, result);
    } catch (error) {
      return routeError(res, req, error);
    }
  });

  api.get('/plaza/v1/posts/:id', async (req, res) => {
    if (!ensurePosts(res, req)) return;
    try {
      const post = await posts.getPostById(req.params.id, {
        viewerId: req.currentUser?.id ?? null,
      });
      void redis.recordView(req.params.id, plazaClientIp(req)).catch(() => {});
      recordPlazaEventsAsync(events, req, res, [{ event_type: 'view', post_id: req.params.id }]);
      return sendData(res, req, { post });
    } catch (error) {
      return routeError(res, req, error);
    }
  });

  api.post('/plaza/v1/posts', async (req, res) => {
    if (!ensurePosts(res, req)) return;
    if (!req.currentUser) return sendError(res, req, 401, 'unauthorized', '未授权，请重新登录');
    try {
      const post = await posts.createPost(req.currentUser.id, req.body ?? {});
      return sendData(res, req, { post }, 201);
    } catch (error) {
      return routeError(res, req, error);
    }
  });

  api.patch('/plaza/v1/posts/:id', async (req, res) => {
    if (!ensurePosts(res, req)) return;
    if (!req.currentUser) return sendError(res, req, 401, 'unauthorized', '未授权，请重新登录');
    try {
      const post = await posts.updatePost(req.currentUser.id, req.params.id, req.body ?? {});
      return sendData(res, req, { post });
    } catch (error) {
      return routeError(res, req, error);
    }
  });

  api.delete('/plaza/v1/posts/:id', async (req, res) => {
    if (!ensurePosts(res, req)) return;
    if (!req.currentUser) return sendError(res, req, 401, 'unauthorized', '未授权，请重新登录');
    try {
      const post = await posts.hidePost(req.currentUser.id, req.params.id);
      return sendData(res, req, { post });
    } catch (error) {
      return routeError(res, req, error);
    }
  });

  // ---- Ops API ----

  const opsApi = express.Router();
  opsApi.use(express.json({ limit: '1mb' }));
  opsApi.use(makeRequireOps('reviewer'));

  opsApi.get('/review/queue', async (req, res) => {
    try {
      const queue = await ops.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 routeError(res, req, error);
    }
  });

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

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

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

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

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

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

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

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

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

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

  api.use('/ops/v1', opsApi);
}

export function attachPlazaAdminRoutes(adminApi, plaza, { sendError }) {
  const { posts, ops } = plaza;

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

  adminApi.post('/plaza/posts/:id/review', async (req, res) => {
    if (!posts) return res.status(503).json({ message: 'Plaza 未启用' });
    try {
      const action = String(req.body?.action ?? 'approve');
      const reason = req.body?.reason ?? null;
      const result =
        ops && req.currentUser?.id
          ? await ops.reviewPostAsOps(req.currentUser.id, req.params.id, action, { reason })
          : await posts.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 },
      });
    }
  });
}
