const { MonthlyDraw, ChitGroup, GroupMember, User, Payment } = require('../models');
const { Op } = require('sequelize');
const crypto = require('crypto');

// Generate provably fair lottery result
const generateProvablyFairResult = (serverSeed, clientSeed, nonce, eligibleMembers) => {
  // Create combined seed
  const combinedSeed = `${serverSeed}-${clientSeed}-${nonce}`;
  const hash = crypto.createHash('sha256').update(combinedSeed).digest('hex');
  
  // Use first 8 characters of hash as random number
  const randomHex = hash.substring(0, 8);
  const randomNumber = parseInt(randomHex, 16);
  
  // Select winner from eligible members
  const winnerIndex = randomNumber % eligibleMembers.length;
  const winner = eligibleMembers[winnerIndex];
  
  return {
    winner,
    hash,
    randomNumber,
    serverSeed,
    clientSeed,
    nonce
  };
};

// Create a new monthly draw
const createMonthlyDraw = async (req, res) => {
  try {
    const {
      group_id,
      month,
      year,
      client_seed,
      winner_id,
      prize_amount,
      is_past_draw,
      animation_winner_user_id,
      server_seed: bodyServerSeed,
      nonce: bodyNonce
    } = req.body;
    
    // Convert month and year to integers
    const monthInt = parseInt(month, 10);
    const yearInt = parseInt(year, 10);
    const managerId = req.user.id;

    // Validate input
    if (!group_id || !month || !year) {
      return res.status(400).json({
        success: false,
        message: 'Group ID, month, and year are required'
      });
    }
    
    // Validate month and year are valid numbers
    if (isNaN(monthInt) || isNaN(yearInt)) {
      return res.status(400).json({
        success: false,
        message: 'Month and year must be valid numbers'
      });
    }
    
    // Validate month range
    if (monthInt < 1 || monthInt > 12) {
      return res.status(400).json({
        success: false,
        message: 'Month must be between 1 and 12'
      });
    }

    // Check if user is the manager of this group
    const chitGroup = await ChitGroup.findOne({
      where: { id: group_id, manager_id: managerId }
    });

    if (!chitGroup) {
      return res.status(404).json({
        success: false,
        message: 'Chit group not found or you are not the manager'
      });
    }

    // Check if group is active
    if (chitGroup.status !== 'active') {
      return res.status(400).json({
        success: false,
        message: 'Can only conduct draws for active chit groups'
      });
    }

    // Check if draw already exists for this month/year
    const existingDraw = await MonthlyDraw.findOne({
      where: { group_id, month: monthInt, year: yearInt }
    });

    if (existingDraw) {
      return res.status(400).json({
        success: false,
        message: 'Draw already exists for this month and year'
      });
    }

    // Get eligible members (active members who haven't won yet)
    const eligibleMembers = await GroupMember.findAll({
      where: {
        group_id,
        status: 'active'
      },
      include: [
        {
          model: User,
          attributes: ['id', 'full_name', 'mobile_number']
        }
      ]
    });

    // Filter out members who have already won
    const wonMembers = await MonthlyDraw.findAll({
      where: { group_id },
      attributes: ['winner_id']
    });

    const wonMemberIds = wonMembers.map((draw) => draw.winner_id);
    const availableMembers = eligibleMembers.filter(
      (member) =>
        !wonMemberIds.some((id) => id != null && String(id) === String(member.user_id))
    );

    const animationWinner =
      animation_winner_user_id != null && String(animation_winner_user_id).trim() !== ''
        ? String(animation_winner_user_id).trim()
        : null;

    if (availableMembers.length === 0 && !is_past_draw && !animationWinner) {
      return res.status(400).json({
        success: false,
        message: 'No eligible members available for draw'
      });
    }

    const alreadyWon = (uid) =>
      wonMemberIds.some((id) => id != null && String(id) === String(uid));

    // Determine winner
    let selectedWinnerId;
    let selectedWinner;
    let serverSeed;
    let resultHash;
    let nonce;
    let fromAppAnimation = false;

    if (animationWinner) {
      // App showed a winner in the animation; persist that user (manager-authenticated).
      // Without this branch the server re-rolls and WhatsApp/public links show the wrong name.
      fromAppAnimation = true;
      selectedWinnerId = animationWinner;
      selectedWinner = eligibleMembers.find((m) => String(m.user_id) === selectedWinnerId);
      if (!selectedWinner) {
        return res.status(400).json({
          success: false,
          message: 'Winner is not an active member of this group'
        });
      }
      if (alreadyWon(selectedWinnerId)) {
        return res.status(400).json({
          success: false,
          message: `${selectedWinner.User.full_name} has already won in a previous draw. Each member can only win once.`,
          alreadyWon: true,
          winnerName: selectedWinner.User.full_name
        });
      }
      if (!availableMembers.some((m) => String(m.user_id) === selectedWinnerId)) {
        return res.status(400).json({
          success: false,
          message: 'Winner is not eligible for this draw (already won or not in pool)'
        });
      }
      serverSeed =
        bodyServerSeed && String(bodyServerSeed).trim() !== ''
          ? String(bodyServerSeed).trim()
          : crypto.randomBytes(32).toString('hex');
      nonce = bodyNonce != null ? parseInt(bodyNonce, 10) : Date.now();
      if (Number.isNaN(nonce)) {
        nonce = Date.now();
      }
      const cs = client_seed != null && String(client_seed).trim() !== '' ? String(client_seed) : 'animation';
      resultHash = crypto
        .createHash('sha256')
        .update(`${serverSeed}-${cs}-${nonce}-${selectedWinnerId}`)
        .digest('hex');
    } else if (is_past_draw && winner_id) {
      // Past draw - manual winner selection
      selectedWinnerId = winner_id;
      serverSeed = `PAST_DRAW_${Date.now()}`;
      nonce = Date.now();
      resultHash = crypto.createHash('sha256').update(`${serverSeed}_${winner_id}_${nonce}`).digest('hex');

      // Get winner details
      selectedWinner = eligibleMembers.find((m) => String(m.user_id) === String(winner_id));
      if (!selectedWinner) {
        return res.status(400).json({
          success: false,
          message: 'Selected winner is not a member of this group'
        });
      }

      // Check if this member has already won
      if (alreadyWon(winner_id)) {
        return res.status(400).json({
          success: false,
          message: `${selectedWinner.User.full_name} has already won in a previous draw. Each member can only win once.`,
          alreadyWon: true,
          winnerName: selectedWinner.User.full_name
        });
      }
    } else {
      // Regular draw - provably fair random selection (API-only / no animation winner)
      serverSeed = crypto.randomBytes(32).toString('hex');
      nonce = Date.now();

      const result = generateProvablyFairResult(
        serverSeed,
        client_seed || 'default',
        nonce,
        availableMembers
      );

      selectedWinnerId = result.winner.user_id;
      selectedWinner = result.winner;
      resultHash = result.hash;
    }

    // Calculate prize amount
    const totalMembers = eligibleMembers.length;
    const calculatedPrizeAmount = prize_amount || chitGroup.total_value; // Use provided or default
    
    // For past imports, use all active members; for animation + random draws, use not-yet-won pool
    const membersForDraw = is_past_draw && !fromAppAnimation ? eligibleMembers : availableMembers;
    
    // Create monthly draw
    const monthlyDraw = await MonthlyDraw.create({
      group_id,
      month: monthInt,
      year: yearInt,
      draw_date: new Date(),
      eligible_members: membersForDraw.map(member => ({
        id: member.user_id,
        name: member.User.full_name,
        mobile: member.User.mobile_number
      })),
      winner_id: selectedWinnerId,
      prize_amount: calculatedPrizeAmount,
      server_seed: serverSeed,
      server_seed_hash: crypto.createHash('sha256').update(serverSeed).digest('hex'),
      client_seed:
        client_seed || (fromAppAnimation ? 'animation' : is_past_draw ? 'PAST_DRAW' : 'default'),
      nonce,
      result_hash: resultHash,
      status: 'completed',
      notes: fromAppAnimation
        ? `App animation draw — ${selectedWinner.User.full_name}`
        : is_past_draw
          ? `Past draw result (imported) - Winner: ${selectedWinner.User.full_name}`
          : `Winner selected: ${selectedWinner.User.full_name}`
    });

    res.status(201).json({
      success: true,
      message: 'Monthly draw completed successfully',
      data: {
        ...monthlyDraw.toJSON(),
        winner: selectedWinner.User,
        eligible_count: membersForDraw.length
      }
    });
  } catch (error) {
    console.error('Create monthly draw error:', error);
    res.status(500).json({
      success: false,
      message: 'Internal server error'
    });
  }
};

