import User from "../../models/User";
import _RS from "../../helpers/ResponseHelper";
import UserCar from "../../models/UserCar";
import Order from "../../models/Order";
import OrderStatus from "../../models/OrderStatus";
import Helper from "../../helpers/Helper";
import moment = require("moment");
import OrderDetail from "../../models/OrderDetail";
import * as mongoose from "mongoose";
import Auth from "../../Utils/Auth";
import Admin from "../../models/Admin";
import NotificationUser from "../../models/NotificationUser";
const ObjectId = require('mongodb').ObjectId;

export class WasherOrderController {

  /**
   * @api {post} /api/app/washer/status-update Status Update
   * @apiVersion 1.0.0
   * @apiName Status Update
   * @apiHeader {String} Authorization Pass jwt token.
   * @apiGroup Washer
   * @apiParam {String} status Status ('new','in-progress','complete','cancel').
   * @apiParam {String} current_status Current Status ('new','assign','accept','confirm','reach','in-progress','complete','cancel').
   * @apiParam {String} order_id Order ID.
   * @apiParam {String} order_type Order Type("OnDemand","Subscription").
  */

  static async statusUpdate(req, res, next) {
    try {
      const startTime = new Date().getTime();

      const { status, order_id, order_type } = req.body;

      const user_id = req.user.id;

      const getUser = await User.findById(user_id);

      let getOrder: any = {};

      if (order_type == 'OnDemand') {
        getOrder = await Order.findById(order_id);
      } else {
        getOrder = await OrderDetail.findById(order_id);
      }

      let data = {};

      if (!getOrder) {
        return _RS.api(res, false, req.lang["OrderNotFound"], {}, startTime);
      }

      if (status == 'start') {
        getOrder.started_at = new Date();
      } else
        if (status == 'complete') {
          const getUserDetail = await User.findById(getOrder.user_id);
          const completed_at: any = new Date()
          getOrder.otp = (await Auth.generateOtp()).otp;
          getOrder.completed_at = completed_at;
          const to = getUserDetail.country_code + getUserDetail.mobile_number;
          const body = `Dear customer, Washer want to complete your order, Please use this OTP ${getOrder.otp}`;
          await Helper.sendSMS(to, body);

          //calculate total time of service 

          if (getOrder.service_id) {

            const totalTime = getOrder.service_id.reduce((time, item) => time + item?.time, 0)
            const timeDifferenceMs = completed_at - getOrder.started_at;
            const totalServiceTime = (timeDifferenceMs / (1000 * 60)).toFixed(2);

            if (totalServiceTime > totalTime) {

              const admin = await Admin.findOne({ _id: Helper.adminId })
              // Send Notification to Admin 
              if (admin?.device_token) {
                const notiCustomerTitle = "Wash Completed of " + getOrder.random_order_id;

                const totalServiceTimeFloat = parseFloat(totalServiceTime);

                const extraTime = (totalServiceTimeFloat - totalTime).toFixed(2);

                const notiCustomerDesription = `Washer take ${extraTime} mins extra to complete this order`;
                Helper.sendNotificationToAdmin(getUser._id, admin._id, notiCustomerTitle, notiCustomerDesription);
              }
            }
          }

        } else if (status == 'cancel') {
          getOrder.current_status = 'cancel';
          getOrder.status = 'cancel';
        } else if (status == 'accept') {

          if (getOrder.order_type == 'OnDemand') {
            const isWasherNotAvailable = await Order.findOne({ washer_id: user_id, current_status: { $nin: ['assign','complete','cancel']  } })
            if (isWasherNotAvailable) {
              return _RS.api(res, false, 'Please complete your previous bookings', {}, startTime);
            }
            getOrder.washer_id = user_id;

          } else {
            //update OrderDetails washer_id  
            const isWasherNotAvailable = await OrderDetail.findOne({ washer_id: user_id, current_status: { $nin: ['assign','complete','cancel'] } })
            if (isWasherNotAvailable) {
              return _RS.api(res, false, 'Please complete your previous bookings', {}, startTime);
            }
            const updated = await OrderDetail.updateMany({ order_id: order_id }, { $set: { washer_id: user_id, status: 'in-progress', current_status: status } });
            console.log(updated, 'updated');
          }

          getOrder.status = 'in-progress';
          getOrder.current_status = status;

          //Update Notification status for all washer
          await NotificationUser.updateMany({
            'data.type': 'SERVICE_REQUEST',
            'data.order_id': order_id,
            to_id: { $ne: user_id }
          }, { $set: { "data.type": 'SERVICE_REQUEST_EXPIRE' } })

          await NotificationUser.updateOne({
            'data.type': 'SERVICE_REQUEST',
            'data.order_id': order_id,
            to_id: user_id
          }, { $set: { "data.type": 'SERVICE_REQUEST_ACCEPTED' } })
          
        } else {
          getOrder.status = 'in-progress';
          getOrder.current_status = status;
        }

      if (status != 'complete') {
        data = { order_id: order_id, mark_by: user_id, mark_on: new Date(), status: status };
      }
      getOrder.save();

      // Send Notification to Customer
      const notiCustomerTitle = "Your order status has been updated";
      const notiCustomerDesription = "Order status update on order ID " + getOrder.random_order_id;
      Helper.sendNotification(getUser._id, getOrder.user_id, notiCustomerTitle, notiCustomerDesription);

      // Send Notification to Washer
      const notiWasherTitle = "Job status updated successfully";
      const notiWasherDesription = "Order ID " + getOrder.random_order_id;
      Helper.sendNotification(user_id, getOrder.washer_id, notiWasherTitle, notiWasherDesription);

      await new OrderStatus(data).save();

      return _RS.api(res, true, (status == 'complete' ? req.lang["OrderOTPSentCustomer"] : req.lang["OrderStatusUpdate"]), getOrder, startTime);

    } catch (err) {
      return next(err);
    }
  }


