const mongoose = require("mongoose");
const crypto = require("crypto");
const subscribe = mongoose.model("Productsubscribe");
const Product = mongoose.model("Products");
const Products = mongoose.model("Products");
const Cart = mongoose.model("Cart");
const PriceRule = mongoose.model("PriceRule");
const DiscountCode = mongoose.model("DiscountCode");
const Addresses = mongoose.model("Addresses");
const CartHistory = mongoose.model("CartHistory");
const WebsiteCategory = mongoose.model("WebsiteCategory");
const User = mongoose.model("User");
const CategoryDesc = mongoose.model("CategoryDesc");
const Order = mongoose.model("Orders");
const BillHistory = mongoose.model("BillHistorys");
const { updateShopperrPriceAlgo } = require("../../apps/middlewares/priceAlgo");
const {
  getNextSequenceValue,
} = require("../../apps/middlewares/CommonFunctions");
const {
  constantValues,
  mongooseConstantValues,
} = require("../../apps/common/systemConstents");

exports.Subscribe = async (req, res) => {
  const ProductsubscribeData = await Productsubscribe.find({
    retailer: req.body.user,
  });

  const productsSubscribeResult = await Promise.all(
    ProductsubscribeData.map(async (item) => {
      const ProductData = await Product.find({ _id: item.product_id });
      // const ProductData = await Product.find({ product_id : 40097 });
      // return ProductData;

      const allData = {};

      let subUnsub = "";

      ProductData.forEach((item1) => {
        if (item.sub_unsub == 1) {
          subUnsub = "Subscribe";
        } else {
          subUnsub = "UnSubscribe";
        }

        allData["product_name"] = item1.product_name;
        allData["product_image"] = item1.product_image;
        allData["product_id"] = item1.product_id;
        allData["status"] = item1.status;
        allData["s_price"] = item1.s_price;
        allData["sub_unsub"] = subUnsub;
        allData["marketplace"] = item.marketplace;
        allData["sku"] = item.istore_product_id;
      });
      return allData;
    })
  );

  return res.send(productsSubscribeResult);
};

exports.getProductInfobyProductId = async (req, res) => {
  try {
    let products = await getProductData(mongoose.Types.ObjectId(req.body.pId));
    if (products.length > 0) {
      let productArray = products[0];
      productArray = JSON.parse(JSON.stringify(productArray));
      let supplier = await User.findOne({
        _id: mongoose.Types.ObjectId(productArray.supplier_id),
      });
      let suppllierOrigin = "";
      if (supplier) {
        suppllierOrigin = supplier.price_algo;
      }
      if (req.body.sku && productArray.vd) {
        let vd = productArray.vd.find((v) => v.sku == req.body.sku);
        if (vd) {
          productArray.price = vd.price;
          productArray.mrp = vd.mrp;
        }
      }
      let fp = productArray["price"];
      let mrps = productArray.mrp != "" ? productArray.mrp : 0;
      let pw = productArray.weight != "" ? productArray.weight : 0;
      let lenght = productArray.length != "" ? productArray.length : 0;
      let width = productArray.width != "" ? productArray.width : 0;
      let height = productArray.height != "" ? productArray.height : 0;
      let pbcd = productArray.bcd != undefined ? productArray.bcd : 10;
      let pigst = productArray.igst != undefined ? productArray.igst : 18;
      let retailerMargin =
        productArray.productMargin != undefined
          ? productArray.productMargin
          : 40;
      let pswc =
        productArray["swcOnBcd"] != undefined ? productArray["swcOnBcd"] : 10;
      let sp = await updateShopperrPriceAlgo(
        Number(fp),
        Number(pw),
        suppllierOrigin,
        pbcd,
        pigst,
        pswc,
        lenght,
        width,
        height,
        mrps,
        retailerMargin
      );
      let amazonprice =
        productArray.marketPlacePrice != undefined
          ? productArray.marketPlacePrice
          : 0;
      let amazonmrp =
        productArray.marketPlaceMrp != undefined
          ? productArray.marketPlaceMrp
          : 0;
      let marketPlaceUrl =
        productArray.marketPlaceUrl != undefined
          ? productArray.marketPlaceUrl
          : "No Data Found.";

      let response = {
        airPrice: {
          factoryPrice: fp,
          price: sp.airPrice.price,
          cogs: sp.airPrice.cogs,
          mrp: productArray.pmrp
            ? productArray.pmrp
            : Math.floor(
                (sp.airPrice.mrp ? sp.airPrice.mrp * 2 : 0).toFixed(2) / 100
              ) *
                100 +
              99,
          priceWithoutGst: sp.airPrice.priceWithoutGst,
          gstOnProduct: new Number(
            (sp.airPrice.price - sp.airPrice.priceWithoutGst).toFixed(2)
          ).toFixed(2),
          retailerMargin: new Number(retailerMargin).toFixed(2),
          retailerMarginPrice: (
            (sp.airPrice.cogs * retailerMargin) /
            100
          ).toFixed(2),
          amazonprice: amazonprice,
          amazonmrp: amazonmrp,
          amazonMarketPlaceUrl: marketPlaceUrl,
          gstPer: pigst,
        },
        dropshipperPrice: {
          factoryPrice: fp,
          price: sp.dropshipperPrice.price,
          cogs: sp.dropshipperPrice.cogs,
          mrp: productArray.pmrp
            ? productArray.pmrp
            : Math.floor(
                (sp.dropshipperPrice.mrp
                  ? sp.dropshipperPrice.mrp * 2
                  : 0
                ).toFixed(2) / 100
              ) *
                100 +
              99,
          priceWithoutGst: sp.dropshipperPrice.priceWithoutGst,
          gstOnProduct: new Number(
            (
              sp.dropshipperPrice.price - sp.dropshipperPrice.priceWithoutGst
            ).toFixed(2)
          ).toFixed(2),
          retailerMargin: new Number(retailerMargin).toFixed(2),
          retailerMarginPrice: (
            (sp.dropshipperPrice.cogs * retailerMargin) /
            100
          ).toFixed(2),
          amazonprice: amazonprice,
          amazonmrp: amazonmrp,
          amazonMarketPlaceUrl: marketPlaceUrl,
          gstPer: pigst,
        },
        seaPrice: {
          factoryPrice: fp,
          price: sp.seaPrice.price,
          cogs: sp.seaPrice.cogs,
          mrp: productArray.pmrp
            ? productArray.pmrp
            : Math.floor(
                (sp.seaPrice.mrp ? sp.seaPrice.mrp * 2 : 0).toFixed(2) / 100
              ) *
                100 +
              99,
          priceWithoutGst: sp.seaPrice.priceWithoutGst,
          gstOnProduct: new Number(
            (sp.seaPrice.price - sp.seaPrice.priceWithoutGst).toFixed(2)
          ).toFixed(2),
          retailerMargin: new Number(retailerMargin).toFixed(2),
          retailerMarginPrice: (
            (sp.seaPrice.cogs * retailerMargin) /
            100
          ).toFixed(2),
          amazonprice: amazonprice,
          amazonmrp: amazonmrp,
          amazonMarketPlaceUrl: marketPlaceUrl,
          gstPer: pigst,
        },
      };
      return res.status(200).json(response);
    } else {
      return res.json({ msg: "no product found" });
    }
  } catch (err) {
    return res.status(500).json({ error: err.toString() });
  }
};