// Get all monthly draws for a group
const getGroupMonthlyDraws = async (req, res) => {
  try {
    const { group_id } = req.params;
    const { status, page = 1, limit = 20 } = req.query;

    // Check if user has access to this group
    const chitGroup = await ChitGroup.findOne({
      where: {
        id: group_id,
        [Op.or]: [
          { manager_id: req.user.id },
          { '$members.user_id$': req.user.id }
        ]
      },
      include: [
        {
          model: GroupMember,
          as: 'members',
          include: [
            {
              model: User,
              attributes: ['id', 'full_name', 'mobile_number']
            }
          ]
        }
      ]
    });

    if (!chitGroup) {
      return res.status(404).json({
        success: false,
        message: 'Chit group not found or access denied'
      });
    }

    const whereClause = { group_id };
    if (status) {
      whereClause.status = status;
    }

    const offset = (page - 1) * limit;

    const monthlyDraws = await MonthlyDraw.findAndCountAll({
      where: whereClause,
      include: [
        {
          model: User,
          as: 'winner',
          attributes: ['id', 'full_name', 'mobile_number']
        }
      ],
      order: [['created_at', 'DESC']],
      limit: parseInt(limit),
      offset: parseInt(offset)
    });

    res.json({
      success: true,
      message: 'Monthly draws retrieved successfully',
      data: {
        monthlyDraws: monthlyDraws.rows,
        total: monthlyDraws.count,
        page: parseInt(page),
        limit: parseInt(limit),
        totalPages: Math.ceil(monthlyDraws.count / limit)
      }
    });
  } catch (error) {
    console.error('Get group monthly draws error:', error);
    res.status(500).json({
      success: false,
      message: 'Internal server error'
    });
  }
};

