const Account = require('../models/account.model');
const Product = require('../models/product.model');
const Order = require('../models/order.model');
const OrderProduct = require('../models/orderProduct.model');
const Transaction = require('../models/transactions.model');
const Wallet = require('../models/wallet.model');
const mongoose = require('mongoose');
const axios = require('axios');

class UserController {
  constructor() {
    // Initialize any dependencies here if needed
  }

  static async getUserDashboardAnalytics(req, res) {
    try {
      const { id: userId } = req.user;
      const {
        transactionPurpose,
        orderStatus,
        productActive,
        dateFrom,
        dateTo,
      } = req.query;

      if (!mongoose.Types.ObjectId.isValid(userId)) {
        return res.status(400).json({
          success: false,
          message: 'Invalid user ID format',
        });
      }

      // Verify user exists
      const user = await Account.findById(userId);
      if (!user) {
        return res
          .status(404)
          .json({ success: false, message: 'User not found' });
      }

      // Build date filter
      const dateFilter = {};
      if (dateFrom || dateTo) {
        if (dateFrom) dateFilter.$gte = new Date(dateFrom);
        if (dateTo) dateFilter.$lte = new Date(dateTo);
      }

      // Get wallet balance
      const wallet = await Wallet.findOne({ user: userId });
      const walletBalance = wallet ? wallet.balance : 0;

      // Build product query
      const productQuery = { userId };
      if (productActive !== undefined) {
        productQuery.isActive = productActive === 'true';
      }
      if (dateFrom || dateTo) {
        productQuery.createdAt = dateFilter;
      }

      // Get total products listed by user with filters
      const totalProducts = await Product.countDocuments(productQuery);

      // Build order query for completed orders
      const orderQuery = { user: userId };

      if (orderStatus) {
        orderQuery.status = orderStatus;
        if (orderStatus === 'completed') {
          orderQuery.paymentCompleted = true;
        }
      } else {
        // Default to completed orders if no status specified
        orderQuery.status = 'completed';
        orderQuery.paymentCompleted = true;
      }

      if (dateFrom || dateTo) {
        orderQuery.createdAt = dateFilter;
      }

      // Get orders
      const orders = await Order.find(orderQuery).lean();

      // Get order products for these orders to calculate products sold
      const orderIds = orders.map((o) => o._id);
      const orderProducts = await OrderProduct.find({
        order: { $in: orderIds },
      })
        .populate('product')
        .lean();

      // Calculate total products sold and revenue
      let totalProductsSold = 0;
      let totalRevenue = 0;

      orders.forEach((order) => {
        totalRevenue += order.totalAmount;
      });

      orderProducts.forEach((op) => {
        totalProductsSold += op.quantity;
      });

      // Get order counts by status (using the same date filters)
      const orderCounts = await Order.countDocuments(orderQuery);

      // Get referral data with filters
      const referralQuery = { referredBy: user.referralCode };
      if (dateFrom || dateTo) {
        referralQuery.createdAt = dateFilter;
      }
      const referredUsers = await Account.countDocuments(referralQuery);

      // Build transaction query
      const transactionQuery = { receiver: userId };
      if (transactionPurpose) {
        transactionQuery.purpose = transactionPurpose;
      }
      if (dateFrom || dateTo) {
        transactionQuery.createdAt = dateFilter;
      }

      // Get transaction data with filters
      const transactions = await Transaction.find(transactionQuery)
        .sort({ createdAt: -1 })
        .lean();

      // Calculate total earnings by purpose
      const earningsByPurpose = {};
      transactions.forEach((t) => {
        if (!earningsByPurpose[t.purpose]) {
          earningsByPurpose[t.purpose] = 0;
        }
        earningsByPurpose[t.purpose] += t.amount;
      });

      return res.status(200).json({
        success: true,
        user: userId,
        data: {
          walletBalance,
          totalProducts,
          totalProductsSold,
          totalRevenue,
          orderCounts,
          referredUsers,
          transactions: {
            totalCount: transactions.length,
            earningsByPurpose,
            recentTransactions: transactions.slice(0, 5), // Return last 5 transactions
          },
          filters: {
            transactionPurpose,
            orderStatus,
            productActive,
            dateFrom,
            dateTo,
          },
        },
      });
    } catch (error) {
      console.error('Error fetching dashboard analytics:', error);
      return res.status(500).json({
        success: false,
        message: 'Internal server error',
        error: error.message,
      });
    }
  }

  static async getUserInfo(req, res) {
    try {
      const { id: userId } = req.user;

      if (!mongoose.Types.ObjectId.isValid(userId)) {
        return res.status(400).json({
          success: false,
          message: 'Invalid user ID format',
        });
      }

      const user = await Account.findById(userId)
        .select(
          '-password -twoFactorCode -twoFactorCodeExpiry -verificationCode -verificationCodeExpiry',
        )
        .lean();

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

      return res.status(200).json({
        success: true,
        data: user,
      });
    } catch (error) {
      console.error('Error fetching user info:', error);
      return res.status(500).json({
        success: false,
        message: 'Internal server error',
        error: error.message,
      });
    }
  }