function getProductData(cond) {
  return new Promise((resolve, reject) => {
    try {
      let productData = Products.find({ _id: cond }).limit(1);
      resolve(productData);
    } catch (err) {
      return true;
    }
  });
}

/*exports.addToCart = async (req, res) => {
	 
	const { merchantId = "", accessStatus = 0,add= -1} = req.body;
	if (!merchantId) {
	  return res.status(400).json({
		InputParams: { ...req.body },
		Response: { status: 400, msg: "Invalid Input Param" },
	  });
    }
	
	const expectedSourceObj = { ios: 1, android: 2, web: 3 };
	const expectedSourceArr = Object.keys(expectedSourceObj);
	var merchant = await User.findOne({ _id: merchantId });
	if (!merchant) {
	  return res.status(400).json({
		InputParams: { ...req.body },
		Response: { status: 400, msg: "Invalid Merchant" },
	  });
	}
	const source = req.body.source;

	if (!source || !expectedSourceArr.includes(source.toLowerCase())) {
	  return res.status(400).json({
		InputParams: { ...req.body },
		Response: {
		  status: 400,
		  msg: "Invalid Input Param : source can not be null and should be one of value [android,ios,web]",
		},
	  });
	}
	//console.log("==========================",accessStatus);
	try {
		
	  if (accessStatus !=0) {
		const cartDetail = await Cart.findOne(
		  { merchantId: merchant._id },
		  { productDetails: 1 }
		);
		
		if (!cartDetail) {
		  return res.status(400).json({
			InputParams: { ...req.body },
			Response: { status: 400, msg: "Data Not Found" },
		  });
		}
		let data = await calculateCartPrice(cartDetail.productDetails);
		return res.status(200).json({
		  InputParams: { ...req.body },
		  Response: { status: 200, data: data },
		});
	  }

	   let cartData = JSON.parse(JSON.stringify(req.body.cartData));
	   if (!cartData) {
		return res.status(400).json({
		  InputParams: { ...req.body },
		  Response: { status: 400, msg: "Invalid Input Param" },
		});
	  }
	  if(add == 1){
		let cartDetail = await Cart.findOne(
		  { merchantId: merchant._id },
		  { productDetails: 1 }
		) || [];
		cartData = [...cartData, ...cartDetail?.productDetails || []]
	  }
	  if(add == 0){
		  let cartDetail = await Cart.findOne(
			  { merchantId: merchant._id },
			  { productDetails: 1 }
			) || [];
			cartData = [...cartDetail?.productDetails || []].filter(el=>{
		  return !cartData.find(elx=>elx.sku === el.sku);
		})
	  }
	  let tempCart = {};
	  cartData.forEach(el=>{
		tempCart[el.sku] = el;
	  })
	  cartData = [];
	  cartData = Object.values(tempCart);
	  delete tempCart;	  
	  const productIds = cartData.map((item) => {
		  return mongoose.Types.ObjectId(item.productId);
		});		
		const products = await Products.find(
			{ _id: { $in: productIds } },
			{ _id: 1, code: 1, varientArray: 1, quantity: 1, vd: 1 }
		);
		const availabeSku = {};		
		products.forEach((item) => {
			let skus = [];
			skus.push(item.code);
			if(item.varientArray){
				item.varientArray.forEach((ele) => {
					skus.push(ele.sku);
				});
			}
			availabeSku[item._id.toString()] = skus;
		});
		let cartInfo = [];
		let invalidDetail = [];
		cartData.forEach((ele, i) => {
		let item = availabeSku[ele.productId.trim()];
		let product = products.find((prod) => {
		  return prod._id.toString() == ele.productId.trim();
		});
		let quantity = 0;
		if(!product){
		  return false;
		}
		if (product.code == ele.sku) {
		  quantity = product.quantity;
		}
		if (product.vd && product.vd.length && !quantity) {
		  let variantData = product.vd.find((vdData) => {
			return vdData.sku == ele.sku;
		  });
		  if (variantData) {
			quantity = variantData.quantity;
		  }
		}
		if (
		  item &&
		  item.indexOf(ele.sku) != -1 &&
		  ele.productId &&
		  ele.quantity &&
		  quantity >= ele.quantity &&
		  ele.sku &&
		  product
		) {
		  cartInfo.push(ele);
		} else {
		  let message = "";
		  if (!ele.quantity || quantity < ele.quantity) {
			message = "The Quantity is not available of product";
		  }
		  if (!item || (item && item.indexOf(ele.sku) == -1) || !ele.productId) {
			message = "provided sku is invalid";
		  }
		  if (!ele.sku) {
			message = "Please provide product sku";
		  }
		  if (!product) {
			message = "Product is not find";
		  }
		  invalidDetail.push({ ...ele, msg: message,error:message.length > 1 ? 1 : 0 });
		}
	  });
  
	  if (invalidDetail.length) {
		return res.status(400).json({
		  InputParams: { ...req.body },
		  Response: {
			status: 400,
			data: invalidDetail,
			msg: "You have provided invalid detail",
		  },
		});
	  }
	  cartDetailData = cartInfo.map((item) => {
		item.source = source;
		return item;
	  });
  
	  const alreadyAddedToCart = await Cart.findOne({
		merchantId: merchant._id,
	  });
	  let deletedFromCart = [];
	  if (alreadyAddedToCart) {
		alreadyAddedToCart.productDetails.forEach((item) => {
		  let ispresent = cartDetailData.find((prod) => {
			return prod.productId == item.productId;
		  });
		  if (ispresent == -1 || !ispresent) {
			let obj = {};
			obj.merchantId = merchant._id;
			obj.updated_on = new Date();
			obj.created_on = new Date();
			obj.productDetail = item;
			item.is_removed = 1;
			deletedFromCart.push(obj);
		  }
		});
	  }
	  if (deletedFromCart.length) {
		await CartHistory.insertMany(deletedFromCart);
	  }
	  //console.log("==========================",[mongooseConstantValues.UPDATED_ON]);
	 const update = await Cart.updateOne(
		{ merchantId: merchant._id },
		{
		  $set: {
			productDetails: cartDetailData,
			merchantId: merchant._id,
			[mongooseConstantValues.UPDATED_ON]: new Date(),
		  },
		},
		{
		  upsert: true,
		}
	  );
	  
	  return res.status(200).json({
		InputParams: { ...req.body },
		Response: {
		  status: 200,
		  msg: "Product added in cart",
		  totalItems: cartDetailData.length,
		},
	  });
	} catch (err) {
	  return res.status(500).json({
		InputParams: { ...req.body },
		Response: { status: 500, msg: err.toString() },
	  });
	}
  };*/

