const Products = require("../../models/Products");
const ProductMenu = require("../../models/ProductMenu");
const Seller = require("../../models/Seller");
const SellersCompany = require("../../models/SellersCompany");
const DetailProductReviews = require("../../models/DetailProductReviews");
const ShopRadius = require("../../models/ShopRadius");
const Sequelize = require("sequelize");
const errorHandler = require("../../util/errorHandler");
const {
  getSellersCompanyExcludes,
} = require("../../helpers/model-excludes-attributes/excludes");
const {
  sellerProductImageBuilder,
  sellerImageBuilder,
} = require("../../helpers/image-src-builder");
const MicrositeHandles = require("../../models/MicrositeHandles");
const { isBase64 } = require("../../helpers/common");
const ProductImages = require("../../models/ProductImages");

exports.findAllSellersCompany = async (req, res, next) => {
  try {
    const {
      keyword = "",
      globalSearch = "false",
      lng = 0,
      lat = 0,
      mid = null,
      productLimit = 1,
    } = req.query;
    const isGlobalSearch = globalSearch.toLowerCase() === "true";

    let isLoggedIn = req.loggedIn;
    let shopRadius;

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

    const finalLat = isLoggedIn ? req.latitude : lat;
    const finalLng = isLoggedIn ? req.longitude : lng;
    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}
    `;

    let whereClause = {
      status: true,
    };

    if (keyword !== "") {
      whereClause[Sequelize.Op.and] = [
        {
          [Sequelize.Op.or]: [
            Sequelize.where(
              Sequelize.fn("LOWER", Sequelize.col("company_name")),
              {
                [Sequelize.Op.like]: `%${keyword.toLowerCase()}%`,
              }
            ),
            Sequelize.where(Sequelize.fn("LOWER", Sequelize.col("shop_name")), {
              [Sequelize.Op.like]: `%${keyword.toLowerCase()}%`,
            }),
            // Sequelize.where(
            //   Sequelize.fn("LOWER", Sequelize.col("products.menu")),
            //   {
            //     [Sequelize.Op.like]: `%${keyword.toLowerCase()}%`,
            //   }
            // ),
            // Sequelize.where(
            //   Sequelize.fn("LOWER", Sequelize.col("products.description")),
            //   {
            //     [Sequelize.Op.like]: `%${keyword.toLowerCase()}%`,
            //   }
            // ),
          ],
        },
      ];
    }

    if (isLoggedIn && !isGlobalSearch) {
      if (!whereClause[Sequelize.Op.and]) {
        whereClause[Sequelize.Op.and] = [];
      }
      whereClause[Sequelize.Op.and].push(Sequelize.literal(radiusLiteralSql));
    }

    if (lng > 0 && lat > 0 && !isGlobalSearch) {
      if (!whereClause[Sequelize.Op.and]) {
        whereClause[Sequelize.Op.and] = [];
      }
      whereClause[Sequelize.Op.and].push(Sequelize.literal(radiusLiteralSql));
    }

    if (mid) {
      whereClause.microsite_handle_id = mid;
    }

    let { page } = req.pagination;
    let sellerCompanyLimit =
      parseInt(req.pagination.sellerCompanyLimit, 10) || 15;

    const offset = (page - 1) * sellerCompanyLimit;

    const _allSellerCompanies = await SellersCompany.findAndCountAll({
      where: whereClause,
      include: [
        {
          model: Products,
          as: "products",
          where: { available: 1 },
          required: false,
          attributes: [
            "id",
            "menu",
            "photo",
            "description",
            "seller_dashboard_src",
          ],
          include: [
            {
              model: ProductImages,
              as: "product_images",
              required: false,
            },
          ],
          limit: parseInt(productLimit),
        },
        {
          model: MicrositeHandles,
          as: "microsite_handle",
          where: { status: true },
          required: false,
        },
      ],
      limit: sellerCompanyLimit,
      offset: offset,
    });

    const allSellerCompanies = _allSellerCompanies.rows;
    const allSellerCompaniesCount = _allSellerCompanies.count;

    if (!allSellerCompanies || allSellerCompanies.length === 0) {
      return res
        .status(404)
        .json({ success: false, message: "No seller companies found" });
    }

    const count = allSellerCompanies.length;
    const totalPages = Math.ceil(count / sellerCompanyLimit);

    // const paginatedSellerCompanies = allSellerCompanies.slice(
    //   (page - 1) * sellerCompanyLimit,
    //   page * sellerCompanyLimit
    // );

    const response = await Promise.all(
      allSellerCompanies.map(async (seller) => {
        const {
          id,
          shop_name,
          shop_location_municipality,
          shop_location_province,
          url_handle,
          microsite_handle,
          company_name,
          description,
          address,
          seller_logo,
          seller_banner_image,
          emailAddress,
          seller_dashboard_src,
          isParentCompany,
          longitude,
          latitude,
        } = seller;

        const products = seller.products;
        const productCount = products.length;

        const productResponse = await Promise.all(
          products.map(async (product) => {
            const {
              id,
              menu,
              photo,
              description,
              product_images,
              seller_dashboard_src,
            } = product;

            let thumbnailImage = "";
            const productImages = [];

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

                productImages.push(i);
              }
            }

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

              if (mainImage) {
                thumbnailImage = mainImage.url;
              }
            } else {
              thumbnailImage = sellerProductImageBuilder(
                photo,
                seller_dashboard_src
              );
            }
            const rating = await DetailProductReviews.findAll({
              where: { product_id: id },
              attributes: [
                [
                  Sequelize.fn(
                    "COALESCE",
                    Sequelize.fn("AVG", Sequelize.col("rate")),
                    0
                  ),
                  "average_rating",
                ],
              ],
            });

            return {
              id,
              menu,
              description,
              photo: thumbnailImage,
              // rating: rating[0] ? rating[0].dataValues.average_rating : 0.0,
            };
          })
        );

        return {
          id,
          name:
            shop_name === "" || shop_name === null ? company_name : shop_name,
          description,
          location: `${
            shop_location_municipality ? `${shop_location_municipality}, ` : ""
          } ${shop_location_province ? shop_location_province : ""}`,
          address,
          logo: seller_logo
            ? sellerImageBuilder(seller_logo, seller_dashboard_src)
            : null,
          banner: seller_banner_image
            ? sellerImageBuilder(seller_banner_image, seller_dashboard_src)
            : null,
          emailAddress,
          url_handle,
          microsite_handle,
          isParentCompany,
          longitude,
          latitude,
          products: productResponse,
        };
      })
    );

    res.status(200).json({
      success: true,
      lng,
      lat,
      response,
      total_companies: count,
      total_pages: totalPages,
      currentPage: page,
      pagination: {
        page,
        pageSize: sellerCompanyLimit,
        totalPages: Math.ceil(allSellerCompaniesCount / sellerCompanyLimit),
        totalCount: allSellerCompaniesCount,
      },
    });
  } catch (error) {
    next(error);
  }
};

exports.getAllProductsBySellerCompany = async (req, res, next) => {
  try {
    const { sellerId: seller_id } = req.params; // This can be seller ID or url handle
    const { page, limit } = req.pagination;
    const { category, nameSort, priceSort, productKeyword = "" } = req.query;
    let productMenuId;

    let decodedKeyword = productKeyword;

    if (productKeyword && isBase64(productKeyword)) {
      try {
        decodedKeyword = decodeURIComponent(
          Buffer.from(productKeyword, "base64").toString("utf-8")
        );
      } catch (err) {
        decodedKeyword = productKeyword;
      }
    }

    if (
      nameSort !== "asc" &&
      nameSort !== "desc" &&
      nameSort !== "" &&
      nameSort !== undefined &&
      nameSort !== null
    ) {
      return res.status(200).json({
        success: false,
        msg: "Invalid name sorting strategy ['asc', 'desc']",
      });
    }
    if (
      priceSort !== "asc" &&
      priceSort !== "desc" &&
      priceSort !== "" &&
      priceSort !== undefined &&
      priceSort !== null
    ) {
      return res.status(200).json({
        success: false,
        msg: "Invalid pricing sorting strategy ['asc', 'desc']",
      });
    }

    if (category) {
      productMenuId = await ProductMenu.findOne({
        where: Sequelize.where(
          Sequelize.fn("LOWER", Sequelize.col("product_menu")),
          {
            [Sequelize.Op.like]: `%${category.toLowerCase()}%`,
          }
        ),
      });
    }

    const productCategoryId = productMenuId ? productMenuId.id : "";

    const sellersCompanyDetail = await SellersCompany.findOne({
      where: {
        [Sequelize.Op.or]: [{ id: seller_id }, { url_handle: seller_id }],
      },
      attributes: {
        exclude: getSellersCompanyExcludes(),
      },
      include: [
        {
          model: MicrositeHandles,
          as: "microsite_handle",
          where: { status: true },
          required: false,
        },
      ],
    });

    if (!sellersCompanyDetail) {
      return res.status(200).json({
        success: false,
        msg: "Invalid url handle or seller id",
      });
    }

    const modifiedSellerCompanyDetail = sellersCompanyDetail
      ? {
          id: sellersCompanyDetail.id,
          seller_name:
            sellersCompanyDetail.shop_name === "" ||
            sellersCompanyDetail.shop_name === null
              ? sellersCompanyDetail.company_name
              : sellersCompanyDetail.shop_name,
          seller_description: sellersCompanyDetail.company_description,
          address: sellersCompanyDetail.address,
          location: `${sellersCompanyDetail.shop_location_municipality}, ${sellersCompanyDetail.shop_location_province}`,
          latitude: sellersCompanyDetail.latitude,
          longitude: sellersCompanyDetail.longitude,
          owner: sellersCompanyDetail.owner,
          seller_logo: sellersCompanyDetail.seller_logo
            ? sellerImageBuilder(
                sellersCompanyDetail.seller_logo,
                sellersCompanyDetail.seller_dashboard_src
              )
            : null,
          seller_banner_img: sellersCompanyDetail.seller_banner_image
            ? sellerImageBuilder(
                sellersCompanyDetail.seller_banner_image,
                sellersCompanyDetail.seller_dashboard_src
              )
            : null,
          emailAddress: sellersCompanyDetail.emailAddress,
          url_handle: sellersCompanyDetail.url_handle,
          microsite_handle: sellersCompanyDetail.microsite_handle,
        }
      : null;


    const whereClause = {
      seller_id: sellersCompanyDetail.id,
      available: 1,
      product_status: true,
      [Sequelize.Op.or]: [{ parent_id: null }, { parent_id: "" }],
    };

    const orderClause = [];
    if (nameSort) orderClause.push(["menu", nameSort]);
    if (priceSort) orderClause.push(["price", priceSort]);

    if (productCategoryId) {
      whereClause.menu_id = productCategoryId;
    }

    if (decodedKeyword !== "") {
      whereClause.menu = Sequelize.where(
        Sequelize.fn("LOWER", Sequelize.col("menu")),
        {
          [Sequelize.Op.like]: `%${decodedKeyword.toLowerCase()}%`,
        }
      );
    }

    const products = await Products.findAll({
      where: whereClause,
      include: [
        {
          model: DetailProductReviews,
          as: "product_review",
          required: false,
        },
        {
          model: ProductImages,
          as: "product_images",
        },
      ],
      order: orderClause,
    });

    let _products;

    const paginatedProducts = products.slice((page - 1) * limit, page * limit);
    _products = paginatedProducts;
    const count = products.length;
    const totalPages = Math.ceil(count / limit);
    if (!products || products.count === 0) _products = [];

    const sortedProduct = await Promise.all(
      _products.map(
        async ({
          id,
          menu_id,
          menu,
          variant,
          description,
          price,
          discounted_price,
          photo,
          quantity_limit,
          stocks,
          seller_dashboard_src,
          available,
          sku,
          product_variation_id,
          product_sub_variation_id,
          product_images,
        }) => {
          const productImages = [];

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

              productImages.push(i);
            }
          }

          const rating = await DetailProductReviews.findAll({
            where: {
              product_id: id,
            },
            attributes: [
              [
                Sequelize.fn(
                  "COALESCE",
                  Sequelize.fn("AVG", Sequelize.col("rate")),
                  0
                ),
                "average_rating",
              ],
            ],
          });

          const response = {
            id: id,
            menuId: menu_id,
            menu: menu,
            variant: variant,
            description: description,
            price: price,
            seller_dashboard_src,
            discountedPrice: discounted_price,
            photo: sellerProductImageBuilder(photo, seller_dashboard_src),
            product_images: productImages,
            quantityLimit: quantity_limit,
            stocks: stocks,
            available: available,
            sku: sku,
            productVariationId: product_variation_id,
            productSubVariationId: product_sub_variation_id,
            rating: rating ? rating[0] : 0.0,
          };

          if (response.photo === null || response.photo === "") {
            const thumbnailImage = productImages.find((p) => p.is_thumbnail);
            if (thumbnailImage) {
              response.photo = thumbnailImage.url;
            }
          }
          return response;
        }
      )
    );

    res.status(200).json({
      success: true,
      seller_company: modifiedSellerCompanyDetail,
      sortedProduct,
      total_pages: totalPages,
      total_products: count,
      currentPage: page,
    });
  } catch (err) {
    next(err);
  }
};

exports.getMicrositeHandleByName = async (req, res, next) => {
  const { microsite_handle } = req.query;

  if (!microsite_handle)
    return res
      .status(400)
      .json({ success: false, message: "Invalid microsite handle." });

  try {
    const micrositeHandle = await MicrositeHandles.findOne({
      where: {
        microsite: microsite_handle,
        status: true,
      },
    });

    if (!micrositeHandle)
      return res
        .status(400)
        .json({ success: false, message: "Invalid microsite handle." });

    res.status(200).json({ success: true, microsite: micrositeHandle });
  } catch (err) {
    next(err);
  }
};

exports.searchModuleSellerCompany = async (req, res, next) => {
  try {
    const sellersCompany = await SellersCompany.findAll({
      attributes: ["id", "company_name", "shop_name", "address"],
    });

    const modifiedSellersCompany = sellersCompany.map((sellerCompany) => ({
      id: sellerCompany.id,
      name:
        sellerCompany.shop_name === "" || sellerCompany.shop_name === null
          ? sellerCompany.company_name
          : sellerCompany.shop_name,
      display_name:
        sellerCompany.shop_name === "" || sellerCompany.shop_name === null
          ? `${sellerCompany.company_name} (${sellerCompany.address})`
          : `${sellerCompany.shop_name} (${sellerCompany.address})`,
    }));
    res
      .status(200)
      .json({ success: true, sellers_company: modifiedSellersCompany });
  } catch (err) {
    next(err);
  }
};
