const DeliveryBoy = require('../models/DeliveryBoy');
const User = require('../models/User');
const { validationResult } = require('express-validator');
const mongoose = require('mongoose');
const ApiError = require('../utils/ApiError');
const httpStatus = require('http-status');
const pick = require('../utils/pick');
const { getPagination } = require('../utils/pagination');
const bcrypt = require('bcryptjs');

// Ensure httpStatus is properly defined
if (!httpStatus) {
  httpStatus = require('http-status-codes');
}

/**
 * @desc    Get all delivery boys with pagination and filters
 * @route   GET /api/delivery-boys
 * @access  Private/Admin
 */
/**
 * @desc    Get all delivery boys with pagination and filters
 * @route   GET /api/delivery-boys
 * @access  Private/Admin
 */
const getAllDeliveryBoys = async (req, res, next) => {
  try {
    console.log('Fetching delivery boys with query:', req.query);
    
    const filter = pick(req.query, ['store', 'status', 'isAvailable', 'isVerified']);
    const options = pick(req.query, ['sortBy', 'limit', 'page', 'search']);
    
    // Set default pagination options
    options.limit = options.limit && parseInt(options.limit) > 0 ? parseInt(options.limit) : 10;
    options.page = options.page && parseInt(options.page) > 0 ? parseInt(options.page) : 1;
    
    // Add search to filter if provided
    if (options.search) {
      filter.$or = [
        { name: { $regex: options.search, $options: 'i' } },
        { email: { $regex: options.search, $options: 'i' } },
        { phone: { $regex: options.search, $options: 'i' } }
      ];
      delete options.search;
    }
    
    console.log('Filter:', filter);
    console.log('Options:', options);
    
    // Execute the query with error handling
    let result;
    try {
      result = await DeliveryBoy.paginate(filter, options);
      console.log('Query successful, found', result.docs?.length || 0, 'delivery boys');
    } catch (dbError) {
      console.error('Database query error:', dbError);
      return next(new ApiError(
        httpStatus.INTERNAL_SERVER_ERROR,
        'Error fetching delivery boys',
        [{ message: dbError.message }]
      ));
    }
    
    // Ensure we have a valid result
    if (!result) {
      console.error('Unexpected empty result from DeliveryBoy.paginate');
      return next(new ApiError(
        httpStatus.INTERNAL_SERVER_ERROR,
        'Unexpected error occurred while fetching delivery boys'
      ));
    }
    
    // Prepare response data
    const responseData = {
      success: true,
      data: Array.isArray(result.docs) ? result.docs : [],
      pagination: {
        total: result.totalDocs || 0,
        limit: result.limit || options.limit || 10,
        page: result.page || options.page || 1,
        pages: result.totalPages || 1,
        hasNextPage: result.hasNextPage || false,
        hasPrevPage: result.hasPrevPage || false
      }
    };
    
    // Debug log the httpStatus object
    console.log('httpStatus object:', httpStatus);
    
    // Use a hardcoded status code as fallback
    const statusCode = 200; // HTTP 200 OK
    console.log('Sending response with status:', statusCode);
    return res.status(statusCode).json(responseData);
    
  } catch (error) {
    console.error('Unexpected error in getAllDeliveryBoys:', error);
    next(error);
  }
};

/**
 * @desc    Get single delivery boy by ID
 * @route   GET /api/delivery-boys/:id
 * @access  Private/Admin
 */
const getDeliveryBoyById = async (req, res, next) => {
  try {
    if (!mongoose.Types.ObjectId.isValid(req.params.id)) {
      throw new ApiError(httpStatus.BAD_REQUEST, 'Invalid delivery boy ID');
    }
    
    const deliveryBoy = await DeliveryBoy.findByIdWithDetails(req.params.id);
    
    if (!deliveryBoy) {
      throw new ApiError(httpStatus.NOT_FOUND, 'Delivery boy not found');
    }

    res.status(httpStatus.OK).json({
      success: true,
      data: deliveryBoy
    });
  } catch (error) {
    next(error);
  }
};