  static async updateUserInfo(req, res) {
    try {
      const { id: userId } = req.user;
      const updateData = req.body;

      if (!mongoose.Types.ObjectId.isValid(userId)) {
        return res.status(400).json({
          success: false,
          message: 'Invalid user ID format',
        });
      }

      // List of allowed fields that can be updated
      const allowedFields = ['firstName', 'lastName', 'phone', 'addresses'];

      // List of protected fields that cannot be updated here
      const protectedFields = [
        'password',
        'verified',
        'role',
        'wallet',
        'referredBy',
        'referralCode',
        'twoFactorEnabled',
        'uid',
        'accountNumber',
        'bankName',
        'bankCode',
        'email',
        'suspended',
        'banned',
      ];

      // Check if any protected fields are being attempted to update
      const attemptedProtectedUpdate = protectedFields.some(
        (field) => field in updateData,
      );
      if (attemptedProtectedUpdate) {
        return res.status(400).json({
          success: false,
          message: 'Cannot update protected fields through this endpoint',
        });
      }

      // Filter the update data to only include allowed fields that exist in the request
      const filteredUpdate = {};
      for (const field of allowedFields) {
        if (field in updateData) {
          filteredUpdate[field] = updateData[field];
        }
      }

      // Handle address update specially
      if (updateData.address && !updateData.addresses) {
        const user = await Account.findById(userId);
        if (user) {
          const addresses = user.addresses || [];
          filteredUpdate.addresses = [...addresses, updateData.address];
        }
      }

      // If no valid fields to update after filtering
      if (Object.keys(filteredUpdate).length === 0) {
        return res.status(400).json({
          success: false,
          message: 'No valid fields provided for update',
        });
      }

      const user = await Account.findByIdAndUpdate(userId, filteredUpdate, {
        new: true,
        runValidators: true,
      }).select(
        '-password -twoFactorCode -twoFactorCodeExpiry -verificationCode -verificationCodeExpiry',
      );

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

      return res.status(200).json({
        success: true,
        message: 'User information updated successfully',
        data: user,
      });
    } catch (error) {
      console.error('Error updating user info:', error);

      // Handle MongoDB validation errors
      if (error.name === 'ValidationError') {
        const errors = Object.keys(error.errors).map((key) => ({
          field: key,
          message: error.errors[key].message,
        }));

        return res.status(400).json({
          success: false,
          message: 'Validation error',
          errors,
        });
      }

      return res.status(500).json({
        success: false,
        message: 'Internal server error',
        error: error.message,
      });
    }
  }

  static async validateAccountNumber(req, res) {
    try {
      const { accountNumber, bankCode } = req.query;
      if (!accountNumber || !bankCode) {
        return res.status(400).json({
          success: false,
          message: 'Account number and bank code are required',
        });
      }

      const response = await axios.get(
        `https://api.paystack.co/bank/resolve?account_number=${accountNumber}&bank_code=${bankCode}`,
        {
          headers: {
            Authorization: `Bearer ${process.env.PAYSTACK_SECRET_KEY}`,
            'Content-Type': 'application/json',
          },
        },
      );

      if (response.data.status) {
        return res.status(200).json({
          success: true,
          message: 'Account number is valid',
          data: response.data.data,
        });
      }

      return res.status(400).json({
        success: false,
        message: 'Invalid account number or bank code',
      });
    } catch (error) {
      console.error('Error validating account number:', error);
      return res.status(500).json({
        success: false,
        message: 'Internal server error',
        error: error.message,
      });
    }
  }