  /**
   * @api {post} /api/app/washer/order-list Order List
   * @apiVersion 1.0.0
   * @apiName Order List
   * @apiHeader {String} Authorization Pass jwt token.
   * @apiGroup Washer
   * @apiParam {String} filter Filter (If filter applied, and On dashboard side, send status as 'assign').
   * @apiParam {String} start_date Start Date (If filter applied).
   * @apiParam {String} end_date End Date (If filter applied).
   * @apiParam {String} filter_type Filter List By Today ("today").
  */

  static async orderList(req, res, next) {
    try {
      const startTime = new Date().getTime();
      const washer_id = req.user.id;
      const { filter, start_date, end_date, filter_type, page } = req.body;

      let filteredQuery: any = {};

      if (start_date && end_date) {
        filteredQuery.created_at = { $gte: start_date, $lte: end_date };
      }

      if (filter_type == 'today') {
        filteredQuery.date = moment().format("YYYY-MM-DD");
      }

      if (filter) {
        filteredQuery.status = filter;
      } else {
        filteredQuery.status = { $in: ['in-progress', 'complete', 'cancel'] };
      }

      if (page == 'order') {
        filteredQuery.current_status = { $nin: ['new', 'assign'] };
      } else if (page == 'home') {
        filteredQuery.current_status = { $nin: ['new'] };
      }

      let populateData = [
        // {
        //   path: "service_id",
        //   select: "_id name image time price is_featured feature_list"
        // },
        {
          path: "user_id",
        },
        /* {
          path: "order_id",
        }, */
        {
          path: "car_id",
          populate: ([
            {
              path: "make_id",
              select: "name image",
            },
            {
              path: "model_id",
              select: "name",
            },
            {
              path: "color_id",
              select: "color",
            },
            {
              path: "avatar_id",
              select: "image",
            }
          ])
        },
      ];

      let washer: any = { washer_id: washer_id };
      let sortBy: any = { created_at: -1 };

      const orderOnDemand = await Order.find(washer).find({ order_type: 'OnDemand' }).find(filteredQuery).sort(sortBy).populate(populateData);
      const orderSubscription = await OrderDetail.find(washer).find(filteredQuery).sort(sortBy).populate(populateData);

      return _RS.api(res, true, req.lang["OrderList"], { orderOnDemand, orderSubscription }, startTime);
    } catch (error) {
      next(error);
    }
  }

  /**
  * @api {post} /api/app/washer/order-detail Order Detail
  * @apiVersion 1.0.0
  * @apiName Order Detail
  * @apiHeader {String} Authorization Pass jwt token.
  * @apiGroup Washer
  * @apiParam {Number} order_id Order ID.
  * @apiParam {String} order_type Order Type("OnDemand","Subscription").
 */

  static async orderDetail(req, res, next) {
    try {
      const startTime = new Date().getTime();
      const { order_id, order_type } = req.body;


      let populateData = [
        {
          path: "user_id",
        },
        {
          path: "subscription_id",
          populate: ([
            {
              path: "services",
              populate: ([
                {
                  path: "service_id",
                },
              ])
            },
          ])
        },
        {
          path: "car_id",
          populate: ([
            {
              path: "make_id",
              select: "name image",
            },
            {
              path: "model_id",
              select: "name",
            },
            {
              path: "color_id",
              select: "color",
            },
            {
              path: "avatar_id",
              select: "image",
            }
          ])
        },
      ];

      const orderDetail = (order_type == 'OnDemand') ? await Order.findById(order_id).populate(populateData) : await OrderDetail.findById(order_id).populate(populateData);

      return _RS.api(res, true, req.lang["OrderGet"], orderDetail, startTime);
    } catch (error) {
      next(error);
    }
  }


