/**
 * Feature Request Routes
 *
 * User-facing routes for submitting and managing feature requests.
 * Users can create, view their own requests, edit (if pending), and withdraw (if pending).
 */

import express, { Request, Response } from 'express';
import { body, validationResult } from 'express-validator';
import { getModels } from '../models';
import { authenticate } from '../middleware/auth';
import { notifyFeatureRequestActivity } from '../services/featureRequestNotifications';

const router = express.Router();

router.use(authenticate);

// ============================================
// GET /:companyId — Get user's own feature requests
// ============================================

router.get('/:companyId', async (req: Request, res: Response) => {
  try {
    const { companyId } = req.params;
    const { FeatureRequest } = getModels();

    // Verify user has access to this company
    if (!req.user!.companyIds.includes(companyId) && req.user!.role !== 'super-admin') {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    // Get all requests for this company (user sees all company requests)
    // Or only their own? Based on requirements: "User sees their submitted requests"
    // Let's show user's own requests only
    const requests = await FeatureRequest.find({
      companyId,
      userId: req.user!._id.toString()
    }).sort({ createdAt: -1 });

    res.json(requests);
  } catch (error: any) {
    console.error('[FeatureRequest] Get error:', error.message);
    res.status(500).json({ error: 'Failed to get feature requests' });
  }
});

// ============================================
// GET /detail/:id — Get single feature request
// ============================================

router.get('/detail/:id', async (req: Request, res: Response) => {
  try {
    const { id } = req.params;
    const { FeatureRequest } = getModels();

    const request = await FeatureRequest.findById(id);
    if (!request) {
      res.status(404).json({ error: 'Feature request not found' });
      return;
    }

    // Verify user owns this request or is super-admin
    if (request.userId !== req.user!._id.toString() && req.user!.role !== 'super-admin') {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    res.json(request);
  } catch (error: any) {
    console.error('[FeatureRequest] Get detail error:', error.message);
    res.status(500).json({ error: 'Failed to get feature request' });
  }
});

// ============================================
// POST / — Create new feature request
// ============================================

router.post(
  '/',
  [
    body('title').trim().notEmpty().withMessage('Title is required').isLength({ max: 200 }).withMessage('Title cannot exceed 200 characters'),
    body('description').trim().notEmpty().withMessage('Description is required').isLength({ max: 5000 }).withMessage('Description cannot exceed 5000 characters'),
    body('companyId').notEmpty().withMessage('Company ID is required'),
    body('category').optional().isIn(['UI', 'AI', 'Integration', 'Other']).withMessage('Invalid category'),
    body('priority').optional().isIn(['Low', 'Medium', 'High']).withMessage('Invalid priority'),
  ],
  async (req: Request, res: Response) => {
    try {
      const errors = validationResult(req);
      if (!errors.isEmpty()) {
        res.status(400).json({ errors: errors.array() });
        return;
      }

      const { title, description, companyId, category, priority } = req.body;

      // Verify user has access to this company
      if (!req.user!.companyIds.includes(companyId) && req.user!.role !== 'super-admin') {
        res.status(403).json({ error: 'Access denied' });
        return;
      }

      const { FeatureRequest } = getModels();

      const request = new FeatureRequest({
        title,
        description,
        companyId,
        userId: req.user!._id.toString(),
        category: category || 'Other',
        priority: priority || 'Medium',
        status: 'Pending'
      });

      await request.save();

      // Notify configured recipients about the new feature request
      notifyFeatureRequestActivity({
        activityType: 'created',
        featureRequestId: (request as any)._id.toString(),
        featureRequestTitle: request.title,
        triggeredByUserId: req.user!._id.toString(),
        triggeredByUserName: (req.user as any).name || 'Unknown',
        triggeredByUserRole: req.user!.role || 'user',
      }).catch(() => { /* fire-and-forget */ });

      res.status(201).json(request);
    } catch (error: any) {
      console.error('[FeatureRequest] Create error:', error.message);
      if (error?.name === 'ValidationError') {
        const messages = Object.values(error.errors).map((e: any) => e.message);
        res.status(400).json({ error: messages.join('. ') });
        return;
      }
      res.status(500).json({ error: 'Failed to create feature request' });
    }
  }
);

// ============================================
// PUT /:id — Update feature request (only if Pending)
// ============================================

router.put('/:id', async (req: Request, res: Response) => {
  try {
    const { id } = req.params;
    const { FeatureRequest } = getModels();

    const request = await FeatureRequest.findById(id);
    if (!request) {
      res.status(404).json({ error: 'Feature request not found' });
      return;
    }

    // Verify user owns this request
    if (request.userId !== req.user!._id.toString()) {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    // Only allow updates if status is Pending
    if (request.status !== 'Pending') {
      res.status(400).json({ error: 'Cannot update request that is not pending' });
      return;
    }

    const { title, description, category, priority } = req.body;

    // Update fields
    if (title !== undefined && title !== '') request.title = title;
    if (description !== undefined && description !== '') request.description = description;
    if (category !== undefined) request.category = category;
    if (priority !== undefined) request.priority = priority;

    // A modified request goes back to Unread so the Super Admin re-reviews it.
    request.isRead = false;
    request.updatedAt = new Date() as any;
    await request.save();

    // Notify configured recipients about the edit
    notifyFeatureRequestActivity({
      activityType: 'edited',
      featureRequestId: (request as any)._id.toString(),
      featureRequestTitle: request.title,
      triggeredByUserId: req.user!._id.toString(),
      triggeredByUserName: (req.user as any).name || 'Unknown',
      triggeredByUserRole: req.user!.role || 'user',
    }).catch(() => { /* fire-and-forget */ });

    res.json(request);
  } catch (error: any) {
    console.error('[FeatureRequest] Update error:', error.message);
    if (error?.name === 'ValidationError') {
      const messages = Object.values(error.errors).map((e: any) => e.message);
      res.status(400).json({ error: messages.join('. ') });
      return;
    }
    res.status(500).json({ error: 'Failed to update feature request' });
  }
});

// ============================================
// DELETE /:id — Withdraw feature request (set status to Withdrawn)
// ============================================

router.delete('/:id', async (req: Request, res: Response) => {
  try {
    const { id } = req.params;
    const { FeatureRequest } = getModels();

    const request = await FeatureRequest.findById(id);
    if (!request) {
      res.status(404).json({ error: 'Feature request not found' });
      return;
    }

    // Verify user owns this request
    if (request.userId !== req.user!._id.toString()) {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    // Only allow withdrawal if status is Pending
    if (request.status !== 'Pending') {
      res.status(400).json({ error: 'Cannot withdraw request that is not pending' });
      return;
    }

    // Set status to Withdrawn instead of deleting
    request.status = 'Withdrawn';
    request.updatedAt = new Date() as any;
    await request.save();

    // Notify configured recipients about the withdrawal
    notifyFeatureRequestActivity({
      activityType: 'withdrawn',
      featureRequestId: (request as any)._id.toString(),
      featureRequestTitle: request.title,
      triggeredByUserId: req.user!._id.toString(),
      triggeredByUserName: (req.user as any).name || 'Unknown',
      triggeredByUserRole: req.user!.role || 'user',
    }).catch(() => { /* fire-and-forget */ });

    res.json({ message: 'Feature request withdrawn successfully', request });
  } catch (error: any) {
    console.error('[FeatureRequest] Withdraw error:', error.message);
    res.status(500).json({ error: 'Failed to withdraw feature request' });
  }
});

// ============================================
// DELETE /:id/hard — Hard delete feature request (user's own request)
// ============================================

router.delete('/:id/hard', async (req: Request, res: Response) => {
  try {
    const { id } = req.params;
    const { FeatureRequest } = getModels();

    const request = await FeatureRequest.findById(id);
    if (!request) {
      res.status(404).json({ error: 'Feature request not found' });
      return;
    }

    // Verify user owns this request
    if (request.userId !== req.user!._id.toString()) {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    // Capture title before deletion for notification
    const requestTitle = request.title;
    const requestId = (request as any)._id.toString();

    // Hard delete - actually remove from database
    await FeatureRequest.findByIdAndDelete(id);

    // Notify configured recipients about the deletion
    notifyFeatureRequestActivity({
      activityType: 'deleted',
      featureRequestId: requestId,
      featureRequestTitle: requestTitle,
      triggeredByUserId: req.user!._id.toString(),
      triggeredByUserName: (req.user as any).name || 'Unknown',
      triggeredByUserRole: req.user!.role || 'user',
    }).catch(() => { /* fire-and-forget */ });

    res.json({ message: 'Feature request deleted successfully' });
  } catch (error: any) {
    console.error('[FeatureRequest] Hard delete error:', error.message);
    res.status(500).json({ error: 'Failed to delete feature request' });
  }
});

// ============================================
// COMMENTS — threaded discussion on a feature request
// Accessible to the request owner, the company's Admins, and Super Admins.
// ============================================

/** Verify the current user may view/participate in a request's discussion. */
const canAccessRequest = (req: Request, request: any): boolean => {
  const uid = req.user!._id.toString();
  if (request.userId === uid) return true;                                      // request owner
  if (req.user!.role === 'super-admin') return true;                            // super admin sees all
  if (req.user!.role === 'admin' && req.user!.companyIds.includes(request.companyId)) return true; // company admin
  return false;
};

// GET /:id/comments — list all comments (flat, chronological) for a request
router.get('/:id/comments', async (req: Request, res: Response) => {
  try {
    const { id } = req.params;
    const { FeatureRequest, FeatureRequestComment } = getModels();

    const request = await FeatureRequest.findById(id);
    if (!request) {
      res.status(404).json({ error: 'Feature request not found' });
      return;
    }

    if (!canAccessRequest(req, request)) {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    const comments = await FeatureRequestComment
      .find({ featureRequestId: id })
      .sort({ createdAt: 1 });

    res.json(comments);
  } catch (error: any) {
    console.error('[FeatureRequest] List comments error:', error.message);
    res.status(500).json({ error: 'Failed to get comments' });
  }
});

// POST /:id/comments — add a comment or a reply (pass parentId to reply)
router.post('/:id/comments',
  [
    body('content').trim().notEmpty().withMessage('Comment cannot be empty').isLength({ max: 2000 }).withMessage('Comment cannot exceed 2000 characters'),
    body('parentId').optional({ nullable: true }),
  ],
  async (req: Request, res: Response) => {
    try {
      const errors = validationResult(req);
      if (!errors.isEmpty()) {
        res.status(400).json({ errors: errors.array() });
        return;
      }

      const { id } = req.params;
      const { content, parentId } = req.body;
      const { FeatureRequest, FeatureRequestComment } = getModels();

      const request = await FeatureRequest.findById(id);
      if (!request) {
        res.status(404).json({ error: 'Feature request not found' });
        return;
      }

      if (!canAccessRequest(req, request)) {
        res.status(403).json({ error: 'Access denied' });
        return;
      }

      // If replying, the parent must exist and belong to the same request.
      if (parentId) {
        const parent = await FeatureRequestComment.findById(parentId);
        if (!parent || parent.featureRequestId !== id) {
          res.status(400).json({ error: 'Invalid parent comment' });
          return;
        }
      }

      const comment = new FeatureRequestComment({
        featureRequestId: id,
        parentId: parentId || null,
        companyId: request.companyId,
        userId: req.user!._id.toString(),
        userName: (req.user as any).name || 'Unknown',
        userRole: req.user!.role || 'user',
        content,
      });

      await comment.save();

      // Notify configured recipients about the new comment
      notifyFeatureRequestActivity({
        activityType: 'comment_added',
        featureRequestId: id,
        featureRequestTitle: request.title,
        triggeredByUserId: req.user!._id.toString(),
        triggeredByUserName: (req.user as any).name || 'Unknown',
        triggeredByUserRole: req.user!.role || 'user',
        details: {
          commentContent: content,
        },
      }).catch(() => { /* fire-and-forget */ });

      res.status(201).json(comment);
    } catch (error: any) {
      console.error('[FeatureRequest] Create comment error:', error.message);
      if (error?.name === 'ValidationError') {
        const messages = Object.values(error.errors).map((e: any) => e.message);
        res.status(400).json({ error: messages.join('. ') });
        return;
      }
      res.status(500).json({ error: 'Failed to add comment' });
    }
  }
);

// DELETE /comments/:commentId — delete a comment (author or super-admin) and its replies
router.delete('/comments/:commentId', async (req: Request, res: Response) => {
  try {
    const { commentId } = req.params;
    const { FeatureRequestComment } = getModels();

    const comment = await FeatureRequestComment.findById(commentId);
    if (!comment) {
      res.status(404).json({ error: 'Comment not found' });
      return;
    }

    // Only the author or a super-admin can delete a comment.
    if (comment.userId !== req.user!._id.toString() && req.user!.role !== 'super-admin') {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    // Collect the comment and all of its nested descendants, then remove them.
    const all = await FeatureRequestComment.find({ featureRequestId: comment.featureRequestId });
    const toDelete = new Set<string>([commentId]);
    let changed = true;
    while (changed) {
      changed = false;
      for (const c of all as any[]) {
        const cid = (c.id || c._id).toString();
        if (c.parentId && toDelete.has(c.parentId.toString()) && !toDelete.has(cid)) {
          toDelete.add(cid);
          changed = true;
        }
      }
    }

    for (const cid of toDelete) {
      await FeatureRequestComment.findByIdAndDelete(cid);
    }

    res.json({ message: 'Comment deleted successfully', deletedCount: toDelete.size });
  } catch (error: any) {
    console.error('[FeatureRequest] Delete comment error:', error.message);
    res.status(500).json({ error: 'Failed to delete comment' });
  }
});

export default router;