exports.addToCart = async (req, res) => {
  const { merchantId = "", accessStatus = 0, add = -1 } = req.body;
  //console.log(req.body);
  if (!merchantId) {
    return res.status(400).json({
      InputParams: { ...req.body },
      Response: { status: 400, msg: "Invalid Input Param B" },
    });
  }
  const expectedSourceObj = { ios: 1, android: 2, web: 3 };
  const expectedSourceArr = Object.keys(expectedSourceObj);

  var merchant = await User.findOne({ _id: merchantId });
  if (!merchant) {
    return res.status(400).json({
      InputParams: { ...req.body },
      Response: { status: 400, msg: "Invalid Merchant N" },
    });
  }

  const source = req.body.source;
  if (!source || !expectedSourceArr.includes(source.toLowerCase())) {
    return res.status(400).json({
      InputParams: { ...req.body },
      Response: {
        status: 400,
        msg: "Invalid Input Param : source can not be null and should be one of value [android,ios,web]",
      },
    });
  }

  try {
    if (!accessStatus) {
      const cartDetail = await Cart.findOne(
        { merchantId: merchant._id },
        { productDetails: 1 }
      );
      //console.log(cartDetail);return false;
      if (!cartDetail) {
        return res.status(400).json({
          InputParams: { ...req.body },
          Response: { status: 400, msg: "Data Not Found" },
        });
      }
      let data = await calculateCartPrice(cartDetail.productDetails);
      return res.status(200).json({
        InputParams: { ...req.body },
        Response: { status: 200, data: data },
      });
    }
    let cartData = JSON.parse(JSON.stringify(req.body.cartData));

    if (!cartData) {
      return res.status(400).json({
        InputParams: { ...req.body },
        Response: { status: 400, msg: "Invalid Input Param K" },
      });
    }

    if (add == 1) {
      let cartDetail =
        (await Cart.findOne(
          { merchantId: merchant._id },
          { productDetails: 1 }
        )) || [];
      cartData = [...cartData, ...(cartDetail?.productDetails || [])];
    }

    if (add == 0) {
      let cartDetail =
        (await Cart.findOne(
          { merchantId: merchant._id },
          { productDetails: 1 }
        )) || [];
      cartData = [...(cartDetail?.productDetails || [])].filter((el) => {
        return !cartData.find((elx) => elx.sku === el.sku);
      });
    }
    let tempCart = {};
    cartData.forEach((el) => {
      tempCart[el.sku] = el;
    });
    cartData = [];
    cartData = Object.values(tempCart);
    delete tempCart;

    const productIds = cartData.map((item) => {
      return mongoose.Types.ObjectId(item.productId);
    });

    const products = await Products.find(
      { _id: { $in: productIds } },
      { _id: 1, code: 1, varientArray: 1, quantity: 1, vd: 1 }
    );
    const availabeSku = {};
    let skus = [];
    /*products.forEach((item) => {
		console.log(item);
      let skus = [];
      skus.push(item.code);
      item.varientArray.forEach((ele) => {
        skus.push(ele.sku);
      });
      availabeSku[item._id.toString()] = skus;
    });*/
    products.forEach((item) => {
      const subVariant = item.vd;

      if (item.quantity == 0) {
        return res.json({
          InputParams: { ...req.body },
          Response: { status: 400, msg: "Out of stock" },
        });
      } else if (subVariant) {
        cartData.map((items) => {
          subVariant.forEach((o) => {
            if (o.sku == items.sku) {
              if (Number(o.quantity) == 0) {
                return res.json({
                  InputParams: { varient_id: o.varient_id, sku: o.sku },
                  Response: { status: 400, msg: "Varient out of stock" },
                });
              }
            }
          });
        });
      }

      let skus = [];
      skus.push(item.code);
      if (item.varients) {
        item.varients.forEach((ele) => {
          skus.push(ele.sku);
        });
      }

      if (item.varientArray) {
        item.varientArray.forEach((ele) => {
          skus.push(ele.sku);
        });
      }
      availabeSku[item._id.toString()] = skus;
    });

    let cartInfo = [];
    let invalidDetail = [];
    cartData.forEach((ele, i) => {
      let item = availabeSku[ele.productId.trim()];
      let product = products.find((prod) => {
        return prod._id.toString() == ele.productId.trim();
      });
      let quantity = 0;
      if (!product) {
        return false;
      }
      if (product.code == ele.sku) {
        quantity = product.quantity;
      }
      if (product.vd && product.vd.length && !quantity) {
        let variantData = product.vd.find((vdData) => {
          return vdData.sku == ele.sku;
        });
        if (variantData) {
          quantity = variantData.quantity;
        }
      }
      if (
        item &&
        item.indexOf(ele.sku) != -1 &&
        ele.productId &&
        ele.quantity &&
        quantity >= ele.quantity &&
        ele.sku &&
        product
      ) {
        cartInfo.push(ele);
      } else {
        let message = "";
        if (!ele.quantity || quantity < ele.quantity) {
          message = "The Quantity is not available of product";
        }
        if (!item || (item && item.indexOf(ele.sku) == -1) || !ele.productId) {
          message = "provided sku is invalid";
        }
        if (!ele.sku) {
          message = "Please provide product sku";
        }
        if (!product) {
          message = "Product is not find";
        }
        invalidDetail.push({
          ...ele,
          msg: message,
          error: message.length > 1 ? 1 : 0,
        });
      }
    });

    if (invalidDetail.length) {
      return res.status(400).json({
        InputParams: { ...req.body },
        Response: {
          status: 400,
          data: invalidDetail,
          msg: "You have provided invalid detail",
        },
      });
    }
    cartDetailData = cartInfo.map((item) => {
      item.source = source;
      return item;
    });

    const alreadyAddedToCart = await Cart.findOne({
      merchantId: merchant._id,
    });
    let deletedFromCart = [];
    if (alreadyAddedToCart) {
      alreadyAddedToCart.productDetails.forEach((item) => {
        let ispresent = cartDetailData.find((prod) => {
          return prod.productId == item.productId;
        });
        if (ispresent == -1 || !ispresent) {
          let obj = {};
          obj.merchantId = merchant._id;
          obj.updated_on = new Date();
          obj.created_on = new Date();
          obj.productDetail = item;
          item.is_removed = 1;
          deletedFromCart.push(obj);
        }
      });
    }
    if (deletedFromCart.length) {
      await CartHistory.insertMany(deletedFromCart);
    }

    const update = await Cart.updateOne(
      { merchantId: merchant._id },
      {
        $set: {
          productDetails: cartDetailData,
          merchantId: merchant._id,
          [mongooseConstantValues.UPDATED_ON]: new Date(),
        },
      },
      {
        upsert: true,
      }
    );
    return res.status(200).json({
      InputParams: { ...req.body },
      Response: {
        status: 200,
        msg: "Product added in cart",
        totalItems: cartDetailData.length,
      },
    });
  } catch (err) {
    return res.status(500).json({
      InputParams: { ...req.body },
      Response: { status: 500, msg: err.toString() },
    });
  }
};

