import Order from "../../models/Order";
import _RS from "../../helpers/ResponseHelper";
import Helper from "../../helpers/Helper";
import Admin from "../../models/Admin";
import OrderStatus from "../../models/OrderStatus";
import * as mongoose from "mongoose";
import OrderDetail from "../../models/OrderDetail";
import moment = require("moment");
import Transaction from "../../models/Transaction";
import NotificationUser from "../../models/NotificationUser";
var slug = require('slug');

export class OrderController {
  static async list(req, res, next) {
    
    try {
      const startTime = new Date().getTime();

      let sort: any = [["createdAt", -1]];
      if (req.query.sort) {
        sort = Object.keys(req.query.sort).map((key) => [
          key,
          req.query.sort[key],
        ]);
      }

      const options = {
        page: req.query.page || 1,
        limit: req.query.limit || 10,
        collation: {
          locale: "en",
        },
      };

      let filteredQuery: any = {};

      
      if (req.query.start_date && req.query.end_date) {
        filteredQuery.created_at = {
          $gte: new Date(req.query.start_date + "T00:00:00Z"),
          $lte: new Date(req.query.end_date + "T23:59:59Z"),
        };
      }

      if(req.query.order_status){
        if(req.query.order_status == 'all'){
          filteredQuery.status = { $in: ['new','in-progress','complete','cancel'] } ;
        }else{
          filteredQuery.status =  req.query.order_status;
        }
      }

      if (req.query.payment_method) {
        var arrayValues   = req.query.payment_method.split(",");
        var booleanValues = arrayValues.map(function (value) {
          return value;
        });
        filteredQuery.payment_method = { $in: booleanValues };
      }

      filteredQuery.order_type = req.query.order_type;

      if (req.query.search && req.query.search.trim()) {
        filteredQuery.$or = [
          {
            random_order_id: {
              $regex: new RegExp(req.query.search),
              $options: "i",
            },
          },
        ];
      }
    
      let query: any = [
        {
          $lookup: {
            from: "users",
            localField: "washer_id",
            foreignField: "_id",
            as: "washer"
          },
        },
        {
          $unwind: {
            path : "$washer",
            preserveNullAndEmptyArrays: true
          },
        },
        {
          $lookup: {
            from: "users",
            localField: "user_id",
            foreignField: "_id",
            as: "user_id"
          },
        },
        {
          $unwind: {
            path : "$user_id",
            preserveNullAndEmptyArrays: true
          },
        },
        {
          $match: filteredQuery,
        },
        {
          $sort: {
            created_at: -1,
          },
        },
      ]
      var myAggregate   =   Order.aggregate(query)
      const list        =   await Order.aggregatePaginate(myAggregate, options);
      return _RS.ok(res, "SUCCESS", "List", { list: list }, startTime);
    } catch (err) {
      next(err);
    }
  }

