import { Request, Response } from 'express';
import RewardCatalog from '../models/rewardCatalog';
import RewardRedemption from '../models/rewardRedemption';
import User from '../models/users';
import { sendResponse, handleError } from '../utils/apiResponse';

// @desc    List all available rewards (active only)
// @route   GET /api/rewards/store
export const listAvailableRewards = async (req: Request, res: Response): Promise<void> => {
  try {
    const userId = req.user?.id;
    const page = parseInt(req.query.page as string) || 1;
    const limit = parseInt(req.query.limit as string) || 12;
    const skip = (page - 1) * limit;
    let available_points = null;
    let rewards = [];
    let total = 0;
    let filter: any = { is_active: true };
    if (userId) {
      const user = await User.findById(userId).select('redeemed_points');
      const quiz_points = await (await import('../models/quizResult')).default.aggregate([
        { $match: { user: user?._id } },
        { $group: { _id: null, total: { $sum: "$score" } } },
      ]).then((r) => r[0]?.total || 0);
      const redeemed_points = user?.redeemed_points || 0;
      available_points = quiz_points - redeemed_points;
      filter.points_required = { $lte: available_points };
    }
    total = await RewardCatalog.countDocuments(filter);
    rewards = await RewardCatalog.find(filter)
      .sort({ points_required: 1 })
      .skip(skip)
      .limit(limit)
      .lean();

    // Add requested: true/false for each reward if userId is present
    if (userId) {
      const rewardIds = rewards.map((r: any) => r._id);
      const userRequests = await RewardRedemption.find({ user: userId, reward: { $in: rewardIds } }).select('reward');
      const requestedSet = new Set(userRequests.map((r) => r.reward.toString()));
      rewards = rewards.map((reward: any) => ({
        ...reward,
        requested: requestedSet.has(reward._id.toString()),
      }));
    } else {
      rewards = rewards.map((reward: any) => ({ ...reward, requested: false }));
    }
    const pagination = {
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
      hasNextPage: page < Math.ceil(total / limit),
      hasPrevPage: page > 1,
    };
    sendResponse(res, 200, 'Available rewards fetched', { rewards, available_points }, pagination);
  } catch (error) {
    handleError(res, error);
  }
};

// @desc    Redeem a reward
// @route   POST /api/rewards/redeem/:rewardId
export const redeemReward = async (req: Request, res: Response): Promise<void> => {
  try {
    const userId = req.user!.id;
    const { rewardId } = req.params;
    const reward = await RewardCatalog.findById(rewardId);
    if (!reward || !reward.is_active) return sendResponse(res, 404, 'Reward not found or inactive');

    const user = await User.findById(userId);
    if (!user) return sendResponse(res, 404, 'User not found');

    // Check if user has enough points
    if (user.total_points < reward.points_required) {
      return sendResponse(res, 400, 'Not enough points to redeem this reward');
    }

    // Check quantity (if limited)
    if (typeof reward.quantity === 'number' && reward.quantity <= 0) {
      return sendResponse(res, 400, 'Reward is out of stock');
    }

    // Prevent duplicate redemption requests for the same reward by the same user


    // Create redemption request (pending approval/fulfillment)
    let redemption;
    try {
      redemption = await RewardRedemption.create({
        user: user._id,
        reward: reward._id,
        status: 'pending',
        requestedAt: new Date(),
      });
    } catch (err) {
      // Handle duplicate key error (E11000)
      if (err && (err as any).code === 11000) {
        return sendResponse(res, 400, 'You have already requested this reward');
      }
      throw err;
    }
    sendResponse(res, 201, 'Reward redemption requested', { redemption });
  } catch (error) {
    handleError(res, error);
  }
};
