const express = require('express');
const { Op } = require('sequelize');
const { ForumPost, ForumComment, PostTag, User } = require('../models');
const { authenticateToken, requireAdmin, optionalAuth } = require('../middleware/auth');
const logger = require('../utils/logger');
const emailServices = require('../services/email');
const googleMapsService = require('../services/googleMapsService');
const locationService = require('../services/locationService');
const { validateS3Keys } = require('../utils/s3KeyValidator');
const { IMAGE_LIMITS } = require('../config/imageLimits');
const router = express.Router();

// Helper function to build nested comment tree
const buildCommentTree = (comments, isAdmin = false) => {
  const commentMap = {};
  const rootComments = [];

  // Create a map of all comments
  comments.forEach(comment => {
    const commentJson = comment.toJSON();

    // Sanitize deleted comments for non-admin users
    if (commentJson.isDeleted && !isAdmin) {
      commentJson.content = '';
      commentJson.imageFilenames = [];
    }

    commentMap[comment.id] = { ...commentJson, replies: [] };
  });

  // Build the tree structure
  comments.forEach(comment => {
    if (comment.parentCommentId && commentMap[comment.parentCommentId]) {
      commentMap[comment.parentCommentId].replies.push(commentMap[comment.id]);
    } else if (!comment.parentCommentId) {
      rootComments.push(commentMap[comment.id]);
    }
  });

  return rootComments;
};

// GET /api/forum/posts - Browse all posts
router.get('/posts', optionalAuth, async (req, res, next) => {
  try {
    const {
      search,
      category,
      tag,
      status,
      page = 1,
      limit = 20,
      sort = 'recent'
    } = req.query;

    const where = {};

    // Filter out deleted posts unless user is admin
    if (!req.user || req.user.role !== 'admin') {
      where.isDeleted = false;
    }

    if (category) {
      where.category = category;
    }

    if (status) {
      where.status = status;
    }

    if (search) {
      where[Op.or] = [
        { title: { [Op.iLike]: `%${search}%` } },
        { content: { [Op.iLike]: `%${search}%` } }
      ];
    }

    const offset = (page - 1) * limit;

    // Determine sort order
    let order;
    switch (sort) {
      case 'comments':
        order = [['commentCount', 'DESC'], ['createdAt', 'DESC']];
        break;
      case 'views':
        order = [['viewCount', 'DESC'], ['createdAt', 'DESC']];
        break;
      case 'recent':
      default:
        order = [['isPinned', 'DESC'], ['updatedAt', 'DESC']];
        break;
    }

    const include = [
      {
        model: User,
        as: 'author',
        attributes: ['id', 'firstName', 'lastName']
      },
      {
        model: PostTag,
        as: 'tags',
        attributes: ['id', 'tagName']
      },
      {
        model: ForumComment,
        as: 'comments',
        attributes: ['id', 'isDeleted'],
        required: false
      }
    ];

    // Filter by tag if provided
    if (tag) {
      include[1].where = { tagName: tag };
      include[1].required = true;
    }

    const { count, rows } = await ForumPost.findAndCountAll({
      where,
      include,
      limit: parseInt(limit),
      offset: parseInt(offset),
      order,
      distinct: true
    });

    // Add hasDeletedComments flag to each post
    const postsWithFlags = rows.map(post => {
      const postJson = post.toJSON();
      postJson.hasDeletedComments = postJson.comments?.some(c => c.isDeleted) || false;
      // Remove comments array to reduce response size (only needed the flag)
      delete postJson.comments;
      return postJson;
    });

    const reqLogger = logger.withRequestId(req.id);
    reqLogger.info("Forum posts fetched", {
      search,
      category,
      tag,
      status,
      postsCount: count,
      page: parseInt(page),
      limit: parseInt(limit)
    });

    res.json({
      posts: postsWithFlags,
      totalPages: Math.ceil(count / limit),
      currentPage: parseInt(page),
      totalPosts: count
    });
  } catch (error) {
    const reqLogger = logger.withRequestId(req.id);
    reqLogger.error("Forum posts fetch failed", {
      error: error.message,
      stack: error.stack,
      query: req.query
    });
    next(error);
  }
});

// GET /api/forum/posts/:id - Get single post with all comments
router.get('/posts/:id', optionalAuth, async (req, res, next) => {
  try {
    const post = await ForumPost.findByPk(req.params.id, {
      include: [
        {
          model: User,
          as: 'author',
          attributes: ['id', 'firstName', 'lastName', 'role']
        },
        {
          model: User,
          as: 'closer',
          attributes: ['id', 'firstName', 'lastName', 'role'],
          required: false
        },
        {
          model: PostTag,
          as: 'tags',
          attributes: ['id', 'tagName']
        },
        {
          model: ForumComment,
          as: 'comments',
          required: false,
          include: [
            {
              model: User,
              as: 'author',
              attributes: ['id', 'firstName', 'lastName', 'role']
            }
          ]
        }
      ],
      order: [
        [{ model: ForumComment, as: 'comments' }, 'createdAt', 'ASC']
      ]
    });

    if (!post) {
      return res.status(404).json({ error: 'Post not found' });
    }

    // Hide deleted posts from non-admins
    if (post.isDeleted && (!req.user || req.user.role !== 'admin')) {
      return res.status(404).json({ error: 'Post not found' });
    }

    // Increment view count without updating the updatedAt timestamp
    await post.increment('viewCount', { silent: true });

    // Build nested comment tree
    const isAdmin = req.user && req.user.role === 'admin';
    const postData = post.toJSON();
    postData.comments = buildCommentTree(post.comments, isAdmin);
    postData.viewCount += 1; // Reflect the increment

    const reqLogger = logger.withRequestId(req.id);
    reqLogger.info("Forum post fetched", {
      postId: req.params.id,
      authorId: post.authorId
    });

    res.json(postData);
  } catch (error) {
    const reqLogger = logger.withRequestId(req.id);
    reqLogger.error("Forum post fetch failed", {
      error: error.message,
      stack: error.stack,
      postId: req.params.id
    });
    next(error);
  }
});