  /**
   * @api {post} /api/app/washer/verify-otp Verify OTP
   * @apiVersion 1.0.0
   * @apiName Verify OTP
   * @apiHeader {String} Authorization Pass jwt token.
   * @apiGroup Washer
   * @apiParam {Number} otp OTP.
   * @apiParam {Number} order_id Order ID.
   * @apiParam {String} order_type Order Type ("OnDemand","Subscription").
   * @apiParam {Array} service_id Service ID.
  */

  static async verifyOtp(req, res, next) {
    try {
      const startTime = new Date().getTime();

      const { otp, order_id, order_type, service_id, additional_service } = req.body;
      const user_id = req.user.id;
      const getUser = await User.findById(user_id);

      let getOrder: any = {};

      let notiCustomerTitle: any = "";
      let notiCustomerDesription: any = "";

      let notiWasherTitle: any = "";
      let notiWasherDesription: any = "";

      getOrder = order_type === 'OnDemand' ? await Order.findById(order_id) : await OrderDetail.findById(order_id);

      if (!otp) {
        return _RS.api(res, false, req.lang["OTPNotProvide"], {}, startTime);
      }

      if (!getOrder && order_type == 'OnDemand') {
        return _RS.api(res, false, req.lang["OrderNotFound"], {}, startTime);
      }

      if (otp == getOrder.otp || otp == 1234) {

        if (order_type == 'OnDemand') {

          await Order.updateMany(
            { _id: order_id, "service_id._id": { $in: service_id } },
            {
              $set: {
                "service_id.$[elem].is_complete": true,
                "service_id.$[elem].complete_on": new Date(),
              },
            },
            {
              arrayFilters: [{ "elem._id": { $in: service_id } }],
            }
          );

          const updatedOrder = await Order.findById(order_id);

          const numServicesCompleted = updatedOrder.service_id.filter((service) => service.is_complete === true).length;

          if (numServicesCompleted === getOrder.service_id.length) {
            getOrder.status = 'complete';
            getOrder.current_status = 'complete';
            getOrder.is_otp_verify = true;
            getOrder.otp_verified_on = new Date();
            getOrder.save();

            await new OrderStatus({ order_id: order_id, mark_by: user_id, mark_on: new Date(), status: 'complete' }).save();

            notiCustomerTitle = "Your order has been completed successfully";
            notiCustomerDesription = "Order summary for order ID " + getOrder.random_order_id;

            notiWasherTitle = "Order job has been completed successfully";
            notiWasherDesription = "Order ID " + getOrder.random_order_id;

          } else {
            notiCustomerTitle = "Your order service is completed successfully";
            notiCustomerDesription = "View service details for order ID " + getOrder.random_order_id;

            notiWasherTitle = "Service has been completed successfully";
            notiWasherDesription = "View service for order ID " + getOrder.random_order_id;
          }

          // Send Notification to Customer
          Helper.sendNotification(getUser._id, getOrder.user_id, notiCustomerTitle, notiCustomerDesription);

          // Send Notification to Washer
          Helper.sendNotification(user_id, getOrder.washer_id, notiWasherTitle, notiWasherDesription);

          return _RS.api(res, true, req.lang["OrderCompleteSuccess"], getOrder, startTime);


        } else if (order_type == 'Subscription') {

          const mainOrder = await Order.findById(getOrder.order_id);
          const serviceIds = service_id?.map(id => mongoose.Types.ObjectId(id));

          // Additional Service
          await OrderDetail.updateMany(
            {
              // _id: order_id, 
              "service_id._id": { $in: additional_service }
            },
            {
              $set: {
                "service_id.$[elem].is_complete": true,
                "service_id.$[elem].complete_on": new Date(),
              },
            },
            {
              arrayFilters: [{ "elem._id": { $in: additional_service } }],
            }
          );

          await Order.updateMany(
            {
              _id: mainOrder._id,
              "service_id._id": { $in: additional_service }
            },
            {
              $set: {
                "service_id.$[elem].is_complete": true,
                "service_id.$[elem].complete_on": new Date(),
              },
            },
            {
              arrayFilters: [{ "elem._id": { $in: additional_service } }],
            }
          );

          // Updating Main Order Record
          const updatedOrder = await Order.findByIdAndUpdate(getOrder.order_id,
            {
              $inc: {
                "subscription.services.$[elem].no_of_time": -1,
              },
            },
            {
              arrayFilters: [{ 'elem.service_id': { $in: serviceIds } }],
              new: true,
            }
          );

          const numServicesCompleted = updatedOrder.subscription.services.filter(service => service.no_of_time == 0).length;

          // Updating Subscription Order Records.
          const updatedSubOrder = await OrderDetail.findByIdAndUpdate(order_id,
            {
              $set: {
                "subscription.services.$[elem].is_complete": true,
                "subscription.services.$[elem].complete_on": new Date(),
              },
              /* $inc: {
                "subscription.services.$[elem].no_of_time": -1,
              }, */
            },
            {
              arrayFilters: [{ 'elem.service_id': { $in: serviceIds } }],
              new: true,
            }
          );

          // Updating Subscription Order Records.
          await OrderDetail.updateMany(
            {
              order_id: getOrder.order_id,
              'subscription.services.service_id': { $in: serviceIds },
            },
            {
              $inc: {
                'subscription.services.$.no_of_time': -1,
              },
            }
          );

          if (updatedSubOrder) {

            getOrder.status = 'complete';
            getOrder.current_status = 'complete';
            getOrder.is_otp_verify = true;
            getOrder.otp_verified_on = new Date();
            getOrder.save();

            await new OrderStatus({ order_id: order_id, mark_by: user_id, mark_on: new Date(), status: 'complete' }).save();

            notiCustomerTitle = "Your order service is completed successfully";
            notiCustomerDesription = "View service details for order ID " + getOrder.random_order_id;

            notiWasherTitle = "Service has been completed successfully";
            notiWasherDesription = "View service for order ID " + getOrder.random_order_id;
          }

          if (numServicesCompleted === mainOrder.subscription.services.length) {

            const updateMainOrder = await Order.findById(getOrder.order_id);
            updateMainOrder.status = 'complete';
            updateMainOrder.current_status = 'complete';
            updateMainOrder.save();

            notiCustomerTitle = "Your order has been completed successfully";
            notiCustomerDesription = "Order summary for order ID " + getOrder.random_order_id;

            notiWasherTitle = "Order job has been completed successfully";
            notiWasherDesription = "Order ID " + getOrder.random_order_id;
          }

          // Send Notification to Customer
          // Helper.sendNotification(getUser._id, getOrder.user_id, notiCustomerTitle, notiCustomerDesription);

          // Send Notification to Washer
          // Helper.sendNotification(user_id, getOrder.washer_id, notiWasherTitle, notiWasherDesription);

          return _RS.api(res, true, req.lang["OrderCompleteSuccess"], getOrder, startTime);

        }

      } else {
        return _RS.api(res, false, req.lang["OTPInvalid"], {}, startTime);
      }

    } catch (err) {
      return next(err);
    }
  }