// Get monthly draw details
const getMonthlyDrawDetails = async (req, res) => {
  try {
    const { draw_id } = req.params;

    const monthlyDraw = await MonthlyDraw.findOne({
      where: { id: draw_id },
      include: [
        {
          model: User,
          as: 'winner',
          attributes: ['id', 'full_name', 'mobile_number']
        },
        {
          model: ChitGroup,
          attributes: ['id', 'name', 'monthly_installment', 'foreman_commission_amount']
        }
      ]
    });

    if (!monthlyDraw) {
      return res.status(404).json({
        success: false,
        message: 'Monthly draw not found'
      });
    }

    // Check if user has access to this draw
    const chitGroup = await ChitGroup.findOne({
      where: {
        id: monthlyDraw.group_id,
        [Op.or]: [
          { manager_id: req.user.id },
          { '$members.user_id$': req.user.id }
        ]
      }
    });

    if (!chitGroup) {
      return res.status(403).json({
        success: false,
        message: 'Access denied to this monthly draw'
      });
    }

    res.json({
      success: true,
      message: 'Monthly draw details retrieved successfully',
      data: monthlyDraw
    });
  } catch (error) {
    console.error('Get monthly draw details error:', error);
    res.status(500).json({
      success: false,
      message: 'Internal server error'
    });
  }
};

// Verify provably fair result
const verifyDrawResult = async (req, res) => {
  try {
    const { draw_id } = req.params;
    const { client_seed } = req.body;

    const monthlyDraw = await MonthlyDraw.findOne({
      where: { id: draw_id },
      include: [
        {
          model: User,
          as: 'winner',
          attributes: ['id', 'full_name', 'mobile_number']
        }
      ]
    });

    if (!monthlyDraw) {
      return res.status(404).json({
        success: false,
        message: 'Monthly draw not found'
      });
    }

    // Check if user has access to this draw
    const chitGroup = await ChitGroup.findOne({
      where: {
        id: monthlyDraw.group_id,
        [Op.or]: [
          { manager_id: req.user.id },
          { '$members.user_id$': req.user.id }
        ]
      }
    });

    if (!chitGroup) {
      return res.status(403).json({
        success: false,
        message: 'Access denied to this monthly draw'
      });
    }

    // Verify the result
    const verificationResult = generateProvablyFairResult(
      monthlyDraw.server_seed,
      client_seed || monthlyDraw.client_seed,
      monthlyDraw.nonce,
      monthlyDraw.eligible_members
    );

    const isVerified = verificationResult.hash === monthlyDraw.result_hash;

    res.json({
      success: true,
      message: 'Draw verification completed',
      data: {
        isVerified,
        originalHash: monthlyDraw.result_hash,
        verificationHash: verificationResult.hash,
        serverSeed: monthlyDraw.server_seed,
        clientSeed: client_seed || monthlyDraw.client_seed,
        nonce: monthlyDraw.nonce,
        winner: monthlyDraw.winner
      }
    });
  } catch (error) {
    console.error('Verify draw result error:', error);
    res.status(500).json({
      success: false,
      message: 'Internal server error'
    });
  }
};