async function calculateCartPrice(productDetails) {
  const cproductIds = productDetails.map((item) => {
    return mongoose.Types.ObjectId(item.productId);
  });

  const allProducts = await Products.find({ _id: { $in: cproductIds } }).select(
    "_id subtitle name handle code price sp mrp quantity length width height weight hsn invoicedesc igst swcOnBcd margin bcd vd supplier_id imgUrl"
  );
  const products = {};
  allProducts.forEach((el) => {
    products[el._id.toString()] = el;
  });
  delete allProducts;
  let totalGst = 0;
  let totalPrice = 0;
  let totalShippingCost = 0;
  let totalQuntity = 0;
  const cartproducts = await Promise.all(
    productDetails.map((product) => {
      return new Promise(async (resolve, reject) => {
        let productObj = products[product.productId];
        if (!productObj) {
          return resolve({});
        }

        productObj = JSON.parse(JSON.stringify(productObj));
        if (productObj.vd && productObj.vd.length) {
          let vd = {};
          vd = productObj.vd.find((variant) => {
            return variant.sku == product.sku;
          });
          if (vd) {
            delete productObj.vd;
            vd.code = vd.sku;
            delete vd.sku;
            productObj = { ...productObj, ...vd };
          }
        }
        // console.log(productObj);

        let gst = 0;
        let sellingPrice = productObj.sp;
        productObj.price = productObj.sp;
        netAmount = productObj.sp;

        let shippingCost = 0;

        totalQuntity += parseInt(product.quantity);
        let total_amount = netAmount * parseInt(product.quantity);
        totalPrice += total_amount;
        let imgUrl = [];
        productObj.imgUrl.forEach((img) => {
          if (img) {
            img.src = `${process.env.S3_BUCKET_FILE_URL}/${img.src}`;
            imgUrl.push(img);
          }
        });
        productObj.imgUrl = imgUrl;
        productObj.cartQuantity = parseInt(product.quantity);
        resolve(productObj);
      });
    })
  )
    .then((data) => data)
    .catch((err) => {
      throw err;
    });

  return {
    products: cartproducts,
    grossAmount: totalPrice.toFixed(2),
    shippingCharges: 0,
    GST: 0,
    netAmount: totalPrice.toFixed(2),
    nproducts: productDetails.length,
    totalItems: totalQuntity,
  };
}
exports.getCategories = async (req, res) => {
  try {
    const expectedSourceObj = { ios: 1, android: 2, web: 3 };
    const expectedSourceArr = Object.keys(expectedSourceObj);
    const level = req.body.level || 1;
    const limit = req.body.limit ? parseInt(req.body.limit) : 1;
    const pageNumber = req.body.pageNumber ? parseInt(req.body.pageNumber) : 1;
    const type = parseInt(req.body.type);
    const source = req.body.source;
    const arrayType = [1, 2, 3, 4, 5];
    const results = {};

    if (!limit || !pageNumber || !type || !source) {
      // return messageBuilder(res, 400, { ...req.body }, "Invalid Input Param", { error: 1 });
    }
    if (!source || !expectedSourceArr.includes(source.toLowerCase())) {
      // return messageBuilder(res, 400, { ...req.body }, "Invalid Input Param : source can not be null and should be one of value [android,ios,web]", { error: 1 });
    }
    if (!arrayType.includes(type)) {
      // return messageBuilder(res, 400, { ...req.body }, "type should be in 1,2,3,4,5", { error: 1 });
    }

    const startIndex = (pageNumber - 1) * limit;
    const endIndex = pageNumber * limit;

    // let query = {code:{$exists:true},level:level};
    let query = {};
    // console.log(query);
    const categoryCount = await CategoryDesc.countDocuments(query).exec();

    results.count = categoryCount;
    results.startIndex = startIndex;
    if (endIndex < categoryCount) {
      //
      results.next = {
        page: pageNumber + 1,
        limit: limit,
      };
    }
    if (startIndex > 0) {
      results.previous = {
        page: pageNumber - 1,
        limit: limit,
      };
    }

    // console.log('category')
    // const resu = await CategoryDesc.distinct("cd_name", {})
    // console.log(resu)

    results.results = await CategoryDesc.find(query, {
      category_id: 1,
      cd_name: 1,
    })
      .sort({ _id: -1 })
      .limit(limit)
      .skip(startIndex)
      .exec();
    // return messageBuilder(res, 200, { ...req.body },"", results);

    return res.send({ error: 0, Response: results });
  } catch (err) {
    // return messageBuilder(res, 500, { ...req.body },err.toString(),{error:1});
    return res.send({ error: 1, Response: err.toString() });
  }
};
exports.websiteCategories = async (req, res) => {
  try {
    const categories = await WebsiteCategory.find({}, { name: 1, uri: 1 }).sort(
      { createdAt: -1 }
    );
    res.status(200).json({
      success: true,
      data: categories,
    });
  } catch (error) {
    res.status(500).json({
      success: false,
      message: "Failed to fetch website categories",
    });
  }
};

/************** */
const chars =
  "100XAXPMNO2IDDEWLZBTEVHV6OG1RDT6XQJX3MVF4AMADQ9TUVHGJFDBODLLLPTNNUUW";
const charsLength = chars.length;

function getRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

function generateRandomString(length) {
  let randStr = "";
  for (let i = 0; i < length; i++) {
    const idx = getRandomInt(0, charsLength - 1);
    randStr += chars.charAt(idx);
  }
  return randStr;
}

/************** */

