const { DeliveryBoy } = require("../../models");
const AppError = require("../../utils/AppError");
const { asyncHandler } = require("../../utils/asyncHandler");
const { hashPassword } = require("../../utils/password");
const { toPublicProfile } = require("../../utils/toPublicProfile");
const { deleteUploadFileByPublicUrl } = require("../../utils/deleteUploadFile");
const { assertObjectId } = require("../../utils/assertObjectId");
const { publicUploadPathFromFile } = require("../../utils/publicUploadPath");
const { getPagination, searchFilter } = require("../../utils/listQuery");

const UPLOAD_FOLDER = "delivery";

exports.listDeliveryBoys = asyncHandler(async (req, res) => {
  const { page, limit, skip } = getPagination(req.query);
  const { status, search } = req.query;

  const filter = {};
  if (status) {
    filter.status = status;
  }
  const searchOr = searchFilter(search, [
    "name",
    "email",
    "phone",
    "licenseNumber",
    "vehicleType",
  ]);
  if (searchOr) {
    Object.assign(filter, searchOr);
  }

  const [deliveryBoys, total] = await Promise.all([
    DeliveryBoy.find(filter)
      .select("-passwordHash")
      .sort({ createdAt: -1 })
      .skip(skip)
      .limit(limit)
      .lean(),
    DeliveryBoy.countDocuments(filter),
  ]);

  res.json({
    deliveryBoys: deliveryBoys.map((d) => toPublicProfile(d)),
    pagination: {
      page,
      limit,
      total,
      pages: Math.ceil(total / limit) || 1,
    },
  });
});

exports.getDeliveryBoyById = asyncHandler(async (req, res) => {
  assertObjectId(req.params.id);
  const deliveryBoy = await DeliveryBoy.findById(req.params.id).select(
    "-passwordHash"
  );
  if (!deliveryBoy) {
    throw new AppError("Delivery partner not found", 404);
  }
  res.json({ deliveryBoy: toPublicProfile(deliveryBoy) });
});

exports.createDeliveryBoy = asyncHandler(async (req, res) => {
  const {
    name,
    email,
    password,
    phone,
    dob,
    gender,
    fcm_id,
    profileImage,
    licenseNumber,
    vehicleType,
    status,
  } = req.body;

  if (!name || !email || !password || !phone) {
    deleteUploadFileByPublicUrl(publicUploadPathFromFile(req, UPLOAD_FOLDER));
    throw new AppError("Name, email, password, and phone are required", 400);
  }

  const emailNorm = String(email).toLowerCase().trim();
  const existing = await DeliveryBoy.findOne({ email: emailNorm });
  if (existing) {
    deleteUploadFileByPublicUrl(publicUploadPathFromFile(req, UPLOAD_FOLDER));
    throw new AppError("Email is already in use", 409);
  }

  const fromFile = publicUploadPathFromFile(req, UPLOAD_FOLDER);
  const passwordHash = await hashPassword(password);
  let deliveryBoy;
  try {
    deliveryBoy = await DeliveryBoy.create({
      name,
      email: emailNorm,
      passwordHash,
      phone,
      dob: dob === "" ? undefined : dob,
      gender,
      fcm_id,
      licenseNumber,
      vehicleType,
      status: status || "pending",
      profileImage: fromFile ?? profileImage ?? null,
    });
  } catch (err) {
    deleteUploadFileByPublicUrl(fromFile);
    throw err;
  }

  res.status(201).json({
    message: "Delivery partner created",
    deliveryBoy: toPublicProfile(
      await DeliveryBoy.findById(deliveryBoy._id).select("-passwordHash")
    ),
  });
});

exports.updateDeliveryBoy = asyncHandler(async (req, res) => {
  assertObjectId(req.params.id);
  const deliveryBoy = await DeliveryBoy.findById(req.params.id);
  if (!deliveryBoy) {
    throw new AppError("Delivery partner not found", 404);
  }

  const {
    name,
    email,
    password,
    phone,
    dob,
    gender,
    fcm_id,
    profileImage,
    licenseNumber,
    vehicleType,
    status,
  } = req.body;

  if (email !== undefined) {
    const emailNorm = String(email).toLowerCase().trim();
    const taken = await DeliveryBoy.findOne({
      email: emailNorm,
      _id: { $ne: deliveryBoy._id },
    });
    if (taken) {
      deleteUploadFileByPublicUrl(publicUploadPathFromFile(req, UPLOAD_FOLDER));
      throw new AppError("Email is already in use", 409);
    }
    deliveryBoy.email = emailNorm;
  }

  if (req.file) {
    deleteUploadFileByPublicUrl(deliveryBoy.profileImage);
    deliveryBoy.profileImage = publicUploadPathFromFile(req, UPLOAD_FOLDER);
  } else if (Object.prototype.hasOwnProperty.call(req.body, "profileImage")) {
    if (profileImage === "" || profileImage === null) {
      deleteUploadFileByPublicUrl(deliveryBoy.profileImage);
      deliveryBoy.profileImage = null;
    } else if (profileImage !== deliveryBoy.profileImage) {
      deleteUploadFileByPublicUrl(deliveryBoy.profileImage);
      deliveryBoy.profileImage = profileImage;
    }
  }

  if (name !== undefined) {
    deliveryBoy.name = name;
  }
  if (phone !== undefined) {
    deliveryBoy.phone = phone;
  }
  if (dob !== undefined) {
    deliveryBoy.dob = dob === "" ? null : dob;
  }
  if (gender !== undefined) {
    deliveryBoy.gender = gender;
  }
  if (fcm_id !== undefined) {
    deliveryBoy.fcm_id = fcm_id;
  }
  if (licenseNumber !== undefined) {
    deliveryBoy.licenseNumber =
      licenseNumber === "" ? null : licenseNumber;
  }
  if (vehicleType !== undefined) {
    deliveryBoy.vehicleType = vehicleType === "" ? null : vehicleType;
  }
  if (status !== undefined) {
    deliveryBoy.status = status;
  }
  if (password) {
    deliveryBoy.passwordHash = await hashPassword(password);
  }

  await deliveryBoy.save();
  res.json({
    message: "Delivery partner updated",
    deliveryBoy: toPublicProfile(
      await DeliveryBoy.findById(deliveryBoy._id).select("-passwordHash")
    ),
  });
});

exports.deleteDeliveryBoy = asyncHandler(async (req, res) => {
  assertObjectId(req.params.id);
  const deliveryBoy = await DeliveryBoy.findById(req.params.id);
  if (!deliveryBoy) {
    throw new AppError("Delivery partner not found", 404);
  }
  deleteUploadFileByPublicUrl(deliveryBoy.profileImage);
  await DeliveryBoy.findByIdAndDelete(req.params.id);
  res.json({ message: "Delivery partner deleted" });
});