// Get draw statistics for a group
const getDrawStatistics = async (req, res) => {
  try {
    const { group_id } = req.params;

    // Check if user has access to this group
    const chitGroup = await ChitGroup.findOne({
      where: {
        id: group_id,
        [Op.or]: [
          { manager_id: req.user.id },
          { '$members.user_id$': req.user.id }
        ]
      }
    });

    if (!chitGroup) {
      return res.status(404).json({
        success: false,
        message: 'Chit group not found or access denied'
      });
    }

    // Get all draws for this group
    const draws = await MonthlyDraw.findAll({
      where: { group_id },
      include: [
        {
          model: User,
          as: 'winner',
          attributes: ['id', 'full_name', 'mobile_number']
        }
      ],
      order: [['created_at', 'ASC']]
    });

    // Get total members
    const totalMembers = await GroupMember.count({
      where: { group_id, status: 'active' }
    });

    // Calculate statistics
    const completedDraws = draws.filter(draw => draw.status === 'completed');
    const totalPrizeAmount = completedDraws.reduce((sum, draw) => sum + parseFloat(draw.prize_amount), 0);
    const averagePrizeAmount = completedDraws.length > 0 ? totalPrizeAmount / completedDraws.length : 0;

    // Get winners by month
    const winnersByMonth = completedDraws.map(draw => ({
      month: draw.month,
      year: draw.year,
      winner: draw.winner,
      prize_amount: draw.prize_amount,
      draw_date: draw.draw_date
    }));

    res.json({
      success: true,
      message: 'Draw statistics retrieved successfully',
      data: {
        totalDraws: draws.length,
        completedDraws: completedDraws.length,
        pendingDraws: draws.filter(draw => draw.status === 'pending').length,
        totalMembers,
        totalPrizeAmount,
        averagePrizeAmount,
        winnersByMonth,
        remainingDraws: chitGroup.duration_months - completedDraws.length
      }
    });
  } catch (error) {
    console.error('Get draw statistics error:', error);
    res.status(500).json({
      success: false,
      message: 'Internal server error'
    });
  }
};

// Delete monthly draw (manager only, for mistakes)
const deleteMonthlyDraw = async (req, res) => {
  try {
    const { draw_id } = req.params;
    const managerId = req.user.id;

    // Find the draw
    const monthlyDraw = await MonthlyDraw.findOne({
      where: { id: draw_id },
      include: [
        {
          model: ChitGroup,
          attributes: ['id', 'manager_id', 'name']
        }
      ]
    });

    if (!monthlyDraw) {
      return res.status(404).json({
        success: false,
        message: 'Monthly draw not found'
      });
    }

    // Check if user is the manager
    if (monthlyDraw.ChitGroup.manager_id !== managerId) {
      return res.status(403).json({
        success: false,
        message: 'Only the group manager can delete draws'
      });
    }

    // Store draw info for response
    const drawInfo = {
      month: monthlyDraw.month,
      year: monthlyDraw.year,
      groupName: monthlyDraw.ChitGroup.name
    };

    // Delete the draw
    await monthlyDraw.destroy();

    res.json({
      success: true,
      message: `Draw for ${drawInfo.month}/${drawInfo.year} deleted successfully`,
      data: drawInfo
    });
  } catch (error) {
    console.error('Delete monthly draw error:', error);
    res.status(500).json({
      success: false,
      message: 'Internal server error'
    });
  }
};