// POST /api/forum/posts - Create new post
router.post('/posts', authenticateToken, async (req, res, next) => {
  try {
    // Require email verification
    if (!req.user.isVerified) {
      return res.status(403).json({
        error: "Please verify your email address before creating forum posts.",
        code: "EMAIL_NOT_VERIFIED"
      });
    }

    let { title, content, category, tags, zipCode, latitude: providedLat, longitude: providedLng, imageFilenames: rawImageFilenames } = req.body;

    // Ensure imageFilenames is an array and validate S3 keys
    const imageFilenamesArray = Array.isArray(rawImageFilenames) ? rawImageFilenames : [];

    const keyValidation = validateS3Keys(imageFilenamesArray, 'forum', { maxKeys: IMAGE_LIMITS.forum });
    if (!keyValidation.valid) {
      return res.status(400).json({
        error: keyValidation.error,
        details: keyValidation.invalidKeys
      });
    }

    const imageFilenames = imageFilenamesArray;

    // Initialize location fields
    let latitude = null;
    let longitude = null;

    // Use provided coordinates if available, otherwise geocode zip code
    if (category === 'item_request' && zipCode) {
      // If coordinates were provided from a saved address, use them directly
      if (providedLat && providedLng) {
        latitude = parseFloat(providedLat);
        longitude = parseFloat(providedLng);

        const reqLogger = logger.withRequestId(req.id);
        reqLogger.info("Using provided coordinates for item request", {
          zipCode,
          latitude,
          longitude,
          source: 'saved_address'
        });
      } else {
        // Otherwise, geocode the zip code
        try {
          const geocodeResult = await googleMapsService.geocodeAddress(zipCode);

          // Check if geocoding was successful
          if (geocodeResult.error) {
            const reqLogger = logger.withRequestId(req.id);
            reqLogger.error("Geocoding failed for item request", {
              error: geocodeResult.error,
              status: geocodeResult.status,
              zipCode
            });
          } else if (geocodeResult.latitude && geocodeResult.longitude) {
            latitude = geocodeResult.latitude;
            longitude = geocodeResult.longitude;

            const reqLogger = logger.withRequestId(req.id);
            reqLogger.info("Geocoded zip code for item request", {
              zipCode,
              latitude,
              longitude,
              source: 'geocoded'
            });
          }
        } catch (error) {
          const reqLogger = logger.withRequestId(req.id);
          reqLogger.error("Geocoding failed for item request", {
            error: error.message,
            stack: error.stack,
            zipCode
          });
          // Continue without coordinates - post will still be created
        }
      }
    }

    const post = await ForumPost.create({
      title,
      content,
      category,
      authorId: req.user.id,
      imageFilenames,
      zipCode: zipCode || null,
      latitude,
      longitude
    });

    // Create tags if provided
    if (tags && Array.isArray(tags) && tags.length > 0) {
      const tagPromises = tags.map(tagName =>
        PostTag.create({
          postId: post.id,
          tagName: tagName.toLowerCase().trim()
        })
      );
      await Promise.all(tagPromises);
    }

    const postWithDetails = await ForumPost.findByPk(post.id, {
      include: [
        {
          model: User,
          as: 'author',
          attributes: ['id', 'firstName', 'lastName']
        },
        {
          model: PostTag,
          as: 'tags',
          attributes: ['id', 'tagName']
        }
      ]
    });

    const reqLogger = logger.withRequestId(req.id);
    reqLogger.info("Forum post created", {
      postId: post.id,
      authorId: req.user.id,
      category,
      title
    });

    res.status(201).json(postWithDetails);

    // Send location-based notifications for item requests (asynchronously)
    if (category === 'item_request' && latitude && longitude) {
      (async () => {
        try {
          logger.info("Starting item request notifications", {
            postId: post.id,
            latitude,
            longitude,
            zipCode
          });

          // Find all users within maximum radius (100 miles)
          const nearbyUsers = await locationService.findUsersInRadius(
            latitude,
            longitude,
            100
          );

          logger.info("Found nearby users", {
            postId: post.id,
            count: nearbyUsers.length,
            users: nearbyUsers.map(u => ({ id: u.id, distance: u.distance }))
          });

          const postAuthor = await User.findByPk(req.user.id);

          let notificationsSent = 0;
          let usersChecked = 0;
          let usersSkipped = 0;

          for (const user of nearbyUsers) {
            // Don't notify the requester
            if (user.id !== req.user.id) {
              usersChecked++;

              // Get user's notification preference
              const userProfile = await User.findByPk(user.id, {
                attributes: ['itemRequestNotificationRadius']
              });

              const userPreferredRadius = userProfile?.itemRequestNotificationRadius;

              // Skip if user has disabled notifications (null)
              if (userPreferredRadius === null || userPreferredRadius === undefined) {
                logger.info("User has disabled item request notifications", {
                  postId: post.id,
                  userId: user.id,
                  userDistance: user.distance
                });
                usersSkipped++;
                continue;
              }

              // Default to 10 miles if somehow not set
              const effectiveRadius = userPreferredRadius || 10;

              logger.info("Checking user notification eligibility", {
                postId: post.id,
                userId: user.id,
                userEmail: user.email,
                userCoordinates: { lat: user.latitude, lng: user.longitude },
                postCoordinates: { lat: latitude, lng: longitude },
                userDistance: user.distance,
                userPreferredRadius: effectiveRadius,
                willNotify: parseFloat(user.distance) <= effectiveRadius
              });

              // Only notify if within user's preferred radius
              if (parseFloat(user.distance) <= effectiveRadius) {
                try {
                  await emailServices.forum.sendItemRequestNotification(
                    user,
                    postAuthor,
                    post,
                    user.distance
                  );
                  notificationsSent++;
                  logger.info("Sent notification to user", {
                    postId: post.id,
                    userId: user.id,
                    distance: user.distance
                  });
                } catch (emailError) {
                  logger.error("Failed to send item request notification", {
                    error: emailError.message,
                    stack: emailError.stack,
                    recipientId: user.id,
                    postId: post.id
                  });
                }
              } else {
                usersSkipped++;
              }
            }
          }

          logger.info("Item request notifications complete", {
            postId: post.id,
            totalNearbyUsers: nearbyUsers.length,
            usersChecked,
            usersSkipped,
            notificationsSent
          });
        } catch (error) {
          logger.error("Failed to process item request notifications", {
            error: error.message,
            stack: error.stack,
            postId: post.id
          });
        }
      })();
    } else if (category === 'item_request') {
      logger.warn("Item request created without location", {
        postId: post.id,
        zipCode,
        hasLatitude: !!latitude,
        hasLongitude: !!longitude
      });
    }
  } catch (error) {
    const reqLogger = logger.withRequestId(req.id);
    reqLogger.error("Forum post creation failed", {
      error: error.message,
      stack: error.stack,
      authorId: req.user.id,
      postData: logger.sanitize(req.body)
    });
    next(error);
  }
});

