const UserCart = require("../models/UserCart");
const SellersCompany = require("../models/SellersCompany");
const SellerWarehouse = require("../models/SellersCompanyWarehouses");
const Fees = require("../models/Fees");
const computeDistance = require("./computeDistance");
const sellerShippingComputeDistance = require("./sellerShippingComputeDistance");
const Customers = require("../models/Customers");
const ProductMenu = require("../models/ProductMenu");
const Products = require("../models/Products");

const CryptoJS = require("crypto-js");
const axios = require("axios");

axios.defaults.baseURL = "https://rest.lalamove.com";

const API_KEY = "pk_prod_c2370054bfcb5ab034c124eb7a6633a5";
const SECRET =
  "sk_prod_RuGAm2kMf0wfRurErghWvvJa1BQrX37R6M89Pwf6XudACuV6Nrhwld5CDPhdgCcs";

const SDKClient = require("@lalamove/lalamove-js");
const { filterAndFormatPhones } = require("./common");

exports.shippingService = async (customer_id, customLatLng) => {
  try {
    // Variables
    let customerLat;
    let customerLng;
    let sellerLat;
    let sellerLng;
    let computationMethod;

    let standardShippingFee = 0;
    let standardShippingRate = 0;
    let standardShippingFlagdown = 0;

    let hardwareShippingFee = 0;
    let hardwareShippingRate = 0;
    let hardwareShippingFlagdown = 0;

    let sellerShippingSettings;
    let enabledSellerShipping = false;

    let sellerSmallItemPerKm = 0;
    let sellerSmallItemFixed = 0;
    let sellerBigItemPerKm = 0;
    let sellerBigItemFixed = 0;
    let sellerSmallItemFlagdownPerKm = 0;
    let sellerBigItemFlagdownPerKm = 0;

    let sellerInhouseSmallPerKm = 0;
    let sellerInhouseSmallFixed = 0;
    let sellerInhouseBigPerKm = 0;
    let sellerInhouseBigFixed = 0;
    let sellerInhouseFlagdownBigPerKm = 0;
    let sellerInhouseFlagdownSmallPerKm = 0;

    // Default Shipping Fee
    const defaultShippingPromise = Fees.findOne({
      where: {
        fee: "Shipping",
      },
    });

    //   Customer Details
    const customerPromise = Customers.findByPk(customer_id);

    // Customer User Cart
    const userCartPromise = UserCart.findAll({
      where: {
        user_id: customer_id,
      },
      attributes: [
        "menu_id",
        "quantity",
        "price",
        "has_sub_variation_chosen",
        "product_variation_id",
        "product_sub_variation_id",
      ],
      include: [
        {
          model: Products,
          as: "product",
          include: [
            {
              model: SellersCompany,
              as: "seller",
              include: [
                {
                  model: SellerWarehouse,
                  as: "seller_warehouse",
                },
              ],
            },
            {
              model: ProductMenu,
              as: "menus",
            },
          ],
        },
      ],
    });

    const [defaultShipping, customer, userCart] = await Promise.all([
      defaultShippingPromise,
      customerPromise,
      userCartPromise,
    ]);

    const defaultShippingFee = parseFloat(defaultShipping.amount);

    customerLat = customer.latitude;
    customerLng = customer.longitude;

    if (customLatLng && customLatLng.longitude && customLatLng.latitude) {
      customerLat = customLatLng.latitude;
      customerLng = customLatLng.longitude;
    }

    if (!customerLat || !customerLng) {
      return { error: "Customer location not set." };
    }

    if (userCart && Array.isArray(userCart) && userCart.length === 0) {
      return { error: "User has no product added in the cart." };
    }

    const sellers = userCart
      .map((cartItem) => cartItem.product?.seller?.id)
      .filter(Boolean);
    const sellerIds = new Set(sellers);
    const differentMerchants = sellerIds.size > 1;

    const productCategory = userCart[0].product.menus.size_category;
    const userCartSellerDetails = userCart[0].product.seller;

    sellerLat = userCartSellerDetails.latitude;
    sellerLng = userCartSellerDetails.longitude;

    // Seller Shipping Fee Computation Method
    computationMethod = userCartSellerDetails.computation_method;

    // Seller Shipping Fee Fees
    sellerSmallItemPerKm = userCartSellerDetails.small_item_fee_perkm;
    sellerSmallItemFixed = userCartSellerDetails.small_item_fee_fixed;

    sellerBigItemPerKm = userCartSellerDetails.big_item_fee_perkm;
    sellerBigItemFixed = userCartSellerDetails.big_item_fee_fixed;

    sellerSmallItemFlagdownPerKm = userCartSellerDetails.flagdownSmallKM;
    sellerBigItemFlagdownPerKm = userCartSellerDetails.flagdownBigKM;

    // Use warehouse location of seller if available
    if (
      userCartSellerDetails &&
      userCartSellerDetails.warehouse_id &&
      userCartSellerDetails.seller_warehouse
    ) {
      sellerLat = userCartSellerDetails.seller_warehouse.latitude;
      sellerLng = userCartSellerDetails.seller_warehouse.longitude;
      computationMethod =
        userCartSellerDetails.seller_warehouse.computation_method;

      // Get the Fees from Warehouse since it has warehouse defined.
      sellerSmallItemPerKm =
        userCartSellerDetails.seller_warehouse.small_item_perkm;
      sellerSmallItemFixed =
        userCartSellerDetails.seller_warehouse.small_item_fixed;
      sellerBigItemPerKm =
        userCartSellerDetails.seller_warehouse.big_item_perkm;
      sellerBigItemPerKm =
        userCartSellerDetails.seller_warehouse.big_item_perkm;
      sellerBigItemFixed =
        userCartSellerDetails.seller_warehouse.big_item_fixed;

      sellerSmallItemFlagdownPerKm =
        userCartSellerDetails.seller_warehouse.small_item_flagdown_perkm;
      sellerBigItemFlagdownPerKm =
        userCartSellerDetails.seller_warehouse.big_item_flagdown_perkm;
    }

    if (!sellerLat || !sellerLng) {
      return { error: "Seller/Warehouse location not set." };
    }

    // NOTES:
    // Seller Shipping is disabled when there are two merchants being computed.
    // Seller Computation Method between two different sellers should be the same, thus getting the computation method of the first seller from the list is being used.

    // Computation Method, Shipping Fees should come from Warehouse Location if there is warehouse set from the seller.

    let inHousePerKmValues = [];
    let inHouseFixedValues = [];
    if (!differentMerchants) {
      const sellerShippingRadius = userCartSellerDetails.seller_radius_shipping;
      sellerShippingSettings = await sellerShippingComputeDistance(
        customerLat,
        customerLng,
        sellerLat,
        sellerLng,
        sellerShippingRadius
      );

      if (sellerShippingSettings) {
        enabledSellerShipping =
          sellerShippingSettings.seller_shipping_within_range &&
          userCartSellerDetails.enabled_seller_shipping &&
          userCartSellerDetails.seller_type !== "Internal";
      }

      sellerInhouseSmallPerKm = userCartSellerDetails.in_house_small_per_km;
      sellerInhouseSmallFixed = userCartSellerDetails.in_house_small_fixed;
      sellerInhouseBigPerKm = userCartSellerDetails.in_house_big_per_km;
      sellerInhouseBigFixed = userCartSellerDetails.in_house_big_fixed;
      sellerInhouseFlagdownSmallPerKm =
        userCartSellerDetails.in_house_flagdown_small_km;
      sellerInhouseFlagdownBigPerKm =
        userCartSellerDetails.in_house_flagdown_big_km;

      inHousePerKmValues = [
        sellerInhouseSmallPerKm,
        sellerInhouseBigPerKm,
        sellerInhouseFlagdownSmallPerKm,
        sellerInhouseFlagdownBigPerKm,
      ];

      inHouseFixedValues = [sellerInhouseSmallFixed, sellerInhouseBigFixed];
    }
    const distance = await computeDistance(
      `${customerLat},${customerLng}`,
      `${sellerLat},${sellerLng}`
    );

    switch (computationMethod.toLowerCase()) {
      case "perkm":
        const hasInvalidInHousePerKmValue = inHousePerKmValues.some(
          (val) => val === null || val === undefined || val === "" || isNaN(val)
        );

        if (hasInvalidInHousePerKmValue) {
          enabledSellerShipping = false;
        }
        if (productCategory.toLowerCase() === "small item") {
          // Standard Shipping
          standardShippingFee =
            parseFloat(sellerSmallItemPerKm) * distance +
            parseFloat(sellerSmallItemFlagdownPerKm);
          standardShippingRate = parseFloat(sellerSmallItemPerKm);
          standardShippingFlagdown = parseFloat(sellerSmallItemFlagdownPerKm);

          // Seller Shipping
          hardwareShippingFee =
            parseFloat(sellerInhouseSmallPerKm) * distance +
            parseFloat(sellerInhouseFlagdownSmallPerKm);
          hardwareShippingRate = parseFloat(sellerInhouseSmallPerKm);
          hardwareShippingFlagdown = parseFloat(
            sellerInhouseFlagdownSmallPerKm
          );

          if (
            !hardwareShippingFee ||
            !hardwareShippingFee ||
            !distance ||
            !sellerInhouseFlagdownSmallPerKm
          )
            enabledSellerShipping = false;
        } else if (productCategory.toLowerCase() === "large item") {
          // Standard Shipping
          standardShippingFee =
            parseFloat(sellerBigItemPerKm) * distance +
            parseFloat(sellerBigItemFlagdownPerKm);
          standardShippingRate = parseFloat(sellerBigItemPerKm);
          standardShippingFlagdown = parseFloat(sellerBigItemFlagdownPerKm);

          // Seller Shipping
          hardwareShippingFee =
            parseFloat(sellerInhouseBigPerKm) * distance +
            parseFloat(sellerInhouseFlagdownBigPerKm);
          hardwareShippingRate = parseFloat(sellerInhouseBigPerKm);
          hardwareShippingFlagdown = parseFloat(sellerInhouseFlagdownBigPerKm);
        }
        break;
      case "fixed":
        const hasInvalidInHouseFixedValue = inHouseFixedValues.some(
          (val) => val === null || val === undefined || val === "" || isNaN(val)
        );

        if (hasInvalidInHouseFixedValue) {
          enabledSellerShipping = false;
        }
        if (productCategory.toLowerCase() === "small item") {
          // Standard Shipping
          standardShippingFee =
            parseFloat(sellerSmallItemFixed) +
            parseFloat(sellerSmallItemFlagdownPerKm);
          standardShippingRate = parseFloat(sellerSmallItemFixed);
          standardShippingFlagdown = parseFloat(sellerSmallItemFlagdownPerKm);

          // Seller Shipping
          hardwareShippingFee =
            parseFloat(sellerInhouseSmallFixed) +
            parseFloat(sellerInhouseFlagdownSmallPerKm);
          hardwareShippingRate = parseFloat(sellerInhouseSmallFixed);
          hardwareShippingFlagdown = parseFloat(
            sellerInhouseFlagdownSmallPerKm
          );
        } else if (productCategory.toLowerCase() === "large item") {
          // Standard Shipping
          standardShippingFee =
            parseFloat(sellerBigItemFixed) +
            parseFloat(sellerBigItemFlagdownPerKm);
          standardShippingRate = parseFloat(sellerBigItemFixed);
          standardShippingFlagdown = sellerBigItemFlagdownPerKm;

          // Seller Shipping
          hardwareShippingFee =
            parseFloat(sellerInhouseBigFixed) +
            parseFloat(sellerInhouseFlagdownBigPerKm);
          hardwareShippingRate = parseFloat(sellerInhouseBigFixed);
          hardwareShippingFlagdown = parseFloat(sellerInhouseFlagdownBigPerKm);
        }
        break;
      default:
        standardShippingFee = defaultShippingFee;
        standardShippingRate = defaultShippingFee;
        standardShippingFlagdown = 0;
        break;
    }

    return {
      standard_shipping_fee: {
        fee: Math.round(standardShippingFee),
        total_distance_to_seller: parseFloat(distance),
        computation_method: computationMethod,
        product_category: productCategory,
        rate: parseFloat(standardShippingRate),
        flagdown: parseFloat(standardShippingFlagdown),
      },
      hardware_shipping: {
        fee: enabledSellerShipping ? parseFloat(hardwareShippingFee) : 0,
        total_distance_to_seller: enabledSellerShipping
          ? parseFloat(distance)
          : 0,
        computation_method: computationMethod,
        product_category: productCategory,
        rate: enabledSellerShipping ? parseFloat(hardwareShippingRate) : 0,
        flagdown: enabledSellerShipping
          ? parseFloat(hardwareShippingFlagdown)
          : 0,
        enabled_hardware_shipping: enabledSellerShipping,
        hardware_shipping_configs: sellerShippingSettings,
      },
    };
  } catch (err) {
    throw err;
  }
};