exports.productCheckout = async (req, res) => {
  const {
    merchantId = "",
    isconfirm = 0,
    addressId,
    discountCode = "",
    returnUrl = "",
    orderNote = "",
  } = req.body;
  if (!merchantId || (!isconfirm && addressId && returnUrl)) {
    return res.status(400).json({
      InputParams: { ...req.body },
      Response: { status: 400, msg: "Invalid Input Param" },
    });
  }
  const expectedSourceObj = { ios: 1, android: 2, web: 3 };
  const expectedSourceArr = Object.keys(expectedSourceObj);

  var merchant = await User.findOne({ _id: merchantId });
  if (!merchant) {
    return res.status(400).json({
      InputParams: { ...req.body },
      Response: { status: 400, msg: "Invalid Merchant" },
    });
  }

  const source = req.body.source;
  if (!source || !expectedSourceArr.includes(source.toLowerCase())) {
    return res.status(400).json({
      InputParams: { ...req.body },
      Response: {
        status: 400,
        msg: "Invalid Input Param : source can not be null and should be one of value [android,ios,web]",
      },
    });
  }

  try {
    const cartDetail = await Cart.findOne({ merchantId: merchant._id });
    //   console.log("233445667",cartDetail)
    if (!cartDetail) {
      return res.status(400).json({
        InputParams: { ...req.body },
        Response: {
          status: 400,
          msg: "Data Not Found. Please add products to cart",
        },
      });
    }

    const userAddresses =
      (await Addresses.findOne(
        { _id: mongoose.Types.ObjectId(addressId) },
        { _id: 0 }
      )) || {};

    if (!userAddresses && isconfirm) {
      return res.status(400).json({
        InputParams: { ...req.body },
        Response: {
          status: 400,
          msg: "Address did not find.",
        },
      });
    }

    let priceRuleObj = null;
    let discountObj;
    /* apply discount code work */
    if (discountCode) {
      discountObj = await DiscountCode.findOne(
        { [mongooseConstantValues.DISCOUNT_CODE]: discountCode },
        { created_on: 0, updated_on: 0 }
      );
      if (discountObj) {
        priceRuleObj = await PriceRule.findOne(
          { id: discountObj[mongooseConstantValues.PRICE_RULE_ID] },
          { created_on: 0, updated_on: 0 }
        );
      }
    }

    const productIds = cartDetail.productDetails.map((item) => {
      return mongoose.Types.ObjectId(item.productId);
    });

    let customer = {};
    customer.email = merchant.email;
    customer.first_name = merchant.firstName;
    customer.last_name = merchant.lastName;
    customer.accepts_marketing = false;
    customer.created_at = new Date();
    customer.updated_on = new Date();
    customer.phone = merchant.phoneNo;
    customer.currency = "INR";
    customer.verified_email = false;
    customer.tax_exempt = false;
    customer.accepts_marketing = false;
    customer.default_address = userAddresses;

    const allProducts = await Products.find({
      _id: { $in: productIds },
    }).select(
      "_id subtitle name handle code sp price mrp quantity length width height weight hsn invoicedesc igst swcOnBcd margin bcd varientArray supplier_id imgUrl"
    );
    const products = {};
    allProducts.forEach((el) => {
      products[el._id.toString()] = el;
    });
    delete allProducts;
    let totalGst = 0;
    let totalPrice = 0;
    let totalShippingCost = 0;
    let totalDiscount = 0;
    let isCouponApplied = 0;
    const orderProducts = await Promise.all(
      cartDetail.productDetails.map((product) => {
        return new Promise(async (resolve, reject) => {
          let item = products[product.productId];
          if (!item) {
            return resolve({});
          }

          item = JSON.parse(JSON.stringify(item));
          let vd = {};
          if (item.vd && item.vd.length) {
            vd = item.vd.find((variant) => {
              return variant.sku == product.sku;
            });
            delete item.vd;
            vd.code = vd.sku;
            //delete vd.sku;
            item = { ...item, ...vd };
          }

          if (!item.igst) {
            item.igst = 18;
          }

          let gst = 0;

          //console.log(item.sp);
          let sellingPrice = product.sp;
          let discountAmount = 0;
          let discountData = parseFloat(sellingPrice);
          if (discountData) {
            sellingPrice = discountData.amountAfterDiscount || sellingPrice;
            discountAmount = discountData.discountAmount || 0;
            isCouponApplied = 1;
          }
          netAmount = item.sp;

          let total_amount = netAmount * parseInt(product.quantity);
          let total_gst = 0; //gst * parseInt(product.quantity);
          let total_shipping_cost = 0; // shippingCost * parseInt(product.quantity);
          totalPrice += total_amount;
          totalGst += total_gst;
          totalShippingCost += total_shipping_cost;
          let productObj = {};
          productObj.name = item.name;
          productObj.title = item.name;
          productObj.sku = item.code;
          productObj.quantity = product.quantity;
          productObj.requires_shipping = true;

          if (!isconfirm && item.imgUrl.length) {
            productObj.src = `${process.env.S3_BUCKET_FILE_URL}/${item.imgUrl[0].src}`;
            if (vd) {
              productObj.varient = vd.varient;
            }
          }

          productObj.price =  netAmount       // total_amount.toFixed(2);
          productObj.price_set = {
            shop_money: {
              amount: total_amount.toFixed(2),
              currency_code: "INR",
            },
          };

          productObj.total_gst = total_gst.toFixed(2);
          productObj.total_gst_set = {
            shop_money: {
              amount: total_gst.toFixed(2),
              currency_code: "INR",
            },
          };

          productObj.total_shipping_cost = total_shipping_cost.toFixed(2);
          productObj.total_shipping_cost_set = {
            shop_money: {
              amount: total_shipping_cost.toFixed(2),
              currency_code: "INR",
            },
          };

          productObj.id = item._id.toString();
          productObj.total_discount = discountAmount.toFixed(2);
          productObj.total_discount_set = {
            shop_money: {
              amount: discountAmount.toFixed(2),
              currency_code: "INR",
            },
          };
          totalDiscount += discountAmount;
          return resolve(productObj);
        });
      })
    )
      .then((data) => data)
      .catch((err) => {
        throw err;
      });

    let discountCodes = [];
    if (isCouponApplied) {
      discountCodes.push({
        target_type: priceRuleObj[mongooseConstantValues.TARGET_TYPE],
        type: "manual",
        value: priceRuleObj[mongooseConstantValues.VALUE],
        value_type: priceRuleObj[mongooseConstantValues.VALUE_TYPE],
        allocation_method: "across",
        target_selection: priceRuleObj[mongooseConstantValues.TARGET_SELECTION],
        title: priceRuleObj[mongooseConstantValues.TITLE],
        description: priceRuleObj[mongooseConstantValues.TITLE],
      });
    }
    let totalAmount = totalPrice + totalShippingCost + totalGst;
    customer.total_spent = totalAmount.toFixed(2);
    customer.total_gst = totalGst.toFixed(2);
    customer.total_shipping_cost = totalShippingCost.toFixed(2);
    customer.orders_count = orderProducts.length;
    customer.price = (totalDiscount + totalAmount).toFixed(2);
    customer.total_price_of_products = totalPrice.toFixed(2);
    customer.total_discount_amount = totalDiscount.toFixed(2);

    let orderObjFinal = {};
    const orderid = await getNextSequenceValue("legacyOrderid");

    const randomString = generateRandomString(15);
    let orders_serial = "100" + randomString + "1";


    let counter = 1;
    const today = new Date();
    const yyyy = today.getFullYear();
    const mm = String(today.getMonth() + 1).padStart(2, "0");
    const dd = String(today.getDate()).padStart(2, "0");
    const formattedDate = yyyy + mm + dd;
    const formattedCount = String(counter).padStart(3, "0");   
    let finalFormattedDate = formattedDate + formattedCount;
    counter++;

    let productDet = [];
    orderProducts.forEach((product) => {
      productDet.push({
        orderItemId: orderid,
        sku: product.sku,
        sellerSku: product.sku,
        quantity: product.quantity,
        quantityShipped: product.quantity,
        consumerPrice: (product.price * product.quantity).toFixed(2),
        title: product.name,
        productPrice: product.price,
        finalPrice: (product.price * product.quantity).toFixed(2),
      });
    });

    orderObjFinal.merchantId = merchantId;
    orderObjFinal.storeDomain = "Shopperr (Dropship)";
    orderObjFinal.legacyOrderid = orderid;
    orderObjFinal.orderSerial = orders_serial;
    orderObjFinal.mkpOrderId = "SD" + finalFormattedDate;
    orderObjFinal.shippingPhoneNumber = userAddresses.contact_number;
    orderObjFinal.recipientName = userAddresses.firstName;
    orderObjFinal.shippingAddress1 = userAddresses.address1;
    orderObjFinal.shippingAddress2 = userAddresses.address2;
    orderObjFinal.consumerPrice =  customer.total_price_of_products;   
    orderObjFinal.shippingCity = userAddresses.city;
    orderObjFinal.shippingState = userAddresses.province;
    orderObjFinal.shippingPostalCode = userAddresses.zip;
    orderObjFinal.shippingCountry = userAddresses.country;
    orderObjFinal.orderDetails = productDet;


    let orderObj = {
      customer,
      products: orderProducts,
      orderObjFinal,
    };
    if (!isconfirm) {
      return res.status(200).json({
        InputParams: { ...req.body },
        Response: { status: 200, data: orderObj },
      });
    }
    const sessionData = await Order(orderObjFinal);
    const savedData = await sessionData.save();
    let paymentObj = {
      orderItemId: savedData.legacyOrderid,
      orderAmount: totalAmount.toFixed(2),
      orderNote,
      returnUrl,
    };

    return res.status(200).json({
      InputParams: { ...req.body },
      Response: {
        status: 200,
        data: {
          orderId: savedData.legacyOrderid,
          orderDetails: orderObj,
          // orderObjFinal,
          paymentObj,
        },
        msg: "Your order generated sucessfully.",
      },
    });
  } catch (err) {
    console.log(err, "err");
    return res.status(500).json({
      InputParams: { ...req.body },
      Response: { status: 500, msg: err.Message },
    });
  }
};

