const {
  FavoriteFolderListings,
  FavoriteFolders,
  Listings,
  ListingImages,
  ListingReviews,
} = require('../../models');
const { formatTimestamp } = require('../../util');

exports.createFavorite = async (req, res, next) => {
  const { userId } = req;
  const { folderId, name, listingId } = req.body;

  try {
    const favoriteListingExists = await FavoriteFolderListings.findOne({
      where: {
        user_id: userId,
        listing_id: listingId,
      },
    });

    if (favoriteListingExists) {
      return res.status(400).json({
        success: false,
        message: 'Listing already exists in favorites',
      });
    }

    if (folderId && listingId) {
      await createFavoriteListing(userId, folderId, listingId);
    } else {
      let folder;
      if (name) {
        folder = await createFavoriteFolder(userId, name, !listingId);
      }

      if (listingId) {
        await createFavoriteListing(userId, folder?.id, listingId);
      }
    }

    return res.status(201).json({
      success: true,
      message: `Favorite${name && !listingId && !folderId ? ' Folder' : ''} created successfully`,
    });
  } catch (error) {
    next(error);
  }
};

exports.updateFavoriteFolder = async (req, res, next) => {
  const { userId } = req;
  const { name } = req.body;
  const { folderId } = req.params;

  try {
    const folder = await FavoriteFolders.findOne({
      where: {
        id: folderId,
        user_id: userId,
      },
    });

    if (!folder) {
      return res.status(404).json({
        success: false,
        message: 'Folder not found',
      });
    }

    folder.folder_name = name;

    await folder.save();

    return res.status(200).json({
      success: true,
      message: 'Folder updated successfully',
    });
  } catch (error) {
    next(error);
  }
};

exports.updateFavoriteListing = async (req, res, next) => {
  const { userId } = req;
  const { folderId, listingId } = req.body;

  try {
    const favoriteListing = await FavoriteFolderListings.findOne({
      where: {
        user_id: userId,
        listing_id: listingId,
      },
    });

    if (!favoriteListing) {
      return res.status(404).json({
        success: false,
        message: 'Favorite not found',
      });
    }

    const folder = await FavoriteFolders.findByPk(folderId);

    if (!folder) {
      return res.status(404).json({
        success: false,
        message: 'Folder not found',
      });
    }

    favoriteListing.folder_id = folderId;

    await favoriteListing.save();

    return res.status(200).json({
      success: true,
      message: 'Favorite updated successfully',
    });
  } catch (error) {
    next(error);
  }
};

exports.getFavorites = async (req, res, next) => {
  const { userId } = req;

  try {
    const favorites = await FavoriteFolders.findAll({
      where: {
        user_id: userId,
      },
      include: [
        {
          model: FavoriteFolderListings,
          as: 'favoriteListings',
          attributes: ['id', 'createdAt'],
          include: [
            {
              model: Listings,
              as: 'listing',
              attributes: [
                'id',
                'name',
                'description',
                'price',
                'location',
                'address_city',
                'address_country',
              ],
              include: [
                {
                  model: ListingImages,
                  as: 'listingImages',
                  attributes: ['image'],
                },
                {
                  model: ListingReviews,
                  as: 'listingReviews',
                  attributes: ['rating'],
                },
              ],
            },
          ],
        },
      ],
      attributes: ['id', ['folder_name', 'folderName'], 'updatedAt'],
      order: [['updatedAt', 'DESC']],
    });

    const data = favorites.map((favorite) => {
      const favoriteListings = favorite.favoriteListings
        .sort((a, b) => b.createdAt - a.createdAt)
        .map((item) => {
          const listingImages = formatListingImages(item?.listing?.listingImages);

          const listing = item.listing
            ? {
                id: item.listing.id,
                name: item.listing.name,
                description: item.listing.description,
                price: item.listing.price,
                location: item.listing.location,
                addressCity: item.listing.address_city,
                addresCountry: item.listing.address_country,
                averageRating: calculateAverageRating(item.listing?.listingReviews),
                listingImages,
              }
            : null;

          const data = {
            id: item.id,
            createdAt: formatTimestamp(item.createdAt),
            listing,
          };

          return data;
        });

      return {
        ...favorite.dataValues,
        favoriteListings,
        updatedAt: formatTimestamp(favorite.updatedAt),
      };
    });

    return res.status(200).json({
      success: true,
      data,
    });
  } catch (error) {
    next(error);
  }
};

exports.deleteFavoriteListing = async (req, res, next) => {
  const { userId } = req;
  const { listingId } = req.params;

  try {
    const deleted = await FavoriteFolderListings.destroy({
      where: {
        listing_id: listingId,
        user_id: userId,
      },
    });

    if (!deleted) {
      return res.status(404).json({
        success: false,
        message: 'Favorite not found',
      });
    }

    return res.status(200).json({
      success: true,
      message: 'Favorite deleted successfully',
    });
  } catch (error) {
    next(error);
  }
};