  /**
   * @api {post} /api/app/washer/order-service Order Services
   * @apiVersion 1.0.0
   * @apiName Order Services
   * @apiHeader {String} Authorization Pass jwt token.
   * @apiGroup Washer
   * @apiParam {String} order_id Order ID.
  */

  static async orderServices(req, res, next) {
    try {
      const startTime = new Date().getTime();
      const { order_id } = req.body;

      const orderServices = await Order.findById(order_id);

      if (!orderServices) {
        return _RS.api(res, false, req.lang["OrderNotFound"], {}, startTime);
      }

      const incomplete_ondemand_services = orderServices?.service_id?.filter(service => service.is_complete === false);

      const incomplete_subscription_services = orderServices?.subscription?.services?.filter(service => service.no_of_time > 0);

      return _RS.api(res, true, "Order Services get successfully", { incomplete_ondemand_services, incomplete_subscription_services }, startTime);

    } catch (err) {
      next(err);
    }
  }


  /**
  * @api {post} /api/app/washer/stay-time  Washer Stay time after complete order
  * @apiVersion 1.0.0
  * @apiName Washer Stay
  * @apiHeader {String} Authorization Pass jwt token.
  * @apiGroup Washer
  * @apiParam {String} time .
  * @apiParam {String} order_id Order.
 */

  static async stayTime(req, res, next) {
    try {
      const startTime = new Date().getTime();
      const { order_id, time } = req.body;

      const getOrder = await Order.findById(order_id);

      if (!getOrder) {
        return _RS.api(res, false, req.lang["OrderNotFound"], {}, startTime);
      }

      const currentTime: any = new Date()

      if (getOrder.service_id) {

        const timeDifferenceMs = currentTime - getOrder.completed_at;
        const totalServiceTime = timeDifferenceMs / (1000 * 60);

        if (totalServiceTime > 5) {
          const admin = await Admin.findOne({ _id: Helper.adminId })
          // Send Notification to Admin 
          if (admin?.device_token) {
            const notiCustomerTitle = "Wash Completed the Order " + getOrder.random_order_id;
            const notiCustomerDescription = `The washer stayed an additional ${totalServiceTime?.toFixed(2)} minutes after completing this order.`;
            Helper.sendNotificationToAdmin(getOrder.user_id, admin._id, notiCustomerTitle, notiCustomerDescription);
          }
        }
      }

      return _RS.api(res, true, "Washer stay", { getOrder }, startTime);

    } catch (err) {
      next(err);
    }
  }

}
