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 {
		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.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', '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,
								address: formatAddress(item.listing),
								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 { favoriteListingId } = req.params;

	try {
		await FavoriteFolderListings.destroy({
			where: {
				id: favoriteListingId,
				user_id: userId,
			},
		});

		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 {
		await FavoriteFolders.destroy({
			where: {
				id: folderId,
				user_id: userId,
			},
		});

		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 {
		const 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', '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',
			});
		}

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

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

			const listing = item?.listing
				? {
						id: item.listing.id,
						name: item.listing.name,
						description: item.listing.description,
						price: item.listing.price,
						address,
						averageRating,
						listingImages,
					}
				: null;

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

			return data;
		});

		const folderData = {
			...folder.dataValues,
		};

		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) => acc + review.dataValues.rating, 0);
	return (sum / data.length).toFixed(2);
}

function formatAddress(listing) {
	return listing?.address_city && listing?.address_country
		? `${listing.address_city}, ${listing.address_country}`
		: null;
}

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}`,
	}));
}