// PUT /api/forum/posts/:id - Update post
router.put('/posts/:id', authenticateToken, async (req, res, next) => {
  try {
    // Require email verification
    if (!req.user.isVerified) {
      return res.status(403).json({
        error: "Please verify your email address before editing forum posts.",
        code: "EMAIL_NOT_VERIFIED"
      });
    }

    const post = await ForumPost.findByPk(req.params.id);

    if (!post) {
      return res.status(404).json({ error: 'Post not found' });
    }

    if (post.authorId !== req.user.id) {
      return res.status(403).json({ error: 'Unauthorized' });
    }

    const { title, content, category, tags, imageFilenames: rawImageFilenames } = req.body;

    // Build update object
    const updateData = { title, content, category };

    // Handle imageFilenames if provided
    if (rawImageFilenames !== undefined) {
      const imageFilenamesArray = Array.isArray(rawImageFilenames) ? rawImageFilenames : [];

      const keyValidation = validateS3Keys(imageFilenamesArray, 'forum', { maxKeys: IMAGE_LIMITS.forum });
      if (!keyValidation.valid) {
        return res.status(400).json({
          error: keyValidation.error,
          details: keyValidation.invalidKeys
        });
      }
      updateData.imageFilenames = imageFilenamesArray;
    }

    await post.update(updateData);

    // Update tags if provided
    if (tags !== undefined) {
      // Delete existing tags
      await PostTag.destroy({ where: { postId: post.id } });

      // Create new tags
      if (Array.isArray(tags) && tags.length > 0) {
        const tagPromises = tags.map(tagName =>
          PostTag.create({
            postId: post.id,
            tagName: tagName.toLowerCase().trim()
          })
        );
        await Promise.all(tagPromises);
      }
    }

    const updatedPost = await ForumPost.findByPk(post.id, {
      include: [
        {
          model: User,
          as: 'author',
          attributes: ['id', 'firstName', 'lastName']
        },
        {
          model: PostTag,
          as: 'tags',
          attributes: ['id', 'tagName']
        }
      ]
    });

    const reqLogger = logger.withRequestId(req.id);
    reqLogger.info("Forum post updated", {
      postId: req.params.id,
      authorId: req.user.id
    });

    res.json(updatedPost);
  } catch (error) {
    const reqLogger = logger.withRequestId(req.id);
    reqLogger.error("Forum post update failed", {
      error: error.message,
      stack: error.stack,
      postId: req.params.id,
      authorId: req.user.id
    });
    next(error);
  }
});

// DELETE /api/forum/posts/:id - Delete post
router.delete('/posts/:id', authenticateToken, async (req, res, next) => {
  try {
    const post = await ForumPost.findByPk(req.params.id);

    if (!post) {
      return res.status(404).json({ error: 'Post not found' });
    }

    if (post.authorId !== req.user.id) {
      return res.status(403).json({ error: 'Unauthorized' });
    }

    // Delete associated tags and comments
    await PostTag.destroy({ where: { postId: post.id } });
    await ForumComment.destroy({ where: { postId: post.id } });
    await post.destroy();

    const reqLogger = logger.withRequestId(req.id);
    reqLogger.info("Forum post deleted", {
      postId: req.params.id,
      authorId: req.user.id
    });

    res.status(204).send();
  } catch (error) {
    const reqLogger = logger.withRequestId(req.id);
    reqLogger.error("Forum post deletion failed", {
      error: error.message,
      stack: error.stack,
      postId: req.params.id,
      authorId: req.user.id
    });
    next(error);
  }
});