// Update monthly draw (manager only, for corrections)
const updateMonthlyDraw = async (req, res) => {
  try {
    const { draw_id } = req.params;
    const { winner_id, prize_amount, notes, month, year, draw_date } = req.body;
    const managerId = req.user.id;

    // Find the draw
    const monthlyDraw = await MonthlyDraw.findOne({
      where: { id: draw_id },
      include: [
        {
          model: ChitGroup,
          attributes: ['id', 'manager_id', 'name']
        }
      ]
    });

    if (!monthlyDraw) {
      return res.status(404).json({
        success: false,
        message: 'Monthly draw not found'
      });
    }

    // Check if user is the manager
    if (monthlyDraw.ChitGroup.manager_id !== managerId) {
      return res.status(403).json({
        success: false,
        message: 'Only the group manager can update draws'
      });
    }

    const updates = {};

    let nextMonth = monthlyDraw.month;
    let nextYear = monthlyDraw.year;
    const monthProvided = month !== undefined && month !== null && month !== '';
    const yearProvided = year !== undefined && year !== null && year !== '';
    if (monthProvided) {
      nextMonth = parseInt(month, 10);
    }
    if (yearProvided) {
      nextYear = parseInt(year, 10);
    }

    if (monthProvided || yearProvided) {
      if (Number.isNaN(nextMonth) || nextMonth < 1 || nextMonth > 12) {
        return res.status(400).json({
          success: false,
          message: 'Month must be between 1 and 12'
        });
      }
      if (Number.isNaN(nextYear) || nextYear < 2020) {
        return res.status(400).json({
          success: false,
          message: 'Year must be 2020 or later'
        });
      }
      const conflict = await MonthlyDraw.findOne({
        where: {
          group_id: monthlyDraw.group_id,
          month: nextMonth,
          year: nextYear,
          id: { [Op.ne]: draw_id }
        }
      });
      if (conflict) {
        return res.status(400).json({
          success: false,
          message: `Another draw already exists for ${nextMonth}/${nextYear}`
        });
      }
      updates.month = nextMonth;
      updates.year = nextYear;
    }

    if (draw_date !== undefined && draw_date !== null && draw_date !== '') {
      const d = new Date(draw_date);
      if (Number.isNaN(d.getTime())) {
        return res.status(400).json({
          success: false,
          message: 'Invalid draw_date'
        });
      }
      updates.draw_date = d;
    }

    // Update winner if provided and different from current
    if (winner_id) {
      const winnerStr = String(winner_id);
      const currentWinnerStr = monthlyDraw.winner_id != null ? String(monthlyDraw.winner_id) : '';
      if (winnerStr !== currentWinnerStr) {
        const member = await GroupMember.findOne({
          where: {
            group_id: monthlyDraw.ChitGroup.id,
            user_id: winner_id,
            status: 'active'
          },
          include: [{ model: User, attributes: ['id', 'full_name'] }]
        });

        if (!member) {
          return res.status(400).json({
            success: false,
            message: 'Winner must be an active member of this group'
          });
        }

        const duplicateWinner = await MonthlyDraw.findOne({
          where: {
            group_id: monthlyDraw.group_id,
            winner_id,
            id: { [Op.ne]: draw_id }
          }
        });
        if (duplicateWinner) {
          return res.status(400).json({
            success: false,
            message: `${member.User.full_name} has already won in another draw. Each member can only win once.`,
            alreadyWon: true,
            winnerName: member.User.full_name
          });
        }

        updates.winner_id = winner_id;
        if (notes === undefined || notes === null) {
          updates.notes = `Winner updated to: ${member.User.full_name}`;
        }
      }
    }

    // Update prize amount if provided (including 0)
    if (prize_amount != null && prize_amount !== '') {
      updates.prize_amount = prize_amount;
    }

    // Update notes if provided
    if (notes !== undefined && notes !== null) {
      updates.notes = notes;
    }

    if (Object.keys(updates).length === 0) {
      return res.status(400).json({
        success: false,
        message: 'No valid fields to update'
      });
    }

    // Perform update
    await monthlyDraw.update(updates);

    // Fetch updated draw with winner details
    const updatedDraw = await MonthlyDraw.findOne({
      where: { id: draw_id },
      include: [
        {
          model: User,
          as: 'winner',
          attributes: ['id', 'full_name', 'mobile_number']
        }
      ]
    });

    res.json({
      success: true,
      message: 'Monthly draw updated successfully',
      data: updatedDraw
    });
  } catch (error) {
    console.error('Update monthly draw error:', error);
    res.status(500).json({
      success: false,
      message: 'Internal server error'
    });
  }
};

module.exports = {
  createMonthlyDraw,
  getGroupMonthlyDraws,
  getMonthlyDrawDetails,
  verifyDrawResult,
  getDrawStatistics,
  deleteMonthlyDraw,
  updateMonthlyDraw
};