/**
 * @desc    Create new delivery boy
 * @route   POST /api/delivery-boys
 * @access  Private/Admin
 */
const createDeliveryBoy = async (req, res, next) => {
  try {
    // Validate request
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return next(new ApiError(400, 'Validation error', errors.array())); // BAD_REQUEST
    }

    // Extract required fields
    const { 
      name, 
      email, 
      phone, 
      password, 
      store, 
      isAvailable = true,
      documents = []
    } = req.body;

    // Create delivery boy data object 
    const deliveryBoyData = {
      name: name.trim(),
      email: email.trim().toLowerCase(),
      phone: phone.trim(),
      isAvailable: Boolean(isAvailable), // Ensure boolean value
      documents: Array.isArray(documents) ? documents : []
    };

    // Add store only if provided and valid
    if (store && mongoose.Types.ObjectId.isValid(store)) {
      deliveryBoyData.store = store;
    }

    // Add password if provided
    if (password && password.length >= 6) {
      deliveryBoyData.password = password;
    }

    // Add location if provided
    if (req.body.longitude && req.body.latitude) {
      const longitude = parseFloat(req.body.longitude);
      const latitude = parseFloat(req.body.latitude);
      
      if (!isNaN(longitude) && !isNaN(latitude)) {
        deliveryBoyData.location = {
          type: 'Point',
          coordinates: [longitude, latitude]
        };
      }
    }

    // First, create the user account
    let user;
    try {
      const hashedPassword = await bcrypt.hash(password || 'password123', 10);
      
      const userData = {
        name: deliveryBoyData.name,
        email: deliveryBoyData.email,
        phone: deliveryBoyData.phone,
        password: hashedPassword,
        role: 'delivery_boy',
        status: true
      };

      // Create user first
      user = await User.create(userData);
      
      // Add user ID to delivery boy data
      deliveryBoyData.user = user._id;
    } catch (userError) {
      console.error('Error creating user account:', userError);
      
      if (userError.code === 11000) {
        const field = Object.keys(userError.keyPattern || { email: 1 })[0];
        throw new ApiError(
          409,
          `${field.charAt(0).toUpperCase() + field.slice(1)} already exists`,
          [{ field, message: `This ${field} is already registered` }]
        );
      }
      
      throw new ApiError(
        userError.statusCode || 500,
        userError.message || 'Failed to create user account for delivery boy'
      );
    }

    // Then create the delivery boy with the user reference
    let deliveryBoy;
    try {
      // Add user reference to delivery boy data
      deliveryBoyData.user = user._id;
      
      // Create delivery boy
      deliveryBoy = await DeliveryBoy.create(deliveryBoyData);
      
      // Update user with delivery boy reference
      await User.findByIdAndUpdate(user._id, { deliveryBoy: deliveryBoy._id });
      
    } catch (deliveryBoyError) {
      // If delivery boy creation fails, delete the user to maintain data consistency
      await User.findByIdAndDelete(user._id);
      
      console.error('Error creating delivery boy:', deliveryBoyError);
      
      // Handle duplicate key errors
      if (deliveryBoyError.code === 11000) {
        const field = Object.keys(deliveryBoyError.keyPattern || { email: 1 })[0];
        throw new ApiError(
          409,
          `${field.charAt(0).toUpperCase() + field.slice(1)} already exists`,
          [{ field, message: `This ${field} is already registered` }]
        );
      }
      
      throw new ApiError(
        deliveryBoyError.statusCode || 500,
        deliveryBoyError.message || 'Failed to create delivery boy'
      );
    }

    // Prepare response data
    const responseData = {
      _id: deliveryBoy._id,
      name: deliveryBoy.name,
      email: deliveryBoy.email,
      phone: deliveryBoy.phone,
      store: deliveryBoy.store,
      status: deliveryBoy.status,
      isAvailable: deliveryBoy.isAvailable,
      isVerified: deliveryBoy.isVerified || false,
      documents: deliveryBoy.documents || [],
      location: deliveryBoy.location,
      rating: deliveryBoy.rating || 0,
      totalDeliveries: deliveryBoy.totalDeliveries || 0,
      completedDeliveries: deliveryBoy.completedDeliveries || 0,
      cancelledDeliveries: deliveryBoy.cancelledDeliveries || 0,
      lastOnline: deliveryBoy.lastOnline,
      createdAt: deliveryBoy.createdAt,
      updatedAt: deliveryBoy.updatedAt
    };

    // Send success response
    return res.status(201).json({
      success: true,
      data: responseData,
      message: 'Delivery boy created successfully'
    });
  } catch (error) {
    // Log the error for debugging
    console.error('Error creating delivery boy:', error);
    
    // Handle ApiError instances (from model)
    if (error instanceof ApiError) {
      return next(error);
    }
    
    // Handle duplicate key errors
    if (error.code === 11000) {
      const field = Object.keys(error.keyPattern)[0];
      return next(new ApiError(
        409, // CONFLICT
        `${field.charAt(0).toUpperCase() + field.slice(1)} already exists`,
        [{ field, message: `This ${field} is already registered` }]
      ));
    }
    
    // Handle validation errors
    if (error.name === 'ValidationError') {
      const errors = Object.values(error.errors).map(err => ({
        field: err.path,
        message: err.message
      }));
      return next(new ApiError(400, 'Validation failed', errors)); // BAD_REQUEST
    }
    
    // Handle other errors with a proper status code
    return next(new ApiError(
      error.statusCode || 500, // INTERNAL_SERVER_ERROR
      error.message || 'An error occurred while creating the delivery boy'
    ));
  }
};

