const {
	Restaurants,
	RestaurantReviews,
	Members,
	sequelize,
	RestaurantHours,
	RestaurantImages,
} = require('../../models');
const { errorHandler } = require('../../util');
const { formatCase, parseJsonSafe } = require('../../util/formatter');
const { deleteFile } = require('../../helpers');

exports.createRestaurant = async (req, res, next) => {
	const transaction = await sequelize.transaction();
	try {
		const { name, about, features, latitude, longitude, restaurant_hours } = req.body;

		if (!name || !about) {
			return errorHandler('Restaurant name and about are required', 400);
		}

		const restaurant = await Restaurants.create(
			{
				...req.body,
				features: JSON.stringify(parseJsonSafe(features)),
				latitude: latitude ? parseFloat(latitude) : null,
				longitude: longitude ? parseFloat(longitude) : null,
			},
			{ transaction },
		);

		if (restaurant_hours) {
			await RestaurantHours.bulkCreate(
				parseJsonSafe(restaurant_hours).map((hour) => ({
					restaurant_id: restaurant.id,
					...hour,
				})),
				{ transaction },
			);
		}

		const images = req.files.restaurantImgs;
		if (images && images.length > 0) {
			const imageData = images.map((file) => ({
				restaurant_id: restaurant.id,
				image: file.filename,
			}));
			const createdImages = await RestaurantImages.bulkCreate(imageData, { transaction });
			images.push(...createdImages);
		}

		await transaction.commit();

		return res.status(201).json({
			success: true,
			message: 'Restaurant created successfully',
			data: formatCase(restaurant.get({ plain: true })),
			images,
		});
	} catch (error) {
		if (transaction) await transaction.rollback();
		next(error);
	}
};

exports.getAllRestaurants = async (req, res, next) => {
	try {
		const { page = 1, limit = 10, search } = req.query;
		const offset = (parseInt(page) - 1) * parseInt(limit);

		let whereClause = {};
		if (search) {
			whereClause = {
				[sequelize.Op.or]: [
					{ name: { [sequelize.Op.like]: `%${search}%` } },
					{ location: { [sequelize.Op.like]: `%${search}%` } },
				],
			};
		}

		const { count, rows: restaurants } = await Restaurants.findAndCountAll({
			where: whereClause,
			attributes: {
				include: [
					[sequelize.fn('AVG', sequelize.col('reviews.rating')), 'averageRating'],
					[sequelize.fn('COUNT', sequelize.col('reviews.id')), 'reviewCount'],
				],
			},
			include: [
				{
					model: RestaurantImages,
					as: 'images',
					attributes: ['id', 'image'],
				},
				{
					model: RestaurantReviews,
					as: 'reviews',
					attributes: [],
				},
			],
			group: ['Restaurants.id', 'images.id'],
			order: [['name', 'ASC']],
			limit: parseInt(limit),
			offset: offset,
		});

		return res.status(200).json({
			success: true,
			data: {
				restaurants: formatCase(restaurants.map((restaurant) => restaurant.get({ plain: true }))),
				pagination: {
					currentPage: parseInt(page),
					totalPages: Math.ceil(count.length / parseInt(limit)),
					totalItems: count.length,
					itemsPerPage: parseInt(limit),
				},
			},
		});
	} catch (error) {
		next(error);
	}
};