exports.lalamoveShipping = async ({
  customerLng,
  customerLat,
  customerAdd,
  sellerLng,
  sellerLat,
  sellerAdd,
  orderSubTotal,
}) => {
  try {
    const time = new Date().getTime().toString();
    const region = "PH";
    const method = "POST";
    const path = "/v3/quotations";
    const vehicle = orderSubTotal > 100000 ? "10WHEEL_TRUCK" : "TRUCK550";

    const reqBody = {
      data: {
        serviceType: vehicle,
        specialRequests: [],
        language: "en_PH",
        stops: [
          {
            coordinates: {
              lat: customerLat.toString(),
              lng: customerLng.toString(),
            },
            address: customerAdd,
          },
          {
            coordinates: {
              lat: sellerLat.toString(),
              lng: sellerLng.toString(),
            },
            address: sellerAdd,
          },
        ],
      },
    };

    const rawSignature = `${time}\r\n${method}\r\n${path}\r\n\r\n${JSON.stringify(
      reqBody
    )}`;
    const SIGNATURE = CryptoJS.HmacSHA256(rawSignature, SECRET).toString();
    const startTime = new Date().getTime();

    const lalamoveResponse = await axios.post(path, reqBody, {
      headers: {
        "Content-type": "application/json; charset=utf-8",
        Authorization: `hmac ${API_KEY}:${time}:${SIGNATURE}`,
        Accept: "application/json",
        Market: region,
      },
    });

    if (lalamoveResponse && lalamoveResponse.status === 201) {
      return lalamoveResponse.data;
    }

    return null;
  } catch (error) {
    return null;
  }
};

exports.lalamoveBooking = async ({
  quotationId,
  senderStopId,
  senderName,
  senderPhone,
  recipientStopId,
  recipientName,
  recipientPhone,
  recipientRemarks = "",
  reference_no = "",
}) => {
  try {
    const sdkClient = new SDKClient.ClientModule(
      new SDKClient.Config(API_KEY, SECRET, "sandbox")
    );

    const _senderPhone = filterAndFormatPhones([senderPhone]);
    const _recipientPhone = filterAndFormatPhones([recipientPhone]);

    const orderPayload = SDKClient.OrderPayloadBuilder.orderPayload()
      .withIsPODEnabled(true)
      .withQuotationID(quotationId)
      .withSender({
        stopId: senderStopId,
        name: senderName,
        phone: `+${_senderPhone[0]}`,
      })
      .withMetadata({
        order_reference_no: reference_no,
      })
      .withRecipients([
        {
          stopId: recipientStopId,
          name: recipientName,
          phone: `+${_recipientPhone[0]}`,
        },
      ])
      .build();
    const order = await sdkClient.Order.create("PH", orderPayload);

    return order;
  } catch (err) {
    console.log("--ERROR: ", err);
  }
};