// legacyorderid
exports.getOrderId = async (req, res) => {
  const { merchantId = "" } = req.body;
  if (!merchantId) {
    return res.status(400).json({
      InputParams: { ...req.body },
      Response: { status: 400, msg: "Invalid Input Param" },
    });
  }
  var merchant = await User.findOne({ _id: merchantId });
  if (!merchant) {
    return res.status(400).json({
      InputParams: { ...req.body },
      Response: { status: 400, msg: "Invalid Merchant" },
    });
  }
  try {
    const order = await Order.findOne({ merchantId: merchant._id }).sort({
      createdAt: -1,
    });

    if (!order) {
      return res.status(404).json({ message: "Order not found" });
    }
    res.json({
      status: 200,
      data: order,
    });
  } catch (error) {
    console.error(error);
    res.status(500).json({ message: "Server Error" });
  }
};

// Payment
exports.deductBalance = async (req, res) => {
  const { merchantId = "", amount = 0, orderSerial = "" } = req.body;
  if (!merchantId && !amount && orderSerial) {
    return res.status(400).json({
      InputParams: { ...req.body },
      Response: { status: 400, msg: "Invalid Input Param" },
    });
  }
  var merchant = await User.findOne({ _id: merchantId });
  if (!merchant) {
    return res.status(400).json({
      InputParams: { ...req.body },
      Response: { status: 400, msg: "Invalid Merchant" },
    });
  }
  try {
    let currentBalance = (
      await User.findOne({ _id: mongoose.Types.ObjectId(merchantId) })
    ).balance;

    currentBalance = parseFloat(currentBalance.toFixed());
    currentBalance -= amount;
    currentBalance = parseFloat(currentBalance.toFixed());
    await User.findByIdAndUpdate(
      merchantId,
      { balance: currentBalance },
      { new: true }
    );

    const billarrData = new BillHistory({
      merchantId: merchantId,
      pgOrderId: orderSerial,
      // systemId:order.systemId,
      billType: 1,
      billAmt: amount,
      beforeAmt: parseInt(currentBalance) + parseInt(amount),
      afterAmt: currentBalance,
      comment: "Order Payment",
      crDate: new Date(),
      modDate: new Date(),
    });

    
    await billarrData.save();

    const currentOrder = await Order.findOne({ orderSerial: orderSerial });
    currentOrder.pStatus = "PD"; // Payment Done
    currentOrder.paidDate = new Date();
    await currentOrder.save();

    res.json({
      status: 200,
      data: currentBalance,
    });
  } catch (error) {
    console.error(error);
    res.status(500).json({ message: "Server Error" });
  }
};

//cartupdate
exports.cartUpdate = async (req, res) => {
  const { merchantId = "" } = req.body;
  if (!merchantId) {
    return res.status(400).json({
      InputParams: { ...req.body },
      Response: { status: 400, msg: "Invalid Input Param" },
    });
  }
  // var merchant = await Cart.findOne({ _id: merchantId });
  // if (!merchant) {
  //   return res.status(400).json({
  //     InputParams: { ...req.body },
  //     Response: { status: 400, msg: "Invalid Merchant" },
  //   });
  // }
  try {
    let cartUpdate = await Cart.findOneAndUpdate(
      { merchantId: mongoose.Types.ObjectId(merchantId) },
      { $set: { productDetails: [] } },
      { new: true }
    );

    if (!cartUpdate) {
      return res.status(500).json({
        InputParams: { ...req.body },
        Response: { status: 500, msg: "Failed to update cart" },
      });
    }
    res.json({
      status: 200,
      data: cartUpdate,
    });
    //console.log(cartUpdate);
  } catch (error) {
    console.error(error);
    res.status(500).json({ message: "Server Error" });
  }
};

exports.getStates = async (req, res) => {
  try {
    return res.status(200).json({
      InputParams: { ...req.body },
      Response: {
        status: 200,
        data: {
          states: [
            "Andaman and Nicobar Islands",
            "Andhra Pradesh",
            "Arunachal Pradesh",
            "Assam",
            "Bihar",
            "Chandigarh",
            "Chhattisgarh",
            "Dadra and Nagar Haveli",
            "Daman and Diu",
            "Delhi",
            "Goa",
            "Gujarat",
            "Haryana",
            "Himachal Pradesh",
            "Jammu and Kashmir",
            "Jharkhand",
            "Karnataka",
            "Kerala",
            "Ladakh",
            "Lakshadweep",
            "Madhya Pradesh",
            "Maharashtra",
            "Manipur",
            "Meghalaya",
            "Mizoram",
            "Nagaland",
            "Odisha",
            "Puducherry",
            "Punjab",
            "Rajasthan",
            "Sikkim",
            "Tamil Nadu",
            "Telangana",
            "Tripura",
            "Uttar Pradesh",
            "Uttarakhand",
            "West Bengal",
          ],
        },
      },
    });
  } catch (e) {
    return res.status(500).json({
      InputParams: { ...req.body },
      Response: { status: 500, msg: e.toString() },
    });
  }
};