exports.getRestaurant = async (req, res, next) => {
	try {
		const { id } = req.params;

		const restaurant = await Restaurants.findByPk(id, {
			attributes: {
				include: [
					[sequelize.fn('AVG', sequelize.col('reviews.rating')), 'averageRating'],
					[sequelize.fn('COUNT', sequelize.col('reviews.id')), 'reviewCount'],
				],
			},
			include: [
				{
					model: RestaurantImages,
					as: 'images',
					attributes: ['id', 'image'],
				},
				{
					model: RestaurantHours,
					as: 'hours',
					attributes: ['id', 'day_of_week', 'open_time', 'close_time', 'is_closed'],
				},
				{
					model: RestaurantReviews,
					as: 'reviews',
					attributes: [],
				},
			],
			group: ['Restaurants.id', 'images.id', 'hours.id'],
		});

		if (!restaurant) {
			return errorHandler('Restaurant not found', 404);
		}

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

exports.updateRestaurant = async (req, res, next) => {
	const transaction = await sequelize.transaction();
	try {
		const { id } = req.params;

		const restaurant = await Restaurants.findByPk(id);
		if (!restaurant) {
			return errorHandler('Restaurant not found', 404);
		}

		let updateData = { ...req.body };
		if (req.files && req.files.restaurantImgs && req.files.restaurantImgs.length > 0) {
			const oldImages = await RestaurantImages.findAll({
				where: { restaurant_id: id },
			});

			for (const image of oldImages) {
				await deleteFile(image.image, 'restaurant-uploads');
				image.destroy({ transaction });
			}

			const imageData = req.files.restaurantImgs.map((file) => ({
				restaurant_id: id,
				image: file.filename,
			}));

			const createdImages = await RestaurantImages.bulkCreate(imageData, { transaction });
			updateData.images = createdImages.map((img) => img.get({ plain: true }));
		}

		await restaurant.update(updateData, { transaction });

		await transaction.commit();

		return res.status(200).json({
			success: true,
			message: 'Restaurant updated successfully',
			data: formatCase(restaurant.get({ plain: true })),
		});
	} catch (error) {
		if (transaction) await transaction.rollback();
		next(error);
	}
};

exports.deleteRestaurant = async (req, res, next) => {
	const transaction = await sequelize.transaction();
	try {
		const { id } = req.params;

		const restaurant = await Restaurants.findByPk(id, {
			include: [
				{
					model: RestaurantImages,
					as: 'images',
					attributes: ['id', 'image'],
				},
			],
		});

		if (!restaurant) {
			return errorHandler('Restaurant not found', 404);
		}

		// Delete associated images from filesystem
		if (restaurant.images && restaurant.images.length > 0) {
			for (const image of restaurant.images) {
				try {
					await deleteFile(`restaurant-uploads/${image.image}`);
				} catch (error) {
					console.error(`Failed to delete image file: ${image.image}`, error);
				}
			}
		}

		await restaurant.destroy({ transaction });

		await transaction.commit();

		return res.status(200).json({
			success: true,
			message: 'Restaurant deleted successfully',
		});
	} catch (error) {
		if (transaction) await transaction.rollback();
		next(error);
	}
};

// Upload additional images for a restaurant
exports.uploadRestaurantImages = async (req, res, next) => {
	const transaction = await sequelize.transaction();
	try {
		const { id } = req.params;

		const restaurant = await Restaurants.findByPk(id);
		if (!restaurant) {
			return errorHandler('Restaurant not found', 404);
		}

		if (!req.files || req.files.length === 0) {
			return errorHandler('No images provided', 400);
		}

		const imageData = req.files.map((file) => ({
			restaurant_id: id,
			image: file.filename,
		}));

		const images = await RestaurantImages.bulkCreate(imageData, { transaction });

		await transaction.commit();

		return res.status(201).json({
			success: true,
			message: 'Images uploaded successfully',
			data: formatCase(images.map((img) => img.get({ plain: true }))),
		});
	} catch (error) {
		if (transaction) await transaction.rollback();
		next(error);
	}
};

// Delete a specific restaurant image
exports.deleteRestaurantImage = async (req, res, next) => {
	const transaction = await sequelize.transaction();
	try {
		const { id, imageId } = req.params;

		const restaurant = await Restaurants.findByPk(id);
		if (!restaurant) {
			return errorHandler('Restaurant not found', 404);
		}

		const image = await RestaurantImages.findOne({
			where: {
				id: imageId,
				restaurant_id: id,
			},
		});

		if (!image) {
			return errorHandler('Image not found', 404);
		}

		// Delete image file from filesystem
		try {
			await deleteFile(`restaurant-uploads/${image.image}`);
		} catch (error) {
			console.error(`Failed to delete image file: ${image.image}`, error);
		}

		await image.destroy({ transaction });

		await transaction.commit();

		return res.status(200).json({
			success: true,
			message: 'Image deleted successfully',
		});
	} catch (error) {
		if (transaction) await transaction.rollback();
		next(error);
	}
};

// Get all images for a restaurant
exports.getRestaurantImages = async (req, res, next) => {
	try {
		const { id } = req.params;

		const restaurant = await Restaurants.findByPk(id);
		if (!restaurant) {
			return errorHandler('Restaurant not found', 404);
		}

		const images = await RestaurantImages.findAll({
			where: { restaurant_id: id },
			attributes: ['id', 'image'],
			order: [['id', 'ASC']],
		});

		return res.status(200).json({
			success: true,
			data: formatCase(images.map((img) => img.get({ plain: true }))),
		});
	} catch (error) {
		next(error);
	}
};