exports.deleteFolder = async (req, res, next) => {
  const { userId } = req;
  const { folderId } = req.params;

  try {
    const deleted = await FavoriteFolders.destroy({
      where: {
        id: folderId,
        user_id: userId,
      },
    });

    if (!deleted) {
      return res.status(404).json({
        success: false,
        message: 'Folder not found',
      });
    }

    await FavoriteFolderListings.destroy({
      where: {
        folder_id: folderId,
      },
    });

    return res.status(200).json({
      success: true,
      message: 'Folder deleted successfully',
    });
  } catch (error) {
    next(error);
  }
};

exports.getFolders = async (req, res, next) => {
  const { userId } = req;

  try {
    const folders = await FavoriteFolders.findAll({
      where: {
        user_id: userId,
      },
      include: [
        {
          model: FavoriteFolderListings,
          as: 'favoriteListings',
          attributes: ['id', 'createdAt'],
          include: [
            {
              model: Listings,
              as: 'listing',
              attributes: ['id'],
              include: [
                {
                  model: ListingImages,
                  as: 'listingImages',
                  attributes: ['image'],
                },
              ],
            },
          ],
        },
      ],
      attributes: ['id', 'folder_name'],
      order: [['updatedAt', 'DESC']],
    });

    const data = folders.map((favorite) => {
      const images = favorite.favoriteListings.map((item) => {
        const image = formatListingImages(item?.listing?.listingImages?.[0]?.image);

        return { image };
      });

      return {
        id: favorite.id,
        folderName: favorite.folder_name,
        images,
      };
    });

    return res.status(200).json({
      success: true,
      data,
    });
  } catch (error) {
    next(error);
  }
};

exports.getFolder = async (req, res, next) => {
  const { userId } = req;
  const { folderId } = req.params;

  try {
    let folder = await FavoriteFolders.findOne({
      where: {
        user_id: userId,
        id: folderId,
      },
      include: [
        {
          model: FavoriteFolderListings,
          as: 'favoriteListings',
          attributes: ['id'],
          include: [
            {
              model: Listings,
              as: 'listing',
              attributes: [
                'id',
                'name',
                'description',
                'price',
                'location',
                'address_city',
                'address_country',
              ],
              include: [
                {
                  model: ListingImages,
                  as: 'listingImages',
                  attributes: ['image'],
                },
                {
                  model: ListingReviews,
                  as: 'listingReviews',
                  attributes: ['rating'],
                },
              ],
            },
          ],
        },
      ],
      attributes: ['id', ['folder_name', 'folderName']],
    });

    if (!folder) {
      return res.status(404).json({
        success: false,
        message: 'Folder not found',
      });
    }

    folder = folder.get({ plain: true });

    const favoriteListings = folder?.favoriteListings?.map((item) => {
      const listingImages = formatListingImages(item?.listing?.listingImages);

      const averageRating = calculateAverageRating(item?.listing?.listingReviews);

      const listing = item?.listing
        ? {
            ...item?.listing,
            averageRating,
            listingImages,
          }
        : null;

      const data = {
        ...item,
        listing,
      };

      return data;
    });

    const folderData = {
      ...folder,
    };

    folderData.favoriteListings = favoriteListings;

    return res.status(200).json({
      success: true,
      data: folderData,
    });
  } catch (error) {
    next(error);
  }
};

async function createFavoriteFolder(userId, name, errorIfExist = false) {
  const folderExists = await FavoriteFolders.findOne({
    where: {
      user_id: userId,
      folder_name: name,
    },
  });

  if (folderExists) {
    if (errorIfExist) {
      throw new Error('Folder already exists');
    }

    folderExists.changed('updatedAt', true);
    await folderExists.save();

    return folderExists;
  }

  return await FavoriteFolders.create({
    user_id: userId,
    folder_name: name,
  });
}

async function createFavoriteListing(userId, folderId, listingId) {
  const listing = await Listings.findByPk(listingId);
  if (!listing) {
    throw new Error('Listing not found');
  }

  let favoriteFolder;
  if (!folderId) {
    favoriteFolder = await createFavoriteFolder(userId, listing?.address_city ?? 'N/A', false);
    favoriteFolder.changed('updatedAt', true);
    await favoriteFolder.save();

    folderId = favoriteFolder.id;
  } else {
    favoriteFolder = await FavoriteFolders.findByPk(folderId);
    if (!favoriteFolder) {
      throw new Error('Folder not found');
    }
  }

  const listingExists = await FavoriteFolderListings.findOne({
    where: {
      user_id: userId,
      folder_id: favoriteFolder.id,
      listing_id: listingId,
    },
  });

  if (listingExists) {
    throw new Error('Listing already exists in this folder');
  }

  return await FavoriteFolderListings.create({
    user_id: userId,
    folder_id: folderId,
    listing_id: listingId,
  });
}

function calculateAverageRating(data) {
  if (!data?.length) {
    return null;
  }
  const sum = data.reduce((acc, review) => {
    const rating = review.dataValues ? review.dataValues.rating : review.rating;
    return acc + rating;
  }, 0);
  return (sum / data.length).toFixed(2);
}

function formatListingImages(data) {
  if (!data) return null;

  if (!Array.isArray(data)) {
    return `${process.env.BASE_URL}/public/host-uploads/${data}`;
  }

  return data?.map((image) => ({
    ...image?.dataValues,
    image: `${process.env.BASE_URL}/public/host-uploads/${image?.dataValues?.image ?? image?.image}`,
  }));
}