exports.userAddresses = async (req, res) => {
  try {
    const { merchantId = "", save = 0, addressId = "" } = req.body;
    if (!merchantId) {
      return res.json({
        InputParams: { ...req.body },
        Response: { status: 400, msg: "Invalid Input Param" },
      });
    }

    var merchant = await User.findOne({ _id: merchantId });
    if (!merchant) {
      return res.json({
        InputParams: { ...req.body },
        Response: {
          status: 400,
          msg: "Invalid Merchant.",
        },
      });
    }

    if (!save) {
      let userAddresses = await Addresses.find({ merchantId: merchant._id });
      if (!userAddresses || (userAddresses && !userAddresses.length)) {
        return res.json({
          InputParams: { ...req.body },
          Response: {
            status: 400,
            msg: "Data Not Found.",
          },
        });
      }
      userAddresses = JSON.parse(JSON.stringify(userAddresses));
      userAddresses = userAddresses.map(function (item) {
        item.email = merchant.email;
        return item;
      });
      return res.json({
        InputParams: { ...req.body },
        Response: { status: 200, data: userAddresses },
      });
    }

    if (!req.body.address) {
      return res.json({
        InputParams: { ...req.body },
        Response: { status: 400, msg: "Invalid Input Param" },
      });
    }
    if (req.body.address[mongooseConstantValues.IS_DEFAULT]) {
      await Addresses.updateMany(
        { merchantId: merchant._id },
        { $set: { [mongooseConstantValues.IS_DEFAULT]: 0 } }
      );
    }
    let address = {};
    let msg = "New Address Saved Successfully.";
    if (!addressId) {
      const sessionData = await Addresses({
        merchantId: merchant._id,
        ...req.body.address,
      });
      address = await sessionData.save();
    } else {
      address = await Addresses.findOne({
        _id: mongoose.Types.ObjectId(addressId),
      });
      if (!address) {
        return res.json({
          InputParams: { ...req.body },
          Response: {
            status: 400,
            msg: "Address did not find.",
          },
        });
      }
      await Addresses.updateMany(
        { _id: address._id },
        { $set: { ...req.body.address } }
      );
      msg = "Address Updated Successfully.";
    }
    return res.json({
      InputParams: { ...req.body },
      Response: { status: 200, data: { addressId: address._id }, msg: msg },
    });
  } catch (err) {
    console.log(err, "err");
    return res.json({
      InputParams: { ...req.body },
      Response: { status: 500, msg: err._message },
    });
  }
};

function calculateDiscount(priceRule, totalPrice, quantity) {
  if (!priceRule) {
    return false;
  }
  const subtotalRange =
    priceRule[mongooseConstantValues.PREREQUISITE_SUBTOTAL_RANGE];
  const quantityRange =
    priceRule[mongooseConstantValues.PREREQUISITE_QUANTITY_RANGE];
  const valueType = priceRule[mongooseConstantValues.VALUE_TYPE];
  const value = parseFloat(priceRule[mongooseConstantValues.VALUE]);
  let totalAmount = parseFloat(totalPrice * quantity);
  if (quantityRange && quantity > quantityRange) {
    return false;
  } else if (
    valueType == "fixed_amount" ||
    totalAmount < value ||
    (subtotalRange && totalAmount > subtotalRange)
  ) {
    return false;
  }
  totalAmount = parseFloat(totalPrice);

  let amountAfterDiscount = totalAmount;
  let discountAmount = 0;
  let perctVal = null;
  if (valueType == "fixed_amount") {
    amountAfterDiscount = totalAmount - value;
    discountAmount = value;
  } else {
    perctVal = Math.abs(parseFloat(value));
    discountAmount = (perctVal * totalAmount) / 100;
    amountAfterDiscount = totalAmount - discountAmount;
  }
  return {
    amountAfterDiscount,
    discountAmount,
    discountType: valueType,
    discountPercentValue: perctVal,
  };
}

// function cashFreeCheckout(merchant, params = {}) {
//   let orderCurrency = "INR";
//   let cf_request = {};
//   let buff = Buffer.from(params.returnUrl + "/" + params.orderId, "utf8");
//   cf_request["appId"] = process.env.CASHFREE_APP_ID;
//   cf_request["orderId"] = params.orderId;
//   cf_request["orderAmount"] = params.orderAmount;
//   cf_request["orderCurrency"] = orderCurrency;
//   cf_request["orderNote"] = params.orderNote;
//   cf_request["customerName"] = merchant.firstName + " " + merchant.lastName;
//   cf_request["customerPhone"] = merchant.phoneNo;
//   cf_request["customerEmail"] = merchant.email;
//   cf_request["notifyUrl"] = process.env.HTTP_URL + "/shopperrds/cashfreeNotify";
//   cf_request["returnUrl"] =
//     process.env.HTTP_URL +
//     "/shopperrds/order-complete?r_token=" +
//     buff.toString("base64");
//   cf_request["signature"] = cashfreeSignatureData(
//     cf_request,
//     process.env.CASHFREE_SECRATEKEY
//   );
//   cf_request["checkoutAction"] =
//     process.env.API_END_POINT + "/billpay/checkout/post/submit";
//   return cf_request;
// }

// function cashfreeSignatureData(data, secretKey) {
//   //for checkout
//   let keys = Object.keys(data);
//   keys.sort();
//   let signatureData = "";
//   keys.forEach((key) => {
//     signatureData += key + data[key];
//   });

//   return crypto
//     .createHmac("sha256", secretKey)
//     .update(signatureData)
//     .digest("base64");
// }

exports.orderSucess = async (req, res) => {
  const {
    orderId = "",
    paymentMode = "",
    txStatus = "",
    orderAmount = "",
    type = "",
    referenceId = "",
    txMsg = "",
    txTime = "",
    stage = "",
  } = req.body;

  let signature = req.body.signature;
  let signatureData =
    orderId +
    orderAmount +
    referenceId +
    txStatus +
    paymentMode +
    txMsg +
    txTime;
  let finalSignature = crypto
    .createHmac("sha256", process.env.CASHFREE_SECRATEKEY)
    .update(signatureData)
    .digest("base64");

  if (
    !orderId ||
    !paymentMode ||
    !orderAmount ||
    !referenceId ||
    signature !== finalSignature
  ) {
    return res.status(400).json({
      InputParams: { ...req.body },
      Response: { status: 400, msg: "Invalid Params" },
    });
  }

  try {
    const productOrder = await Order.findOne({ orderId: orderId });
    if (!productOrder) {
      return res.status(400).json({
        InputParams: { ...req.body },
        Response: { status: 400, msg: "Order did not find" },
      });
    }
    let orderObj = await Order.findOne({
      $and: [{ orderId: orderId }, { storeDomain: productOrder.storeDomain }],
    });
    if (orderObj) {
      await Order.updateOne(
        {
          $and: [
            { orderId: orderId },
            { storeDomain: productOrder.storeDomain },
          ],
        },
        {
          $set: {
            pStatus: txStatus == "SUCCESS" ? "paid" : "unpaid",
            updated_on: new Date().toISOString(),
          },
        }
      );

      return res.status(200).json({
        InputParams: { ...req.body },
        Response: { status: 200, msg: "Order updated successfully." },
      });
    }

    let merchant = await User.findOne({
      _id: mongoose.Types.ObjectId(productOrder.merchantId),
    });
    if (!merchant) {
      return res.status(400).json({
        InputParams: { ...req.body },
        Response: { status: 400, msg: "Order did not find" },
      });
    }
    orderObj = await saveOrder(productOrder, merchant, {
      paymentMode,
      paymentDetails: {
        txStatus,
        orderAmount,
        type,
        txMsg,
        txTime: new Date(txTime),
        referenceId,
        stage,
      },
    });

    await updatePaymentStatus(
      productOrder,
      txStatus == "SUCCESS" ? "paid" : "unpaid"
    );
    return res.status(200).json({
      InputParams: { ...req.body },
      Response: {
        status: 200,
        msg: "Order Placed Successfully.",
        orderId: orderObj._id,
      },
    });
  } catch (e) {
    return res.json({
      InputParams: { ...req.body },
      Response: { status: 500, msg: e.message },
    });
  }
};