/**
 * @desc    Update delivery boy
 * @route   PUT /api/delivery-boys/:id
 * @access  Private/Admin
 */
const updateDeliveryBoy = async (req, res, next) => {
  try {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      throw new ApiError(httpStatus.BAD_REQUEST, 'Validation error', errors.array());
    }

    if (!mongoose.Types.ObjectId.isValid(req.params.id)) {
      throw new ApiError(httpStatus.BAD_REQUEST, 'Invalid delivery boy ID');
    }

    const updateData = { ...req.body };
    
    // Prevent updating certain fields directly
    delete updateData.email;
    delete updateData.phone;
    delete updateData.totalDeliveries;
    delete updateData.completedDeliveries;
    delete updateData.cancelledDeliveries;

    // Handle location update if provided
    if (req.body.longitude && req.body.latitude) {
      updateData.location = {
        type: 'Point',
        coordinates: [
          parseFloat(req.body.longitude),
          parseFloat(req.body.latitude)
        ]
      };
    }

    // Handle documents update if provided
    if (req.body.documents) {
      updateData.$push = { documents: { $each: req.body.documents } };
    }

    const updatedDeliveryBoy = await DeliveryBoy.updateDeliveryBoy(req.params.id, updateData);

    res.status(200).json({
      success: true,
      data: updatedDeliveryBoy,
      message: 'Delivery boy updated successfully'
    });
  } catch (error) {
    next(error);
  }
};

/**
 * @desc    Delete delivery boy (hard delete)
 * @route   DELETE /api/delivery-boys/:id
 * @access  Private/Admin
 */
const deleteDeliveryBoy = async (req, res, next) => {
  try {
    if (!mongoose.Types.ObjectId.isValid(req.params.id)) {
      return res.status(400).json({
        success: false,
        message: 'Invalid delivery boy ID'
      });
    }

    // Get the delivery boy first to get the associated user ID
    const deliveryBoy = await DeliveryBoy.findById(req.params.id);
    if (!deliveryBoy) {
      return res.status(404).json({
        success: false,
        message: 'Delivery boy not found'
      });
    }

    try {
      // Find and delete the associated user using the deliveryBoy reference
      await User.findOneAndDelete({ deliveryBoy: req.params.id });

      // Then delete the delivery boy
      await DeliveryBoy.deleteOne({ _id: req.params.id });

      return res.status(200).json({
        success: true,
        message: 'Delivery boy deleted successfully'
      });
    } catch (error) {
      console.error('Error during deletion:', error);
      return res.status(500).json({
        success: false,
        message: 'Error deleting delivery boy',
        error: error.message
      });
    }
  } catch (error) {
    console.error('Error in deleteDeliveryBoy:', error);
    return res.status(500).json({
      success: false,
      message: 'Internal server error',
      error: error.message
    });
  }
};

