const Products = require("../models/Products");
const SellersCompany = require("../models/SellersCompany");
const ShopRadius = require("../models/ShopRadius");
const Sequelize = require("sequelize");
const {
  sellerProductImageBuilder,
  sellerImageBuilder,
} = require("./image-src-builder");
const ProductImages = require("../models/ProductImages");

exports.searchService = async (req, queryObj) => {
  const isLoggedIn = req.loggedIn;

  let shopRadius;
  const radius = await ShopRadius.findAll();
  if (radius.length > 0) {
    const { dataValues } = radius[0];
    if (dataValues) {
      shopRadius = dataValues.radius;
    }
  }

  if (queryObj.location === "all") {
  }

  let finalLat, finalLng;
  if (queryObj.location === "near") {
    finalLat = isLoggedIn ? req.latitude : queryObj.latitude;
    finalLng = isLoggedIn ? req.longitude : queryObj.longitude;
  }

  let whereClause = {
    product_status: true,
    available: true,
    [Sequelize.Op.or]: [{ parent_id: null }, { parent_id: "" }],
  };

  // Order
  let orderClause = [];
  if (queryObj.order && queryObj.order !== "") {
    switch (queryObj.order) {
      case "price_asc":
        orderClause.push([
          Sequelize.literal(
            "CASE WHEN discounted_price IS NOT NULL AND discounted_price > 0 THEN discounted_price ELSE price END"
          ),
          "ASC",
        ]);
        break;
      case "price_desc":
        orderClause.push([
          Sequelize.literal(
            "CASE WHEN discounted_price IS NOT NULL AND discounted_price > 0 THEN discounted_price ELSE price END"
          ),
          "DESC",
        ]);
        break;
      case "name_asc":
        orderClause.push([Sequelize.fn("LOWER", Sequelize.col("menu")), "ASC"]);
        break;
      case "name_desc":
        orderClause.push([
          Sequelize.fn("LOWER", Sequelize.col("menu")),
          "DESC",
        ]);
        break;
      default:
        orderClause.push(["id", "DESC"]);
    }
  }

  // Search Keyword
  if (queryObj.keyword && queryObj.keyword !== "") {
    const sanitize = (term) =>
      term
        .replace(/[^a-zA-Z0-9\s\/\.\-]/g, "")
        .trim()
        .toLowerCase();

    const keywordTerms = queryObj.keyword
      .split(/\s+/)
      .map(sanitize)
      .filter(Boolean);

    const scoreFragments = keywordTerms
      .map(
        (term) => `
  (CASE 
    WHEN LOWER(menu) = '${term}' THEN 10
    WHEN LOWER(menu) LIKE '${term}%' THEN 7
    WHEN LOWER(menu) LIKE '%${term}%' THEN 5
    WHEN LOWER(description) LIKE '${term}%' THEN 3
    WHEN LOWER(description) LIKE '%${term}%' THEN 1
    ELSE 0
  END)
`
      )
      .join(" + ");
    const relevanceScore = Sequelize.literal(`(${scoreFragments})`);

    const orConditions = keywordTerms.map((term) => ({
      [Sequelize.Op.or]: [
        Sequelize.where(Sequelize.fn("LOWER", Sequelize.col("menu")), {
          [Sequelize.Op.like]: `%${term}%`,
        }),
        Sequelize.where(Sequelize.fn("LOWER", Sequelize.col("description")), {
          [Sequelize.Op.like]: `%${term}%`,
        }),
      ],
    }));

    whereClause[Sequelize.Op.and] = orConditions;

    if (!queryObj.order) {
      orderClause.push([relevanceScore, "DESC"]);
    }
  }

  // Location
  if (queryObj.location === "near") {
    const radiusLiteralSql = `
    6371 * 2 * ASIN(
        SQRT(
          POWER(SIN((${finalLat} - latitude) * PI()/180 / 2), 2) +
          COS(${finalLat} * PI()/180) * COS(latitude * PI()/180) *
          POWER(SIN((${finalLng} - longitude) * PI()/180 / 2), 2)
        )
      ) <= ${shopRadius}
    `;
    if (!whereClause[Sequelize.Op.and]) {
      whereClause[Sequelize.Op.and] = [];
    }
    whereClause[Sequelize.Op.and].push(Sequelize.literal(radiusLiteralSql));
  }

  // Price Min & Max
  if (
    queryObj.priceMin &&
    queryObj.priceMin !== "" &&
    queryObj.priceMax &&
    queryObj.priceMax !== ""
  ) {
    const min = parseFloat(queryObj.priceMin);
    const max = parseFloat(queryObj.priceMax);

    if (!whereClause[Sequelize.Op.and]) {
      whereClause[Sequelize.Op.and] = [];
    }

    whereClause[Sequelize.Op.and].push({
      [Sequelize.Op.or]: [
        {
          price: {
            [Sequelize.Op.between]: [min, max],
          },
        },
        {
          discounted_price: {
            [Sequelize.Op.between]: [min, max],
          },
        },
      ],
    });
  }

  //Category
  if (queryObj.category && queryObj.category !== "") {
    if (!whereClause[Sequelize.Op.and]) {
      whereClause[Sequelize.Op.and] = [];
    }

    whereClause[Sequelize.Op.and].push({
      menu_id: queryObj.category,
    });
  }

  // Seller
  if (queryObj.seller && queryObj.seller !== "") {
    if (!whereClause[Sequelize.Op.and]) {
      whereClause[Sequelize.Op.and] = [];
    }

    whereClause[Sequelize.Op.and].push({
      seller_id: queryObj.seller,
    });
  }

  // Availability
  if (queryObj.availability && queryObj.availability !== "") {
    if (!whereClause[Sequelize.Op.and]) {
      whereClause[Sequelize.Op.and] = [];
    }

    if (queryObj.availability === "inStock") {
      whereClause[Sequelize.Op.and].push({
        stocks: {
          [Sequelize.Op.gt]: 0,
        },
      });
    }
    if (queryObj.availability === "outOfStock") {
      whereClause[Sequelize.Op.and].push({
        stocks: {
          [Sequelize.Op.lt]: 1,
        },
      });
    }
  }

  try {
    const offset = (queryObj.page - 1) * queryObj.limit;
    const productQuery = await Products.findAndCountAll({
      where: whereClause,
      order: orderClause,
      include: [
        {
          model: SellersCompany,
          as: "seller",
          required: true,
        },
        {
          model: ProductImages,
          as: "product_images",
          required: false,
        },
      ],
      limit: queryObj.limit,
      offset,
      distinct: true
    });

    const productCount = productQuery.count;
    const productResult = productQuery.rows;

    const totalPages = Math.ceil(productCount / queryObj.limit);

    const responseProduct = productResult.map((product) => {
      const productImages = [];

      let thumbnailImage = "";
      if (
        product.product_images &&
        Array.isArray(product.product_images) &&
        product.product_images.length > 0
      ) {
        for (const image of product.product_images) {
          const i = {
            id: image.id,
            url: sellerProductImageBuilder(image.file_name, true),
            is_thumbnail: image.is_thumbnail,
          };

          productImages.push(i);
        }
      }

      if (product.photo === null || product.photo === "") {
        const mainImage = productImages.find((p) => p.is_thumbnail);

        if (mainImage) {
          thumbnailImage = mainImage.url;
        }
      } else {
        thumbnailImage = sellerProductImageBuilder(
          product.photo,
          product.seller_dashboard_src
        );
      }

      return {
        id: product.id,
        menuId: product.menu_id,
        menu: product.menu,
        variant: product.variant,
        description: product.description,
        price: product.price,
        seller_dashboard_src: product.seller_dashboard_src,
        discountedPrice: product.discounted_price,
        photo: thumbnailImage,
        quantityLimit: product.quantity_limit,
        stocks: product.stocks,
        available: product.available,
        sku: product.sku,
        productVariationId: product.product_variation_id,
        productSubVariationId: product.product_sub_variation_id,
        seller: {
          id: product.seller.id,
          seller_name:
            product.seller.shop_name === "" || product.seller.shop_name === null
              ? product.seller.company_name
              : product.seller.shop_name,
          seller_description: product.seller.company_description,
          address: product.seller.address,
          location: `${product.seller.shop_location_municipality}, ${product.seller.shop_location_province}`,
          latitude: product.seller.latitude,
          longitude: product.seller.longitude,
          owner: product.seller.owner,
          seller_logo: product.seller.seller_logo
            ? sellerImageBuilder(
                product.seller.seller_logo,
                product.seller.seller_dashboard_src
              )
            : null,
          seller_banner_img: product.seller.seller_banner_image
            ? sellerImageBuilder(
                product.seller.seller_banner_image,
                product.seller.seller_dashboard_src
              )
            : null,
          emailAddress: product.seller.emailAddress,
          url_handle: product.seller.url_handle,
          microsite_handle: product.seller.microsite_handle,
        },
      };
    });

    const response = {
      results: responseProduct,
      pagination: {
        page: queryObj.page,
        pageSize: queryObj.limit,
        totalPages: totalPages,
        totalCount: productCount,
      },
    };
    return response;
  } catch (err) {
    console.log(err);
    return { error: err };
  }
};

exports.sellersCompanyService = async () => {
  try {
    const sellerCompanies = await SellersCompany.findAll({
      where: {
        status: true,
      },
      attributes: ["id", "company_name", "shop_name", "address"],
    });

    const mappedSellerCompanies = sellerCompanies.map((sellerCompany) => {
      const trimmedAddress = sellerCompany.address
        ? sellerCompany.address.slice(0, 20)
        : "";

      return {
        id: sellerCompany.id,
        name:
          sellerCompany.shop_name && sellerCompany.shop_name !== ""
            ? `${sellerCompany.shop_name} (${trimmedAddress}...)`
            : `${sellerCompany.company_name} (${trimmedAddress}...)`,
      };
    });

    return { results: mappedSellerCompanies };
  } catch (err) {
    return { error: err };
  }
};