// PATCH /api/forum/posts/:id/status - Update post status
router.patch('/posts/:id/status', authenticateToken, async (req, res, next) => {
  try {
    const { status } = req.body;
    const post = await ForumPost.findByPk(req.params.id);

    if (!post) {
      return res.status(404).json({ error: 'Post not found' });
    }

    if (post.authorId !== req.user.id) {
      return res.status(403).json({ error: 'Only the author can update post status' });
    }

    if (!['open', 'answered', 'closed'].includes(status)) {
      return res.status(400).json({ error: 'Invalid status value' });
    }

    // Set closedBy and closedAt when closing, clear them when reopening
    const updateData = {
      status,
      closedBy: status === 'closed' ? req.user.id : null,
      closedAt: status === 'closed' ? new Date() : null
    };

    await post.update(updateData);

    // Send email notifications when closing (not when reopening)
    if (status === 'closed') {
      (async () => {
        try {
          const closerUser = await User.findByPk(req.user.id, {
            attributes: ['id', 'firstName', 'lastName', 'email']
          });

          const postWithAuthor = await ForumPost.findByPk(post.id, {
            include: [
              {
                model: User,
                as: 'author',
                attributes: ['id', 'firstName', 'lastName', 'email']
              }
            ]
          });

          // Get all unique participants (author + commenters)
          const comments = await ForumComment.findAll({
            where: {
              postId: post.id,
              isDeleted: false
            },
            include: [
              {
                model: User,
                as: 'author',
                attributes: ['id', 'email', 'firstName', 'lastName']
              }
            ]
          });

          // Collect unique recipients (excluding the closer)
          const recipientMap = new Map();

          // Add post author if not the closer
          if (postWithAuthor.author && postWithAuthor.author.id !== req.user.id) {
            recipientMap.set(postWithAuthor.author.id, postWithAuthor.author);
          }

          // Add all comment authors (excluding closer)
          comments.forEach(comment => {
            if (comment.author && comment.author.id !== req.user.id && !recipientMap.has(comment.author.id)) {
              recipientMap.set(comment.author.id, comment.author);
            }
          });

          // Send emails to all recipients
          const recipients = Array.from(recipientMap.values());
          const emailPromises = recipients.map(recipient =>
            emailServices.forum.sendForumPostClosedNotification(
              recipient,
              closerUser,
              postWithAuthor,
              updateData.closedAt
            )
          );

          await Promise.allSettled(emailPromises);

          const reqLogger = logger.withRequestId(req.id);
          reqLogger.info("Post closure notification emails sent", {
            postId: post.id,
            recipientCount: recipients.length,
            closedBy: req.user.id
          });
        } catch (emailError) {
          // Email errors don't block the closure
          const reqLogger = logger.withRequestId(req.id);
          reqLogger.error("Failed to send post closure notification emails", {
            error: emailError.message,
            stack: emailError.stack,
            postId: req.params.id
          });
          logger.error("Email notification error", { error: emailError });
        }
      })();
    }

    const updatedPost = await ForumPost.findByPk(post.id, {
      include: [
        {
          model: User,
          as: 'author',
          attributes: ['id', 'firstName', 'lastName']
        },
        {
          model: User,
          as: 'closer',
          attributes: ['id', 'firstName', 'lastName'],
          required: false
        },
        {
          model: PostTag,
          as: 'tags',
          attributes: ['id', 'tagName']
        }
      ]
    });

    const reqLogger = logger.withRequestId(req.id);
    reqLogger.info("Forum post status updated", {
      postId: req.params.id,
      newStatus: status,
      authorId: req.user.id
    });

    res.json(updatedPost);
  } catch (error) {
    const reqLogger = logger.withRequestId(req.id);
    reqLogger.error("Forum post status update failed", {
      error: error.message,
      stack: error.stack,
      postId: req.params.id,
      authorId: req.user.id
    });
    next(error);
  }
});

// PATCH /api/forum/posts/:id/accept-answer - Mark/unmark comment as accepted answer
router.patch('/posts/:id/accept-answer', authenticateToken, async (req, res, next) => {
  try {
    const { commentId } = req.body;
    const post = await ForumPost.findByPk(req.params.id);

    if (!post) {
      return res.status(404).json({ error: 'Post not found' });
    }

    if (post.authorId !== req.user.id) {
      return res.status(403).json({ error: 'Only the post author can mark answers' });
    }

    // If commentId is provided, validate it
    if (commentId) {
      const comment = await ForumComment.findByPk(commentId);

      if (!comment) {
        return res.status(404).json({ error: 'Comment not found' });
      }

      if (comment.postId !== post.id) {
        return res.status(400).json({ error: 'Comment does not belong to this post' });
      }

      if (comment.isDeleted) {
        return res.status(400).json({ error: 'Cannot mark deleted comment as answer' });
      }

      if (comment.parentCommentId) {
        return res.status(400).json({ error: 'Only top-level comments can be marked as answers' });
      }
    }

    // Update the post with accepted answer and close the discussion
    await post.update({
      acceptedAnswerId: commentId || null,
      status: commentId ? 'closed' : 'open',
      closedBy: commentId ? req.user.id : null,
      closedAt: commentId ? new Date() : null
    });

    // Send email notifications if marking an answer (not unmarking)
    if (commentId) {
      (async () => {
        try {
          const comment = await ForumComment.findByPk(commentId, {
            include: [
              {
                model: User,
                as: 'author',
                attributes: ['id', 'firstName', 'lastName', 'email']
              }
            ]
          });

          const postAuthor = await User.findByPk(req.user.id, {
            attributes: ['id', 'firstName', 'lastName', 'email']
          });

          const postWithAuthor = await ForumPost.findByPk(post.id, {
            include: [
              {
                model: User,
                as: 'author',
                attributes: ['id', 'firstName', 'lastName', 'email']
              }
            ]
          });

          // Send answer accepted email if not marking your own comment as answer
          if (comment && comment.authorId !== req.user.id) {
            await emailServices.forum.sendForumAnswerAcceptedNotification(
              comment.author,
              postAuthor,
              post,
              comment
            );
          }

          // Send post closure notifications to all participants
          // Get all unique participants (author + commenters)
          const comments = await ForumComment.findAll({
            where: {
              postId: post.id,
              isDeleted: false
            },
            include: [
              {
                model: User,
                as: 'author',
                attributes: ['id', 'email', 'firstName', 'lastName']
              }
            ]
          });

          // Collect unique recipients (excluding the post author who marked the answer)
          const recipientMap = new Map();

          // Don't send to post author since they closed it
          // Add all comment authors (excluding post author)
          comments.forEach(c => {
            if (c.author && c.author.id !== req.user.id && !recipientMap.has(c.author.id)) {
              recipientMap.set(c.author.id, c.author);
            }
          });

          // Send closure notification emails to all recipients
          const recipients = Array.from(recipientMap.values());
          const closureEmailPromises = recipients.map(recipient =>
            emailServices.forum.sendForumPostClosedNotification(
              recipient,
              postAuthor,
              postWithAuthor,
              new Date()
            )
          );

          await Promise.allSettled(closureEmailPromises);

          logger.info("Answer marked and closure notifications sent", {
            postId: post.id,
            commentId: commentId,
            closureNotificationCount: recipients.length
          });
        } catch (emailError) {
          // Email errors don't block answer marking
          logger.error("Failed to send notification emails", {
            error: emailError.message,
            stack: emailError.stack,
            commentId: commentId,
            postId: req.params.id
          });
          logger.error("Email notification error", { error: emailError });
        }
      })();
    }

    const updatedPost = await ForumPost.findByPk(post.id, {
      include: [
        {
          model: User,
          as: 'author',
          attributes: ['id', 'firstName', 'lastName']
        },
        {
          model: PostTag,
          as: 'tags',
          attributes: ['id', 'tagName']
        }
      ]
    });

    const reqLogger = logger.withRequestId(req.id);
    reqLogger.info("Answer marked/unmarked", {
      postId: req.params.id,
      commentId: commentId || 'unmarked',
      authorId: req.user.id
    });

    res.json(updatedPost);
  } catch (error) {
    const reqLogger = logger.withRequestId(req.id);
    reqLogger.error("Mark answer failed", {
      error: error.message,
      stack: error.stack,
      postId: req.params.id,
      authorId: req.user.id
    });
    next(error);
  }
});

