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");

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.
    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;
    }
    const distance = await computeDistance(`${customerLat},${customerLng}`, `${sellerLng},${sellerLat}`);

    switch (computationMethod.toLowerCase()) {
      case "perkm":
        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);
        } 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":
        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;
  }
};