exports.applyDiscountcode = async (req, res) => {
  const { code = "", merchantId = "" } = req.body;
  if (!code || !merchantId) {
    return res.status(400).json({
      InputParams: { ...req.body },
      Response: { status: 400, msg: "Invalid Params" },
    });
  }

  try {
    var merchant = await User.findOne({
      _id: mongoose.Types.ObjectId(merchantId),
    });
    if (!merchant) {
      return res.status(400).json({
        InputParams: { ...req.body },
        Response: { status: 400, msg: "Invalid Merchant" },
      });
    }

    const cartDetail = await Cart.findOne(
      { merchantId: mongoose.Types.ObjectId(merchantId) },
      { productDetails: 1 }
    );
    if (!cartDetail) {
      return res.status(400).json({
        InputParams: { ...req.body },
        Response: { status: 400, msg: "Data Not Found" },
      });
    }

    let discountCode = await DiscountCode.findOne(
      { [mongooseConstantValues.DISCOUNT_CODE]: code },
      { created_on: 0, updated_on: 0 }
    );

    if (
      !discountCode ||
      (discountCode && !discountCode[mongooseConstantValues.IS_ACTIVE])
    ) {
      return res.status(400).json({
        InputParams: { ...req.body },
        Response: { status: 400, msg: "Discount code is invalid." },
      });
    }
    let priceRule = await PriceRule.findOne(
      { id: discountCode[mongooseConstantValues.PRICE_RULE_ID] },
      { created_on: 0, updated_on: 0 }
    );
    if (!priceRule) {
      return res.status(400).json({
        InputParams: { ...req.body },
        Response: { status: 400, msg: "Discount code is invalid." },
      });
    }
    const cartData = await calculateCartPrice(cartDetail.productDetails);

    if (!cartData) {
      return res.status(400).json({
        InputParams: { ...req.body },
        Response: { status: 400, msg: "Please add product to cart." },
      });
    }
    let isApplied = 0;
    cartData.products.forEach((item) => {
      if (calculateDiscount(priceRule, item.sp, item.cartQuantity)) {
        isApplied = 1;
      }
    });

    if (!isApplied) {
      return res.status(400).json({
        InputParams: { ...req.body },
        Response: { status: 400, msg: "Discount code is invalid." },
      });
    }

    return res.status(200).json({
      InputParams: { ...req.body },
      Response: { status: 200, msg: "discount code is valid." },
    });
  } catch (e) {
    return res.status(500).json({
      InputParams: { ...req.body },
      Response: { status: 500, msg: e.message },
    });
  }
};

async function saveOrder(productOrder, merchant, params = {}) {
  let address = {};
  let {
    address1 = null,
    phone = null,
    city = null,
    zip = null,
    province = null,
    country = null,
    address2 = null,
    company = null,
    latitude = null,
    longitude = null,
    country_code = null,
    province_code = null,
  } = productOrder.customer.default_address;
  address = {
    address1,
    phone,
    city,
    zip,
    province,
    country,
    address2,
    company,
    latitude,
    longitude,
    country_code,
    province_code,
  };
  address.first_name = productOrder.customer.default_address.firstName;
  address.last_name = productOrder.customer.default_address.lastName;
  address.name = address.first_name.trim() + " " + address.last_name.trim();
  let billingAddress = address;
  if (productOrder.customer.default_address.sa) {
    billingAddress = productOrder.customer.default_address.sa;
    billingAddress.first_name =
      billingAddress?.first_name ?? address.first_name;
    billingAddress.last_name = billingAddress?.last_name ?? address.last_name;
  }
  productOrder.customer.default_address = address;
  return await new Order({
    merchantId: merchant._id,
    storeDomain: productOrder.storeDomain,
    orderId: productOrder.orderId,
    price: productOrder.customer.total_spent,
    products: productOrder.products,
    orderNumber: productOrder.orderId,
    customer: productOrder.customer,
    created_on: new Date().toISOString(),
    fulfillmentStatus: null,
    shippingLines: null,
    paymentDetails: null,
    billingAddress: address,
    shippingAddress: address,
    taxLines: [],
    discountCodes: productOrder.discountCodes,
    note: "",
    paymentMethod: [params.paymentMode, params.paymentDetails],
    pStatus: "paid",
  })
    .save()
    .then((order) => order)
    .catch((err) => {
      throw err;
    });
}
async function updatePaymentStatus(productOrder, status) {
  return Order.updateOne(
    {
      $and: [
        { orderId: productOrder.orderId },
        { storeDomain: productOrder.storeDomain },
      ],
    },
    {
      $set: {
        status: status,
        updated_on: new Date().toISOString(),
      },
    }
  );
}

exports.confirmOrder = async (req, res) => {
  const {
    orderId = "",
    paymentMode = "",
    txStatus = "",
    orderAmount = "",
    type = "",
    referenceId = "",
    txMsg = "",
    txTime = "",
    stage = "",
  } = req.body;

  let signature = req.body.signature;
  let signatureData =
    orderId +
    orderAmount +
    referenceId +
    txStatus +
    paymentMode +
    txMsg +
    txTime;
  let finalSignature = crypto
    .createHmac("sha256", process.env.CASHFREE_SECRATEKEY)
    .update(signatureData)
    .digest("base64");

  let r_token = req.query.r_token || "";
  let bufferObj = Buffer.from(r_token, "base64");
  let url = bufferObj.toString("utf8");

  if (
    !orderId ||
    !paymentMode ||
    !orderAmount ||
    !referenceId ||
    signature !== finalSignature
  ) {
    return res.redirect(url);
  }

  try {
    const productOrder = await Order.findOne({ orderId: orderId });
    if (!productOrder) {
      return res.redirect(url);
    }

    let merchant = await User.findOne({
      _id: mongoose.Types.ObjectId(productOrder.merchantId),
    });
    if (!merchant) {
      return res.redirect(url);
    }
    let orderObj = await Order.findOne({
      $and: [{ orderId: orderId }, { storeDomain: productOrder.storeDomain }],
    });

    if (orderObj) {
      return res.redirect(url);
    }

    orderObj = await saveOrder(productOrder, merchant, {
      paymentMode,
      paymentDetails: {
        txStatus,
        orderAmount,
        type,
        txMsg,
        txTime: new Date(txTime),
        referenceId,
        stage,
      },
    });

    await updatePaymentStatus(
      productOrder,
      txStatus == "SUCCESS" ? "paid" : "unpaid"
    );

    await Cart.updateOne(
      { merchantId: merchant._id },
      {
        $set: {
          productDetails: [],
          [mongooseConstantValues.UPDATED_ON]: new Date(),
        },
      }
    );
    return res.redirect(url);
  } catch (e) {
    return res.redirect(url);
  }
};