// POST /api/forum/posts/:id/comments - Add comment/reply
router.post('/posts/:id/comments', authenticateToken, async (req, res, next) => {
  try {
    // Require email verification
    if (!req.user.isVerified) {
      return res.status(403).json({
        error: "Please verify your email address before commenting.",
        code: "EMAIL_NOT_VERIFIED"
      });
    }

    // Support both parentId (new) and parentCommentId (legacy) for backwards compatibility
    const { content, parentId, parentCommentId, imageFilenames: rawImageFilenames } = req.body;
    const parentIdResolved = parentId || parentCommentId;
    const post = await ForumPost.findByPk(req.params.id);

    if (!post) {
      return res.status(404).json({ error: 'Post not found' });
    }

    // Prevent comments on closed posts
    if (post.status === 'closed') {
      return res.status(403).json({ error: 'Cannot comment on a closed discussion' });
    }

    // Validate parent comment if provided
    if (parentIdResolved) {
      const parentComment = await ForumComment.findByPk(parentIdResolved);
      if (!parentComment || parentComment.postId !== post.id) {
        return res.status(400).json({ error: 'Invalid parent comment' });
      }
    }

    // Ensure imageFilenames is an array and validate S3 keys
    const imageFilenamesArray = Array.isArray(rawImageFilenames) ? rawImageFilenames : [];

    const keyValidation = validateS3Keys(imageFilenamesArray, 'forum', { maxKeys: IMAGE_LIMITS.forum });
    if (!keyValidation.valid) {
      return res.status(400).json({
        error: keyValidation.error,
        details: keyValidation.invalidKeys
      });
    }

    const imageFilenames = imageFilenamesArray;

    const comment = await ForumComment.create({
      postId: req.params.id,
      authorId: req.user.id,
      content,
      parentCommentId: parentIdResolved || null,
      imageFilenames
    });

    // Increment comment count and update post's updatedAt to reflect activity
    await post.increment('commentCount');
    await post.update({ updatedAt: new Date() });

    const commentWithDetails = await ForumComment.findByPk(comment.id, {
      include: [
        {
          model: User,
          as: 'author',
          attributes: ['id', 'firstName', 'lastName', 'email']
        }
      ]
    });

    // Send email notifications (non-blocking)
    (async () => {
      try {
        const commenter = commentWithDetails.author;
        const notifiedUserIds = new Set();

        // Reload post with author details for email
        const postWithAuthor = await ForumPost.findByPk(req.params.id, {
          include: [
            {
              model: User,
              as: 'author',
              attributes: ['id', 'firstName', 'lastName', 'email']
            }
          ]
        });

        // If this is a reply, send reply notification to parent comment author
        if (parentCommentId) {
          const parentComment = await ForumComment.findByPk(parentCommentId, {
            include: [
              {
                model: User,
                as: 'author',
                attributes: ['id', 'firstName', 'lastName', 'email']
              }
            ]
          });

          // Send reply notification if not replying to yourself
          if (parentComment && parentComment.authorId !== req.user.id) {
            await emailServices.forum.sendForumReplyNotification(
              parentComment.author,
              commenter,
              postWithAuthor,
              commentWithDetails,
              parentComment
            );
            notifiedUserIds.add(parentComment.authorId);
          }
        } else {
          // Send comment notification to post author if not commenting on your own post
          if (postWithAuthor.authorId !== req.user.id) {
            await emailServices.forum.sendForumCommentNotification(
              postWithAuthor.author,
              commenter,
              postWithAuthor,
              commentWithDetails
            );
            notifiedUserIds.add(postWithAuthor.authorId);
          }
        }

        // Get all unique participants who have commented on this post (excluding commenter and already notified)
        const participants = await ForumComment.findAll({
          where: {
            postId: req.params.id,
            authorId: {
              [Op.notIn]: [req.user.id, ...Array.from(notifiedUserIds)]
            },
            isDeleted: false
          },
          attributes: ['authorId'],
          include: [
            {
              model: User,
              as: 'author',
              attributes: ['id', 'firstName', 'lastName', 'email']
            }
          ],
          group: ['ForumComment.authorId', 'author.id']
        });

        // Send thread activity notifications to all unique participants
        for (const participant of participants) {
          if (participant.author) {
            await emailServices.forum.sendForumThreadActivityNotification(
              participant.author,
              commenter,
              postWithAuthor,
              commentWithDetails
            );
          }
        }
      } catch (emailError) {
        // Email errors don't block comment creation
        logger.error("Failed to send forum comment notification emails", {
          error: emailError.message,
          stack: emailError.stack,
          commentId: comment.id,
          postId: req.params.id
        });
        logger.error("Email notification error", { error: emailError });
      }
    })();

    const reqLogger = logger.withRequestId(req.id);
    reqLogger.info("Forum comment created", {
      postId: req.params.id,
      commentId: comment.id,
      authorId: req.user.id,
      isReply: !!parentCommentId
    });

    res.status(201).json(commentWithDetails);
  } catch (error) {
    const reqLogger = logger.withRequestId(req.id);
    reqLogger.error("Forum comment creation failed", {
      error: error.message,
      stack: error.stack,
      postId: req.params.id,
      authorId: req.user.id
    });
    next(error);
  }
});

