import crypto from 'node:crypto';

const REPORT_REASONS = new Set(['spam', 'violence', 'porn', 'political', 'privacy', 'other']);
const FEATURED_POSITIONS = new Set(['homepage_banner', 'category_top', 'trending']);
const OPS_ROLE_RANK = { none: 0, reviewer: 1, editor: 2, ops_admin: 3 };

function opsError(message, code, details) {
  return Object.assign(new Error(message), { code, details });
}

function clampLimit(value, fallback = 20, max = 100) {
  const limit = Number(value ?? fallback);
  if (!Number.isFinite(limit) || limit < 1) return fallback;
  return Math.min(Math.floor(limit), max);
}

export function hasOpsRole(role, minimum) {
  return (OPS_ROLE_RANK[role] ?? 0) >= (OPS_ROLE_RANK[minimum] ?? 0);
}

export const opsInternals = {
  REPORT_REASONS,
  FEATURED_POSITIONS,
  hasOpsRole,
};

export function createPlazaOpsService(
  pool,
  {
    idFactory = () => crypto.randomUUID(),
    formatPostRow,
    reviewPost,
    invalidateFeedCaches = null,
  } = {},
) {
  const loadOperatorRole = async (userId) => {
    const [rows] = await pool.query(`SELECT ops_role FROM h5_users WHERE id = ? LIMIT 1`, [userId]);
    return rows[0]?.ops_role ?? 'none';
  };

  const writeAuditLog = async (operatorId, action, targetType, targetId, detail) => {
    await pool.query(
      `INSERT INTO ops_audit_log (id, operator_id, action, target_type, target_id, detail, created_at)
       VALUES (?, ?, ?, ?, ?, ?, ?)`,
      [idFactory(), operatorId, action, targetType, targetId, JSON.stringify(detail ?? {}), Date.now()],
    );
  };

  const listReviewQueue = async ({
    status = 'pending_review',
    cursor = null,
    limit = 20,
    keyword = null,
  } = {}) => {
    const pageLimit = clampLimit(limit, 20, 100);
    const params = [status];
    let cursorClause = '';
    if (cursor) {
      cursorClause = 'AND pp.published_at < (SELECT published_at FROM plaza_posts WHERE id = ? LIMIT 1)';
      params.push(cursor);
    }
    let keywordClause = '';
    if (keyword) {
      keywordClause = 'AND (pp.title LIKE ? OR pp.user_display_name LIKE ? OR pp.user_slug LIKE ?)';
      const pattern = `%${String(keyword).trim()}%`;
      params.push(pattern, pattern, pattern);
    }
    params.push(pageLimit + 1);
    const [rows] = await pool.query(
      `SELECT pp.id, pp.title, pp.summary, pp.cover_url, pp.status, pp.user_display_name, pp.user_slug,
              pp.published_at, pp.created_at, c.name AS category_name, c.slug AS category_slug, c.icon AS category_icon
       FROM plaza_posts pp
       JOIN plaza_categories c ON c.id = pp.category_id
       WHERE pp.status = ? ${cursorClause} ${keywordClause}
       ORDER BY pp.published_at DESC, pp.id DESC
       LIMIT ?`,
      params,
    );
    const hasMore = rows.length > pageLimit;
    const pageRows = hasMore ? rows.slice(0, pageLimit) : rows;
    const now = Date.now();
    const posts = pageRows.map((row) => ({
      id: row.id,
      title: row.title,
      summary: row.summary ?? '',
      cover_url: row.cover_url ?? '',
      status: row.status,
      author: { display_name: row.user_display_name, slug: row.user_slug },
      category: { name: row.category_name, slug: row.category_slug, icon: row.category_icon ?? '' },
      published_at: new Date(Number(row.published_at)).toISOString(),
      sla_warning: status === 'pending_review' && now - Number(row.published_at) > 2 * 3_600_000,
    }));
    return {
      posts,
      next_cursor: hasMore ? pageRows[pageRows.length - 1].id : null,
      has_more: hasMore,
    };
  };

  const reviewPostAsOps = async (operatorId, postId, action, { reason = null } = {}) => {
    const [beforeRows] = await pool.query(`SELECT status FROM plaza_posts WHERE id = ? LIMIT 1`, [postId]);
    const before = beforeRows[0];
    if (!before) throw opsError('帖子不存在', 'POST_NOT_FOUND');
    const result = await reviewPost(postId, action, { reason });
    await writeAuditLog(operatorId, `${action}_post`, 'post', postId, {
      before_status: before.status,
      after_status: result.status,
      reason: reason ?? null,
    });
    return result;
  };

  const batchReviewPosts = async (operatorId, { post_ids: postIds, action, reason = null } = {}) => {
    const ids = Array.isArray(postIds) ? postIds.map((id) => String(id).trim()).filter(Boolean) : [];
    if (ids.length === 0) throw opsError('post_ids 不能为空', 'invalid_input');
    if (ids.length > 50) throw opsError('单次最多审核 50 条', 'invalid_input');
    const posts = [];
    for (const postId of ids) {
      posts.push(await reviewPostAsOps(operatorId, postId, action, { reason }));
    }
    return { posts };
  };

  const createReport = async (reporterId, { target_type: targetType, target_id: targetId, reason, detail }) => {
    const type = String(targetType ?? '').trim();
    if (type !== 'post' && type !== 'comment') {
      throw opsError('不支持的举报类型', 'invalid_input');
    }
    const reasonValue = String(reason ?? '').trim();
    if (!REPORT_REASONS.has(reasonValue)) throw opsError('不支持的举报原因', 'invalid_input');
    if (type === 'post') {
      const [rows] = await pool.query(
        `SELECT id FROM plaza_posts WHERE id = ? AND status = 'published' LIMIT 1`,
        [targetId],
      );
      if (!rows[0]) throw opsError('帖子不存在', 'POST_NOT_FOUND');
    } else {
      const [rows] = await pool.query(
        `SELECT id FROM plaza_comments WHERE id = ? AND status = 'visible' LIMIT 1`,
        [targetId],
      );
      if (!rows[0]) throw opsError('评论不存在', 'COMMENT_NOT_FOUND');
    }
    const id = idFactory();
    const now = Date.now();
    await pool.query(
      `INSERT INTO plaza_reports
       (id, target_type, target_id, reporter_id, reason, detail, status, created_at)
       VALUES (?, ?, ?, ?, ?, ?, 'pending', ?)`,
      [id, type, targetId, reporterId, reasonValue, String(detail ?? '').trim().slice(0, 500), now],
    );
    return { id, status: 'pending' };
  };

  const listReports = async ({ status = 'pending', limit = 50 } = {}) => {
    const pageLimit = clampLimit(limit, 50, 100);
    const [rows] = await pool.query(
      `SELECT r.*,
              (SELECT COUNT(*) FROM plaza_reports r2
               WHERE r2.target_type = r.target_type AND r2.target_id = r.target_id AND r2.status = 'pending') AS target_report_count
       FROM plaza_reports r
       WHERE r.status = ?
       ORDER BY r.created_at DESC
       LIMIT ?`,
      [status, pageLimit],
    );
    return {
      reports: rows.map((row) => ({
        id: row.id,
        target_type: row.target_type,
        target_id: row.target_id,
        reason: row.reason,
        detail: row.detail,
        status: row.status,
        target_report_count: Number(row.target_report_count ?? 1),
        created_at: new Date(Number(row.created_at)).toISOString(),
      })),
    };
  };

  const processReport = async (operatorId, reportId, { action, action_taken: actionTaken = '' } = {}) => {
    const [rows] = await pool.query(`SELECT * FROM plaza_reports WHERE id = ? LIMIT 1`, [reportId]);
    const report = rows[0];
    if (!report) throw opsError('举报不存在', 'report_not_found');
    const now = Date.now();
    const status = action === 'dismiss' ? 'dismissed' : 'processed';
    await pool.query(
      `UPDATE plaza_reports
       SET status = ?, processed_by = ?, processed_at = ?, action_taken = ?
       WHERE id = ?`,
      [status, operatorId, now, String(actionTaken).slice(0, 200), reportId],
    );
    if (action === 'hide_post' && report.target_type === 'post') {
      await pool.query(
        `UPDATE plaza_posts SET status = 'hidden', updated_at = ? WHERE id = ?`,
        [now, report.target_id],
      );
      await invalidateFeedCaches?.();
    }
    await writeAuditLog(operatorId, 'process_report', 'report', reportId, {
      action,
      target_type: report.target_type,
      target_id: report.target_id,
    });
    return { id: reportId, status };
  };

  const setFeatured = async (operatorId, { post_id: postId, position, sort_order: sortOrder = 0, expires_at: expiresAt = null }) => {
    const pos = String(position ?? '').trim();
    if (!FEATURED_POSITIONS.has(pos)) throw opsError('不支持的精选位', 'invalid_input');
    const [postRows] = await pool.query(
      `SELECT id FROM plaza_posts WHERE id = ? AND status = 'published' LIMIT 1`,
      [postId],
    );
    if (!postRows[0]) throw opsError('帖子不存在或未发布', 'POST_NOT_FOUND');
    const now = Date.now();
    const id = idFactory();
    const expiresMs = expiresAt ? new Date(expiresAt).getTime() : null;
    await pool.query(
      `INSERT INTO plaza_featured (id, post_id, position, sort_order, starts_at, expires_at, created_by, created_at)
       VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
      [id, postId, pos, Number(sortOrder) || 0, now, expiresMs, operatorId, now],
    );
    await invalidateFeedCaches?.();
    await writeAuditLog(operatorId, 'set_featured', 'featured', id, { post_id: postId, position: pos });
    return { id, post_id: postId, position: pos };
  };

  const listFeatured = async () => {
    const now = Date.now();
    const [rows] = await pool.query(
      `SELECT f.*, pp.title, pp.cover_url, pp.user_display_name
       FROM plaza_featured f
       JOIN plaza_posts pp ON pp.id = f.post_id
       WHERE f.starts_at <= ? AND (f.expires_at IS NULL OR f.expires_at > ?)
       ORDER BY f.position ASC, f.sort_order ASC, f.created_at DESC`,
      [now, now],
    );
    return {
      items: rows.map((row) => ({
        id: row.id,
        post_id: row.post_id,
        position: row.position,
        sort_order: row.sort_order,
        title: row.title,
        cover_url: row.cover_url,
        author: row.user_display_name,
        expires_at: row.expires_at ? new Date(Number(row.expires_at)).toISOString() : null,
      })),
    };
  };

  const removeFeatured = async (operatorId, featuredId) => {
    const [result] = await pool.query(`DELETE FROM plaza_featured WHERE id = ?`, [featuredId]);
    if ((result.affectedRows ?? 0) === 0) throw opsError('精选不存在', 'featured_not_found');
    await invalidateFeedCaches?.();
    await writeAuditLog(operatorId, 'remove_featured', 'featured', featuredId, {});
    return { id: featuredId };
  };

  const loadActiveFeaturedPosts = async (viewerId = null) => {
    const now = Date.now();
    const [rows] = await pool.query(
      `SELECT f.position, f.sort_order, pp.*, c.name AS category_name, c.slug AS category_slug, c.icon AS category_icon
       FROM plaza_featured f
       JOIN plaza_posts pp ON pp.id = f.post_id AND pp.status = 'published'
       JOIN plaza_categories c ON c.id = pp.category_id
       WHERE f.starts_at <= ? AND (f.expires_at IS NULL OR f.expires_at > ?)
       ORDER BY f.position ASC, f.sort_order ASC`,
      [now, now],
    );
    const grouped = { homepage_banner: [], trending: [], category_top: {} };
    for (const row of rows) {
      const post = formatPostRow ? formatPostRow(row, { viewerReacted: null }) : row;
      if (row.position === 'homepage_banner') grouped.homepage_banner.push(post);
      if (row.position === 'trending') grouped.trending.push(post);
      if (row.position === 'category_top') {
        const slug = row.category_slug;
        if (!grouped.category_top[slug]) grouped.category_top[slug] = [];
        grouped.category_top[slug].push(post);
      }
    }
    return grouped;
  };

  const getAnalyticsOverview = async () => {
    const now = Date.now();
    const dayMs = 86_400_000;
    const todayStart = now - (now % dayMs);
    const yesterdayStart = todayStart - dayMs;

    const [[todayPosts]] = await pool.query(
      `SELECT COUNT(*) AS count FROM plaza_posts WHERE status = 'published' AND published_at >= ?`,
      [todayStart],
    );
    const [[yesterdayPosts]] = await pool.query(
      `SELECT COUNT(*) AS count FROM plaza_posts WHERE status = 'published' AND published_at >= ? AND published_at < ?`,
      [yesterdayStart, todayStart],
    );
    const [[pendingReview]] = await pool.query(
      `SELECT COUNT(*) AS count FROM plaza_posts WHERE status = 'pending_review'`,
    );
    const [[signupFromPlaza]] = await pool.query(
      `SELECT COUNT(*) AS count FROM plaza_attribution_events
       WHERE event_type = 'signup' AND utm_source = 'plaza' AND created_at >= ?`,
      [todayStart],
    );
    const [categoryRows] = await pool.query(
      `SELECT c.name, COUNT(p.id) AS count
       FROM plaza_categories c
       LEFT JOIN plaza_posts p ON p.category_id = c.id AND p.status = 'published'
       GROUP BY c.id, c.name
       ORDER BY count DESC`,
    );
    const [topCreators] = await pool.query(
      `SELECT user_slug AS slug, user_display_name AS display_name, COUNT(*) AS post_count,
              SUM(like_count) AS total_likes
       FROM plaza_posts
       WHERE status = 'published'
       GROUP BY user_id, user_slug, user_display_name
       ORDER BY total_likes DESC
       LIMIT 10`,
    );
    const [dailyPosts] = await pool.query(
      `SELECT DATE(FROM_UNIXTIME(published_at / 1000)) AS day, COUNT(*) AS count
       FROM plaza_posts
       WHERE status = 'published' AND published_at >= ?
       GROUP BY day
       ORDER BY day ASC`,
      [now - 14 * dayMs],
    );

    return {
      today: {
        new_posts: Number(todayPosts?.count ?? 0),
        plaza_signups: Number(signupFromPlaza?.count ?? 0),
        pending_review: Number(pendingReview?.count ?? 0),
      },
      yesterday: {
        new_posts: Number(yesterdayPosts?.count ?? 0),
      },
      categories: categoryRows.map((row) => ({
        name: row.name,
        count: Number(row.count ?? 0),
      })),
      top_creators: topCreators.map((row) => ({
        slug: row.slug,
        display_name: row.display_name,
        post_count: Number(row.post_count ?? 0),
        total_likes: Number(row.total_likes ?? 0),
      })),
      daily_posts: dailyPosts.map((row) => ({
        day: row.day,
        count: Number(row.count ?? 0),
      })),
    };
  };

  const listCreators = async ({ keyword = null, limit = 50 } = {}) => {
    const pageLimit = clampLimit(limit, 50, 100);
    const params = [];
    let keywordClause = '';
    if (keyword) {
      keywordClause = 'AND (u.username LIKE ? OR u.display_name LIKE ? OR u.slug LIKE ?)';
      const pattern = `%${String(keyword).trim()}%`;
      params.push(pattern, pattern, pattern);
    }
    params.push(pageLimit);
    const [rows] = await pool.query(
      `SELECT u.id, u.slug, u.username, u.display_name, u.plaza_post_count, u.plaza_follower_count,
              u.plaza_verified, u.plaza_post_banned, u.plaza_comment_banned, u.ops_role
       FROM h5_users u
       WHERE u.plaza_post_count > 0 ${keywordClause}
       ORDER BY u.plaza_post_count DESC
       LIMIT ?`,
      params,
    );
    return {
      creators: rows.map((row) => ({
        user_id: row.id,
        slug: row.slug || row.username,
        display_name: row.display_name || row.username,
        post_count: Number(row.plaza_post_count ?? 0),
        follower_count: Number(row.plaza_follower_count ?? 0),
        verified: Boolean(row.plaza_verified),
        post_banned: Boolean(row.plaza_post_banned),
        comment_banned: Boolean(row.plaza_comment_banned),
      })),
    };
  };

  const updateCreator = async (operatorId, userId, patch) => {
    const updates = [];
    const params = [];
    if (patch?.verified != null) {
      updates.push('plaza_verified = ?');
      params.push(patch.verified ? 1 : 0);
    }
    if (patch?.post_banned != null) {
      updates.push('plaza_post_banned = ?');
      params.push(patch.post_banned ? 1 : 0);
    }
    if (patch?.comment_banned != null) {
      updates.push('plaza_comment_banned = ?');
      params.push(patch.comment_banned ? 1 : 0);
    }
    if (updates.length === 0) throw opsError('没有可更新的字段', 'invalid_input');
    updates.push('updated_at = ?');
    params.push(Date.now(), userId);
    const [result] = await pool.query(
      `UPDATE h5_users SET ${updates.join(', ')} WHERE id = ?`,
      params,
    );
    if ((result.affectedRows ?? 0) === 0) throw opsError('用户不存在', 'user_not_found');
    await writeAuditLog(operatorId, 'update_creator', 'user', userId, patch);
    return { user_id: userId };
  };

  return {
    loadOperatorRole,
    hasOpsRole,
    listReviewQueue,
    reviewPostAsOps,
    batchReviewPosts,
    createReport,
    listReports,
    processReport,
    setFeatured,
    listFeatured,
    removeFeatured,
    loadActiveFeaturedPosts,
    getAnalyticsOverview,
    listCreators,
    updateCreator,
  };
}