  static async statusChange(req, res, next) {
    try {
      const startTime   =   new Date().getTime();
      const { status, order_id, service_id }  =   req.body;
      const getOrder    =   await Order.findById(order_id);
      if (!getOrder){
        return _RS.notFound(res, "NOTFOUND", "Order not found", {}, startTime);
      }
      
      if (status === 'complete') {
        
        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);

        // Count the number of services with is_complete set to true
        const numServicesCompleted = updatedOrder.service_id.filter((service) => service.is_complete === true).length;
          
        // Check if all services are completed
        if (numServicesCompleted === getOrder.service_id.length) {
          getOrder.status         = status;
          getOrder.current_status = status;
        }
      } else {
        getOrder.status         = status;
        getOrder.current_status = status;
      }
      
      getOrder.save();

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

      return _RS.ok(res, "SUCCESS", "Order status has been updated successfully", getOrder, startTime);
    } catch (err) {
      next(err);
    }
  }

  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.notFound(res, "NOTFOUND", "Order not found", {}, startTime);
      }

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

      return _RS.ok(res, "SUCCESS", "Order Services get successfully", incompleteServices, startTime);

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

  }

  static async view(req, res, next) {
    try {
      const startTime = new Date().getTime();
      const getOrderStatus = await OrderStatus.find({ order_id : req.params.id }).sort({ created_at : -1 }).populate([
        { path : "mark_by" }
      ]);

      const getOrderSubscription = await OrderDetail.aggregate([
        {
          $lookup: {
            from: "users",
            localField: "washer_id",
            foreignField: "_id",
            as: "washer"
          },
        },
        {
          $unwind: {
            path : "$washer",
            preserveNullAndEmptyArrays: true
          },
        },
        {
          $match: {
            order_id : new mongoose.Types.ObjectId(req.params.id)
          },
        },
        {
          $sort: {
            date : 1,
          },
        },
      ]);
      
      // find({ order_id : req.params.id }).sort({ date : 1 }).populate({ path: "washer_id" });
      
      const getOrder = await Order.findOne({ _id: req.params.id }).populate([
        { path: "user_id" },
        { path: "washer_id" },
        { path: "subscription_id" },
        { path: "discount_id" },
        {
          path: "car_id",
          populate: ([
            {
              path: "make_id",
              select: "name image",
            },
            {
              path: "type_id",
              select: "name",
            },
            {
              path: "model_id",
              select: "name",
            },
            {
              path: "color_id",
              select: "color",
            },
            {
              path: "avatar_id",
              select: "image",
            }
          ])
        },
      ]);
      
      return _RS.ok(res, "SUCCESS", "Data get successfully", { getOrder, getOrderStatus, getOrderSubscription }, startTime);
    } catch (err) {
      next(err);
    }
  }


  static async assignWasher(req, res, next) {
    // try {
    //   const { order_id, washer_id, order_type }   =   req.body;

    //   const admin         =   await Admin.findById(req.user.id);
    //   const startTime     =   new Date().getTime();

    //   let getOrder : any  =   {};

    //   if(order_type == 'OnDemand'){
    //     getOrder                =   await Order.findById(order_id);
    //   }else{
    //     getOrder                =   await OrderDetail.findById(order_id);
    //   }
      
    //   getOrder.washer_id        =   washer_id;
    //   getOrder.current_status   =   'assign';
    //   getOrder.status           =   'in-progress'
    //   getOrder.save();

    //   await new OrderStatus({ order_id : order_id, mark_by : admin._id, mark_on : new Date(), status : getOrder.status }).save();
      
    //   if(order_type == 'Subscription'){
    //     await OrderDetail.find({ order_id : getOrder._id, date : { $gte: getOrder.date }}).updateMany({ washer_id : washer_id, status : 'in-progress' });
    //   }

    //   await Transaction.findOne({ order_id : getOrder._id }).updateOne({ washer_id  : washer_id })

    //   const notiTitle         =  "Washer has been assigned by " + admin.name;
    //   const notiDesription    =  "Washer assigned to your order ID " + getOrder.random_order_id;
      
    //   // Helper.sendNotification(admin._id, getOrder.user_id, notiTitle, notiDesription);

    //   return _RS.ok(res, "SUCCESS", "Washer has been assigned successfully", {}, startTime);
    // } catch (err) {
    //   next(err);
    // }
    try {
      const { order_id, washer_id, order_type }   =   req.body;
      const admin                     =   await Admin.findById(req.user.id);
      const startTime                 =   new Date().getTime();

      let getOrder                    =   await Order.findById(order_id);
      const getOrderDetail            =   await OrderDetail.findById(order_id);
      

      /* if(getOrder.payment_status != 'paid'){
        return _RS.notAcceptable(res, "NOT_ACCEPTABLE", "The payment has not been completed yet. Please wait for its completion.", {}, startTime);
      } */

      if(getOrderDetail){
        getOrder                      =   await Order.findById(getOrderDetail.order_id);
      }

      // let notiTitle                   =   '';
      // let notiDesription              =   '';
      
      if(order_type == 'OnDemand'){
        getOrder.washer_id              =   washer_id;
        getOrder.current_status         =   'assign';
        getOrder.status                 =   'in-progress'
        getOrder.assigned_by            =   admin._id;
        getOrder.save();

        await new OrderStatus({ order_id : order_id, mark_by : admin._id, mark_on : new Date(), status : getOrder.status }).save();
        
        // await OrderDetail.find({ order_id : order_id }).updateMany({ washer_id : washer_id });

        await Transaction.findOne({ order_id : getOrder._id }).updateOne({ washer_id  : washer_id })

        // notiTitle         =  "Washer has been assigned by " + admin.name;
        // notiDesription    =  "Washer assigned to your order ID " + getOrder.random_order_id;
      }

      if(order_type == 'Subscription'){

        await OrderDetail.find({ order_id : getOrder._id, date : { $gte: getOrderDetail.date }}).updateMany({ washer_id : washer_id, current_status: 'assign', status : 'in-progress' });

        getOrder.washer_id       =   washer_id;
        getOrder.current_status  =   'assign';
        getOrder.status          =   'in-progress'
        getOrder.assigned_by     =   admin._id;
        getOrder.save();

        // notiTitle         =  "Washer has been assigned by " + admin.name;
        // notiDesription    =  "Washer assigned to your order ID " + getOrder.random_order_id;
      }

      // Send Notification to Customer
      const notiCustomerTitle         =  "Washer has been assigned by " + admin.name;
      const notiCustomerDesription    =  "Washer assigned on order ID " + getOrder.random_order_id;
      Helper.sendNotification(admin._id, getOrder ? getOrder.user_id : getOrderDetail.user_id, notiCustomerTitle, notiCustomerDesription);

      // Send Notification to Washer
      const notiWasherTitle           =  "You received a new job";
      const notiWasherDesription      =  "Order ID " + getOrder.random_order_id;
      Helper.sendNotification(admin._id, getOrder ? getOrder.washer_id : getOrderDetail.washer_id, notiWasherTitle, notiWasherDesription); 

      await NotificationUser.updateMany({ 'data.type': 'SERVICE_REQUEST', 'data.order_id': getOrder._id, to_id: { $ne: washer_id }},
      { $set: { "data.type" : 'SERVICE_REQUEST_EXPIRE'  } }) 

      await NotificationUser.updateOne({ 'data.type': 'SERVICE_REQUEST', 'data.order_id': getOrder._id, to_id: washer_id },
      { $set: { "data.type" : 'SERVICE_REQUEST_ACCEPTED'  } })
            
      return _RS.ok(res, "SUCCESS", "Washer has been assigned successfully", {}, startTime);
    } catch (err) {
      next(err);
    }
  }

}