// PUT /api/forum/comments/:id - Edit comment
router.put('/comments/:id', authenticateToken, async (req, res, next) => {
  try {
    // Require email verification
    if (!req.user.isVerified) {
      return res.status(403).json({
        error: "Please verify your email address before editing comments.",
        code: "EMAIL_NOT_VERIFIED"
      });
    }

    const { content, imageFilenames: rawImageFilenames } = req.body;
    const comment = await ForumComment.findByPk(req.params.id);

    if (!comment) {
      return res.status(404).json({ error: 'Comment not found' });
    }

    if (comment.authorId !== req.user.id) {
      return res.status(403).json({ error: 'Unauthorized' });
    }

    if (comment.isDeleted) {
      return res.status(400).json({ error: 'Cannot edit deleted comment' });
    }

    const updateData = { content };

    // Handle image filenames if provided
    if (rawImageFilenames !== undefined) {
      const imageFilenamesArray = Array.isArray(rawImageFilenames) ? rawImageFilenames : [];
      const keyValidation = validateS3Keys(imageFilenamesArray, 'forum', { maxKeys: IMAGE_LIMITS.forum });
      if (!keyValidation.valid) {
        return res.status(400).json({ error: keyValidation.error });
      }
      updateData.imageFilenames = imageFilenamesArray;
    }

    await comment.update(updateData);

    const updatedComment = await ForumComment.findByPk(comment.id, {
      include: [
        {
          model: User,
          as: 'author',
          attributes: ['id', 'firstName', 'lastName']
        }
      ]
    });

    const reqLogger = logger.withRequestId(req.id);
    reqLogger.info("Forum comment updated", {
      commentId: req.params.id,
      authorId: req.user.id
    });

    res.json(updatedComment);
  } catch (error) {
    const reqLogger = logger.withRequestId(req.id);
    reqLogger.error("Forum comment update failed", {
      error: error.message,
      stack: error.stack,
      commentId: req.params.id,
      authorId: req.user.id
    });
    next(error);
  }
});

// DELETE /api/forum/comments/:id - Soft delete comment
router.delete('/comments/:id', authenticateToken, async (req, res, next) => {
  try {
    const comment = await ForumComment.findByPk(req.params.id);

    if (!comment) {
      return res.status(404).json({ error: 'Comment not found' });
    }

    if (comment.authorId !== req.user.id) {
      return res.status(403).json({ error: 'Unauthorized' });
    }

    // Soft delete (preserve content for potential restoration)
    await comment.update({ isDeleted: true });

    // Decrement comment count
    const post = await ForumPost.findByPk(comment.postId);
    if (post && post.commentCount > 0) {
      await post.decrement('commentCount');
    }

    const reqLogger = logger.withRequestId(req.id);
    reqLogger.info("Forum comment deleted", {
      commentId: req.params.id,
      authorId: req.user.id,
      postId: comment.postId
    });

    res.status(204).send();
  } catch (error) {
    const reqLogger = logger.withRequestId(req.id);
    reqLogger.error("Forum comment deletion failed", {
      error: error.message,
      stack: error.stack,
      commentId: req.params.id,
      authorId: req.user.id
    });
    next(error);
  }
});

// GET /api/forum/my-posts - Get user's posts
router.get('/my-posts', authenticateToken, async (req, res, next) => {
  try {
    const posts = await ForumPost.findAll({
      where: { authorId: req.user.id },
      include: [
        {
          model: User,
          as: 'author',
          attributes: ['id', 'firstName', 'lastName']
        },
        {
          model: PostTag,
          as: 'tags',
          attributes: ['id', 'tagName']
        }
      ],
      order: [['createdAt', 'DESC']]
    });

    const reqLogger = logger.withRequestId(req.id);
    reqLogger.info("User forum posts fetched", {
      userId: req.user.id,
      postsCount: posts.length
    });

    res.json(posts);
  } catch (error) {
    const reqLogger = logger.withRequestId(req.id);
    reqLogger.error("User forum posts fetch failed", {
      error: error.message,
      stack: error.stack,
      userId: req.user.id
    });
    next(error);
  }
});

// GET /api/forum/tags - Get all unique tags for autocomplete
router.get('/tags', async (req, res, next) => {
  try {
    const { search } = req.query;

    const where = {};
    if (search) {
      where.tagName = { [Op.iLike]: `%${search}%` };
    }

    const tags = await PostTag.findAll({
      where,
      attributes: [
        'tagName',
        [require('sequelize').fn('COUNT', require('sequelize').col('tagName')), 'count']
      ],
      group: ['tagName'],
      order: [[require('sequelize').fn('COUNT', require('sequelize').col('tagName')), 'DESC']],
      limit: 50
    });

    const reqLogger = logger.withRequestId(req.id);
    reqLogger.info("Tags fetched", {
      search,
      tagsCount: tags.length
    });

    res.json(tags);
  } catch (error) {
    const reqLogger = logger.withRequestId(req.id);
    reqLogger.error("Tags fetch failed", {
      error: error.message,
      stack: error.stack,
      query: req.query
    });
    next(error);
  }
});