  static async addAccountDetails(req, res) {
    const session = await mongoose.startSession();
    session.startTransaction();

    try {
      const { id: userId } = req.user;
      const { accountNumber, bankName, bankCode } = req.body;

      if (!mongoose.Types.ObjectId.isValid(userId)) {
        await session.abortTransaction();
        session.endSession();
        return res.status(400).json({
          success: false,
          message: 'Invalid user ID format',
        });
      }

      // Validate required fields
      if (!accountNumber || !bankName || !bankCode) {
        await session.abortTransaction();
        session.endSession();
        return res.status(400).json({
          success: false,
          message: 'Account number, bank name, and bank code are required',
        });
      }

      // Find the user
      const account = await Account.findById(userId).session(session);
      if (!account) {
        await session.abortTransaction();
        session.endSession();
        return res.status(404).json({
          success: false,
          message: 'User not found',
        });
      }

      const response = await axios.post(
        `https://api.paystack.co/transferrecipient`,
        {
          type: 'nuban',
          name: `${account.firstName} ${account.lastName}`,
          account_number: accountNumber,
          bank_code: bankCode,
          currency: 'NGN',
          metadata: { userId: userId.toString() },
        },
        {
          headers: {
            Authorization: `Bearer ${process.env.PAYSTACK_SECRET_KEY}`,
            'Content-Type': 'application/json',
          },
        },
      );

      const wallet = await Wallet.findOne({ user: userId }).session(session);
      if (!wallet) {
        await session.abortTransaction();
        session.endSession();
        return res.status(404).json({
          success: false,
          message: 'Could not update the wallet details for this user',
        });
      }

      account.accountNumber = accountNumber;
      account.bankName = bankName;
      account.bankCode = bankCode;

      wallet.transferRecipientId = response.data.data.id;
      wallet.transferRecipientCode = response.data.data.recipient_code;

      await account.save({ session });
      await wallet.save({ session });

      await session.commitTransaction();
      session.endSession();

      return res.status(200).json({
        success: true,
        message: 'Account details updated successfully',
        data: {
          accountNumber,
          bankName,
          bankCode,
        },
      });
    } catch (error) {
      await session.abortTransaction();
      session.endSession();
      console.error('Error adding account details:', error);
      return res.status(500).json({
        success: false,
        message: 'Internal server error',
        error: error.message,
      });
    }
  }

  static async getUserAddresses(req, res) {
    try {
      const { id: userId } = req.user;

      if (!mongoose.Types.ObjectId.isValid(userId)) {
        return res.status(400).json({
          success: false,
          message: 'Invalid user ID format',
        });
      }

      const user = await Account.findById(userId).select('addresses').lean();

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

      return res.status(200).json({
        success: true,
        data: user.addresses || [],
      });
    } catch (error) {
      console.error('Error fetching user addresses:', error);
      return res.status(500).json({
        success: false,
        message: 'Internal server error',
        error: error.message,
      });
    }
  }

  static async addUserAddress(req, res) {
    try {
      const { userId } = req.params;
      const newAddress = req.body;

      if (!mongoose.Types.ObjectId.isValid(userId)) {
        return res.status(400).json({
          success: false,
          message: 'Invalid user ID format',
        });
      }

      const user = await Account.findById(userId);
      if (!user) {
        return res
          .status(404)
          .json({ success: false, message: 'User not found' });
      }

      // Initialize addresses array if it doesn't exist
      const addresses = user.addresses || [];

      // Add a unique ID to the address for later reference
      newAddress.id = new Date().getTime().toString(); // Simple unique ID

      if (addresses.length === 0) {
        newAddress.isDefault = newAddress.isDefault || true;
      } else {
        newAddress.isDefault = newAddress.isDefault || false;
      }

      addresses.push(newAddress);
      user.addresses = addresses;
      await user.save();

      return res.status(201).json({
        success: true,
        message: 'Address added successfully',
        data: addresses,
      });
    } catch (error) {
      console.error('Error adding user address:', error);
      return res.status(500).json({
        success: false,
        message: 'Internal server error',
        error: error.message,
      });
    }
  }

  static async updateUserAddress(req, res) {
    try {
      const { addressId } = req.params;
      const { id: userId } = req.user;
      const updatedAddress = req.body;

      if (!mongoose.Types.ObjectId.isValid(userId)) {
        return res.status(400).json({
          success: false,
          message: 'Invalid user ID format',
        });
      }

      const user = await Account.findById(userId);
      if (!user) {
        return res
          .status(404)
          .json({ success: false, message: 'User not found' });
      }

      const addresses = user.addresses || [];

      // Find address by ID
      const addressIndex = addresses.findIndex((addr) => addr.id === addressId);

      if (addressIndex === -1) {
        return res
          .status(404)
          .json({ success: false, message: 'Address not found' });
      }

      if (updatedAddress.isDefault) {
        addresses.forEach((address, index) => {
          if (index !== addressIndex) {
            address.isDefault = false;
          }
        });
      }

      addresses[addressIndex] = {
        ...addresses[addressIndex],
        ...updatedAddress,
        id: addressId, // Preserve the original ID
      };

      user.addresses = addresses;
      await user.save();

      return res.status(200).json({
        success: true,
        message: 'Address updated successfully',
        data: addresses,
      });
    } catch (error) {
      console.error('Error updating user address:', error);
      return res.status(500).json({
        success: false,
        message: 'Internal server error',
        error: error.message,
      });
    }
  }