/**
 * @desc    Get nearby delivery boys
 * @route   GET /api/delivery-boys/nearby
 * @access  Private/Admin
 */
const getNearbyDeliveryBoys = async (req, res, next) => {
  try {
    const { longitude, latitude, maxDistance = 5000 } = req.query;
    
    if (!longitude || !latitude) {
      throw new ApiError(httpStatus.BAD_REQUEST, 'Longitude and latitude are required');
    }

    const deliveryBoys = await DeliveryBoy.findAvailableNear(
      [parseFloat(longitude), parseFloat(latitude)],
      parseInt(maxDistance)
    );

    res.status(httpStatus.OK).json({
      success: true,
      data: deliveryBoys
    });
  } catch (error) {
    next(error);
  }
};

/**
 * @desc    Update delivery boy status
 * @route   PATCH /api/delivery-boys/:id/status
 * @access  Private/Admin
 */
const updateDeliveryBoyStatus = async (req, res, next) => {
  try {
    if (!mongoose.Types.ObjectId.isValid(req.params.id)) {
      throw new ApiError(httpStatus.BAD_REQUEST, 'Invalid delivery boy ID');
    }

    const { status } = req.body;
    
    if (!['pending', 'approved', 'rejected'].includes(status)) {
      throw new ApiError(httpStatus.BAD_REQUEST, 'Invalid status value. Must be one of: pending, approved, rejected.');
    }

    const updatedDeliveryBoy = await DeliveryBoy.updateDeliveryBoy(
      req.params.id,
      { status }
    );

    res.status(httpStatus.OK).json({
      success: true,
      data: updatedDeliveryBoy,
      message: 'Delivery boy status updated successfully'
    });
  } catch (error) {
    next(error);
  }
};

/**
 * @desc    Update delivery boy availability
 * @route   PATCH /api/delivery-boys/:id/availability
 * @access  Private/Admin
 */
const updateDeliveryBoyAvailability = async (req, res, next) => {
  try {
    if (!mongoose.Types.ObjectId.isValid(req.params.id)) {
      throw new ApiError(400, 'Invalid delivery boy ID');
    }

    const { isAvailable } = req.body;
    
    if (typeof isAvailable !== 'boolean') {
      throw new ApiError(400, 'isAvailable must be a boolean');
    }

    const updatedDeliveryBoy = await DeliveryBoy.updateDeliveryBoy(
      req.params.id,
      { 
        isAvailable,
        lastOnline: new Date()
      }
    );

    if (!updatedDeliveryBoy) {
      throw new ApiError(404, 'Delivery boy not found');
    }

    // Ensure we're sending a proper response
    return res.status(200).json({
      success: true,
      data: updatedDeliveryBoy,
      message: `Delivery boy marked as ${isAvailable ? 'available' : 'unavailable'}`
    });
  } catch (error) {
    console.error('Error in updateDeliveryBoyAvailability:', error);
    
    // Ensure we're sending a proper error response
    if (error instanceof ApiError) {
      return res.status(error.statusCode || 500).json({
        success: false,
        message: error.message,
        error: error.message
      });
    }
    
    return res.status(500).json({
      success: false,
      message: 'Internal server error',
      error: error.message
    });
  }
};

module.exports = {
  getAllDeliveryBoys,
  getDeliveryBoyById,
  createDeliveryBoy,
  updateDeliveryBoy,
  deleteDeliveryBoy,
  getNearbyDeliveryBoys,
  updateDeliveryBoyStatus,
  updateDeliveryBoyAvailability
};