// ============ ADMIN ROUTES ============

// DELETE /api/forum/admin/posts/:id - Admin soft-delete post
router.delete('/admin/posts/:id', authenticateToken, requireAdmin, async (req, res, next) => {
  try {
    const { reason } = req.body;

    if (!reason || !reason.trim()) {
      return res.status(400).json({ error: "Deletion reason is required" });
    }

    const post = await ForumPost.findByPk(req.params.id, {
      include: [
        {
          model: User,
          as: 'author',
          attributes: ['id', 'firstName', 'lastName', 'email']
        }
      ]
    });

    if (!post) {
      return res.status(404).json({ error: 'Post not found' });
    }

    if (post.isDeleted) {
      return res.status(400).json({ error: 'Post is already deleted' });
    }

    // Soft delete the post
    await post.update({
      isDeleted: true,
      deletedBy: req.user.id,
      deletedAt: new Date(),
      deletionReason: reason.trim()
    });

    const reqLogger = logger.withRequestId(req.id);
    reqLogger.info("Admin deleted post", {
      postId: req.params.id,
      adminId: req.user.id,
      originalAuthorId: post.authorId,
      reason: reason.trim()
    });

    // Send email notification to post author (non-blocking)
    (async () => {
      try {
        const admin = await User.findByPk(req.user.id, {
          attributes: ['id', 'firstName', 'lastName']
        });

        if (post.author && admin) {
          await emailServices.forum.sendForumPostDeletionNotification(
            post.author,
            admin,
            post,
            reason.trim()
          );
          console.log(`Forum post deletion notification email sent to author ${post.authorId}`);
        }
      } catch (emailError) {
        // Log but don't fail the deletion
        logger.error('Failed to send forum post deletion notification email', {
          error: emailError.message,
          stack: emailError.stack,
          postId: post.id,
          authorId: post.authorId
        });
      }
    })();

    res.status(200).json({ message: 'Post deleted successfully', post });
  } catch (error) {
    const reqLogger = logger.withRequestId(req.id);
    reqLogger.error("Admin post deletion failed", {
      error: error.message,
      stack: error.stack,
      postId: req.params.id,
      adminId: req.user.id
    });
    next(error);
  }
});

// PATCH /api/forum/admin/posts/:id/restore - Admin restore deleted post
router.patch('/admin/posts/:id/restore', authenticateToken, requireAdmin, async (req, res, next) => {
  try {
    const post = await ForumPost.findByPk(req.params.id);

    if (!post) {
      return res.status(404).json({ error: 'Post not found' });
    }

    if (!post.isDeleted) {
      return res.status(400).json({ error: 'Post is not deleted' });
    }

    // Restore the post
    await post.update({
      isDeleted: false,
      deletedBy: null,
      deletedAt: null,
      deletionReason: null
    });

    const reqLogger = logger.withRequestId(req.id);
    reqLogger.info("Admin restored post", {
      postId: req.params.id,
      adminId: req.user.id,
      originalAuthorId: post.authorId
    });

    res.status(200).json({ message: 'Post restored successfully', post });
  } catch (error) {
    const reqLogger = logger.withRequestId(req.id);
    reqLogger.error("Admin post restoration failed", {
      error: error.message,
      stack: error.stack,
      postId: req.params.id,
      adminId: req.user.id
    });
    next(error);
  }
});

// DELETE /api/forum/admin/comments/:id - Admin soft-delete comment
router.delete('/admin/comments/:id', authenticateToken, requireAdmin, async (req, res, next) => {
  try {
    const { reason } = req.body;

    if (!reason || !reason.trim()) {
      return res.status(400).json({ error: "Deletion reason is required" });
    }

    const comment = await ForumComment.findByPk(req.params.id, {
      include: [
        {
          model: User,
          as: 'author',
          attributes: ['id', 'firstName', 'lastName', 'email']
        }
      ]
    });

    if (!comment) {
      return res.status(404).json({ error: 'Comment not found' });
    }

    if (comment.isDeleted) {
      return res.status(400).json({ error: 'Comment is already deleted' });
    }

    // Soft delete the comment (preserve original content for potential restoration)
    await comment.update({
      isDeleted: true,
      deletedBy: req.user.id,
      deletedAt: new Date(),
      deletionReason: reason.trim()
    });

    // Decrement comment count
    const post = await ForumPost.findByPk(comment.postId, {
      attributes: ['id', 'title']
    });
    if (post && post.commentCount > 0) {
      await post.decrement('commentCount');
    }

    const reqLogger = logger.withRequestId(req.id);
    reqLogger.info("Admin deleted comment", {
      commentId: req.params.id,
      adminId: req.user.id,
      originalAuthorId: comment.authorId,
      postId: comment.postId,
      reason: reason.trim()
    });

    // Send email notification to comment author (non-blocking)
    (async () => {
      try {
        const admin = await User.findByPk(req.user.id, {
          attributes: ['id', 'firstName', 'lastName']
        });

        if (comment.author && admin && post) {
          await emailServices.forum.sendForumCommentDeletionNotification(
            comment.author,
            admin,
            post,
            reason.trim()
          );
          console.log(`Forum comment deletion notification email sent to author ${comment.authorId}`);
        }
      } catch (emailError) {
        // Log but don't fail the deletion
        logger.error('Failed to send forum comment deletion notification email', {
          error: emailError.message,
          stack: emailError.stack,
          commentId: comment.id,
          authorId: comment.authorId
        });
      }
    })();

    res.status(200).json({ message: 'Comment deleted successfully', comment });
  } catch (error) {
    const reqLogger = logger.withRequestId(req.id);
    reqLogger.error("Admin comment deletion failed", {
      error: error.message,
      stack: error.stack,
      commentId: req.params.id,
      adminId: req.user.id
    });
    next(error);
  }
});