  static async deleteUserAddress(req, res) {
    try {
      const { addressId } = req.params;
      const { id: userId } = req.user;

      if (!mongoose.Types.ObjectId.isValid(userId)) {
        return res.status(400).json({
          success: false,
          message: 'Invalid user ID format',
        });
      }

      const user = await Account.findById(userId);
      if (!user) {
        return res
          .status(404)
          .json({ success: false, message: 'User not found' });
      }

      const addresses = user.addresses || [];

      // Find address by ID
      const addressIndex = addresses.findIndex((addr) => addr.id === addressId);

      if (addressIndex === -1) {
        return res
          .status(404)
          .json({ success: false, message: 'Address not found' });
      }

      // If deleting the default address, set another address as default if available
      if (addresses[addressIndex].isDefault && addresses.length > 1) {
        const newDefaultIndex = addressIndex === 0 ? 1 : 0;
        addresses[newDefaultIndex].isDefault = true;
      }

      addresses.splice(addressIndex, 1);
      user.addresses = addresses;
      await user.save();

      return res.status(200).json({
        success: true,
        message: 'Address deleted successfully',
        data: addresses,
      });
    } catch (error) {
      console.error('Error deleting user address:', error);
      return res.status(500).json({
        success: false,
        message: 'Internal server error',
        error: error.message,
      });
    }
  }

  static async toggle2FA(req, res) {
    try {
      const { id: userId } = req.user;

      if (!mongoose.Types.ObjectId.isValid(userId)) {
        return res.status(400).json({
          success: false,
          message: 'Invalid user ID format',
        });
      }

      const user = await Account.findById(userId);
      if (!user) {
        return res
          .status(404)
          .json({ success: false, message: 'User not found' });
      }

      // Toggle 2FA status
      user.twoFactorEnabled = !user.twoFactorEnabled;
      await user.save();

      return res.status(200).json({
        success: true,
        message: `2FA ${user.twoFactorEnabled ? 'enabled' : 'disabled'} successfully`,
      });
    } catch (error) {
      console.error('Error Enabling 2FA:', error);
      return res.status(500).json({
        success: false,
        message: 'Internal server error',
        error: error.message,
      });
    }
  }

  static async initiate2FA(req, res) {
    try {
      const { id: userId } = req.user;

      if (!mongoose.Types.ObjectId.isValid(userId)) {
        return res.status(400).json({
          success: false,
          message: 'Invalid user ID format',
        });
      }

      const user = await Account.findById(userId);
      if (!user) {
        return res
          .status(404)
          .json({ success: false, message: 'User not found' });
      }

      // Generate a random 6-digit code
      const verificationCode = Math.floor(
        100000 + Math.random() * 900000,
      ).toString();
      const codeExpiry = new Date(Date.now() + 15 * 60 * 1000); // 15 minutes from now

      // Schedule email sending (you'll need to implement this with Agenda)
      // agenda.schedule('now', 'SEND_EMAIL', {
      //   to: user.email,
      //   type: '2FA',
      //   data: { code: verificationCode },
      // });

      // For now, we'll just log it (in production, implement proper email sending)
      console.log(`2FA code for ${user.email}: ${verificationCode}`);

      user.twoFactorCode = verificationCode;
      user.twoFactorCodeExpiry = codeExpiry;
      await user.save();

      return res.status(200).json({
        success: true,
        message: '2FA code generated and sent to your email',
        // In production, don't include the actual code in the response
        // code: verificationCode, // Only for demonstration, remove in production
      });
    } catch (error) {
      console.error('Error initiating 2FA:', error);
      return res.status(500).json({
        success: false,
        message: 'Internal server error',
        error: error.message,
      });
    }
  }

  static async verify2FA(req, res) {
    try {
      const { id: userId } = req.user;
      const { code } = req.body;

      if (!mongoose.Types.ObjectId.isValid(userId)) {
        return res.status(400).json({
          success: false,
          message: 'Invalid user ID format',
        });
      }

      if (!code) {
        return res.status(400).json({
          success: false,
          message: '2FA code is required',
        });
      }

      const user = await Account.findById(userId);
      if (!user) {
        return res
          .status(404)
          .json({ success: false, message: 'User not found' });
      }

      // Check if code matches and isn't expired
      if (
        user.twoFactorCode !== code ||
        !user.twoFactorCodeExpiry ||
        new Date() > new Date(user.twoFactorCodeExpiry)
      ) {
        return res
          .status(400)
          .json({ success: false, message: 'Invalid or expired code' });
      }

      // Clear the code after successful verification
      user.twoFactorCode = null;
      user.twoFactorCodeExpiry = null;
      await user.save();

      return res.status(200).json({
        success: true,
        message: '2FA verification successful',
        user: {
          id: user._id,
          email: user.email,
          name: `${user.firstName} ${user.lastName}`,
        },
      });
    } catch (error) {
      console.error('Error verifying 2FA:', error);
      return res.status(500).json({
        success: false,
        message: 'Internal server error',
        error: error.message,
      });
    }
  }
}

module.exports = UserController;