// PATCH /api/forum/admin/comments/:id/restore - Admin restore deleted comment
router.patch('/admin/comments/:id/restore', authenticateToken, requireAdmin, async (req, res, next) => {
  try {
    const comment = await ForumComment.findByPk(req.params.id);

    if (!comment) {
      return res.status(404).json({ error: 'Comment not found' });
    }

    if (!comment.isDeleted) {
      return res.status(400).json({ error: 'Comment is not deleted' });
    }

    // Restore the comment with its original content
    await comment.update({
      isDeleted: false,
      deletedBy: null,
      deletedAt: null,
      deletionReason: null
    });

    // Increment comment count
    const post = await ForumPost.findByPk(comment.postId);
    if (post) {
      await post.increment('commentCount');
    }

    const reqLogger = logger.withRequestId(req.id);
    reqLogger.info("Admin restored comment", {
      commentId: req.params.id,
      adminId: req.user.id,
      originalAuthorId: comment.authorId,
      postId: comment.postId
    });

    res.status(200).json({
      message: 'Comment restored successfully',
      comment
    });
  } catch (error) {
    const reqLogger = logger.withRequestId(req.id);
    reqLogger.error("Admin comment restoration failed", {
      error: error.message,
      stack: error.stack,
      commentId: req.params.id,
      adminId: req.user.id
    });
    next(error);
  }
});

// PATCH /api/forum/admin/posts/:id/close - Admin close discussion
router.patch('/admin/posts/:id/close', authenticateToken, requireAdmin, async (req, res, next) => {
  try {
    const post = await ForumPost.findByPk(req.params.id, {
      include: [
        {
          model: User,
          as: 'author',
          attributes: ['id', 'firstName', 'lastName', 'email']
        }
      ]
    });

    if (!post) {
      return res.status(404).json({ error: 'Post not found' });
    }

    if (post.status === 'closed') {
      return res.status(400).json({ error: 'Post is already closed' });
    }

    const closedAt = new Date();

    // Close the post
    await post.update({
      status: 'closed',
      closedBy: req.user.id,
      closedAt: closedAt
    });

    const reqLogger = logger.withRequestId(req.id);
    reqLogger.info("Admin closed post", {
      postId: req.params.id,
      adminId: req.user.id,
      originalAuthorId: post.authorId
    });

    // Send email notifications asynchronously
    (async () => {
      try {
        const admin = await User.findByPk(req.user.id, {
          attributes: ['id', 'firstName', 'lastName', 'email']
        });

        // Get all unique participants (author + commenters)
        const comments = await ForumComment.findAll({
          where: {
            postId: post.id,
            isDeleted: false
          },
          include: [
            {
              model: User,
              as: 'author',
              attributes: ['id', 'email', 'firstName', 'lastName']
            }
          ]
        });

        // Collect unique recipients (author + all comment authors)
        const recipientMap = new Map();

        // Add post author if not the admin
        if (post.author && post.author.id !== req.user.id) {
          recipientMap.set(post.author.id, post.author);
        }

        // Add all comment authors (excluding admin)
        comments.forEach(comment => {
          if (comment.author && comment.author.id !== req.user.id && !recipientMap.has(comment.author.id)) {
            recipientMap.set(comment.author.id, comment.author);
          }
        });

        // Send emails to all recipients
        const recipients = Array.from(recipientMap.values());
        const emailPromises = recipients.map(recipient =>
          emailServices.forum.sendForumPostClosedNotification(
            recipient,
            admin,
            post,
            closedAt
          )
        );

        await Promise.allSettled(emailPromises);

        reqLogger.info("Post closure notification emails sent", {
          postId: post.id,
          recipientCount: recipients.length
        });
      } catch (emailError) {
        // Email errors don't block the closure
        reqLogger.error("Failed to send post closure notification emails", {
          error: emailError.message,
          stack: emailError.stack,
          postId: req.params.id
        });
        logger.error("Email notification error", { error: emailError });
      }
    })();

    res.status(200).json({ message: 'Post closed successfully', post });
  } catch (error) {
    const reqLogger = logger.withRequestId(req.id);
    reqLogger.error("Admin post closure failed", {
      error: error.message,
      stack: error.stack,
      postId: req.params.id,
      adminId: req.user.id
    });
    next(error);
  }
});

// PATCH /api/forum/admin/posts/:id/reopen - Admin reopen discussion
router.patch('/admin/posts/:id/reopen', authenticateToken, requireAdmin, async (req, res, next) => {
  try {
    const post = await ForumPost.findByPk(req.params.id);

    if (!post) {
      return res.status(404).json({ error: 'Post not found' });
    }

    if (post.status !== 'closed') {
      return res.status(400).json({ error: 'Post is not closed' });
    }

    // Reopen the post
    await post.update({
      status: 'open',
      closedBy: null,
      closedAt: null
    });

    const reqLogger = logger.withRequestId(req.id);
    reqLogger.info("Admin reopened post", {
      postId: req.params.id,
      adminId: req.user.id,
      originalAuthorId: post.authorId
    });

    res.status(200).json({ message: 'Post reopened successfully', post });
  } catch (error) {
    const reqLogger = logger.withRequestId(req.id);
    reqLogger.error("Admin post reopen failed", {
      error: error.message,
      stack: error.stack,
      postId: req.params.id,
      adminId: req.user.id
    });
    next(error);
  }
});

module.exports = router;
