const { deleteFile } = require('../../helpers');
const {
	Hotels,
	HotelImages,
	HotelRooms,
	HotelRoomImages,
	HotelBookings,
	HotelReviews,
	HotelReviewImages,
	Members,
	sequelize,
} = require('../../models');
const { formatImage, parseJsonSafe, formatCase } = require('../../util/formatter');
const { errorHandler } = require('../../util');

exports.createHotel = async (req, res, next) => {
	const transaction = await sequelize.transaction();
	try {
		const { name, location, longitude, latitude } = req.body;

		const body = formatCase(req.body, 'snake');

		const additional_info = parseJsonSafe(req.body.additional_info);

		if (!name || !location || !longitude || !latitude) {
			return errorHandler('Name, location, longitude, and latitude are required', 400);
		}

		let hotel = await Hotels.create(
			{
				...body,
				additional_info: additional_info ? parseJsonSafe(additional_info) : null,
				longitude: parseFloat(longitude),
				latitude: parseFloat(latitude),
			},
			{ transaction },
		);

		let hotelImages = [];
		const hotelImgs = req?.files?.hotelImgs;

		if (hotelImgs?.length > 0) {
			for (const img of hotelImgs) {
				const image = await HotelImages.create(
					{
						hotel_id: hotel.id,
						image: img.filename,
					},
					{ transaction },
				);
				hotelImages.push(image);
			}
		}

		await transaction.commit();

		hotel = hotel.get({ plain: true });
		hotel.images = hotelImages.map((img) => ({
			id: img.id,
			image: formatImage(img.image),
		}));

		return res.status(201).json({ success: true, data: hotel });
	} catch (error) {
		if (transaction) await transaction.rollback();
		next(error);
	}
};

exports.createHotelRoom = async (req, res, next) => {
	const transaction = await sequelize.transaction();
	try {
		const { hotel_id, name, description, additional_info, price } = req.body;

		const hotel = await Hotels.findByPk(hotel_id);
		if (!hotel) {
			return errorHandler('Hotel not found', 404);
		}

		if (!name || !price) {
			await transaction.rollback();
			return errorHandler('Room name and price are required', 400);
		}

		const createdRoom = await HotelRooms.create(
			{
				hotel_id: hotel_id,
				name,
				description,
				additional_info: parseJsonSafe(additional_info),
				price: parseFloat(price),
			},
			{ transaction },
		);

		// Handle room images
		let roomImages = [];
		const roomImgs = req?.files?.roomImgs || [];

		for (const img of roomImgs) {
			const roomImage = await HotelRoomImages.create(
				{
					hotel_room_id: createdRoom.id,
					image: img.filename,
				},
				{ transaction },
			);
			roomImages.push(roomImage);
		}

		await transaction.commit();

		const roomData = createdRoom.get({ plain: true });
		roomData.images = roomImages.map((img) => ({
			id: img.id,
			image: formatImage(img.image),
		}));

		return res.status(201).json({
			success: true,
			message: 'Hotel room created successfully',
			data: roomData,
		});
	} catch (error) {
		if (transaction) await transaction.rollback();
		next(error);
	}
};

exports.getAllHotels = async (req, res, next) => {
	try {
		const hotels = await Hotels.findAll({
			include: [
				{
					model: HotelImages,
					as: 'images',
				},
				{
					model: HotelRooms,
					as: 'rooms',
					include: [{ model: HotelRoomImages, as: 'images' }],
				},
				{
					model: HotelBookings,
					as: 'bookings',
					include: [
						{
							model: Members,
							as: 'member',
							attributes: ['id', 'first_name', 'last_name', 'email'],
						},
					],
				},
				{
					model: HotelReviews,
					as: 'reviews',
					include: [
						{ model: HotelReviewImages, as: 'images' },
						{
							model: Members,
							as: 'member',
							attributes: ['id', 'first_name', 'last_name', 'profile_image'],
						},
					],
				},
			],
			order: [['createdAt', 'DESC']],
		});

		// Format images
		const formattedHotels = hotels.map((hotel) => {
			const hotelData = hotel.get({ plain: true });
			hotelData.additional_info = parseJsonSafe(hotelData.additional_info);
			hotelData.images =
				hotelData.images?.map((img) => ({
					...img,
					image: formatImage(img.image),
				})) || [];

			hotelData.rooms =
				hotelData.rooms?.map((room) => ({
					...room,
					images:
						room.images?.map((img) => ({
							...img,
							image: formatImage(img.image),
						})) || [],
				})) || [];

			hotelData.reviews =
				hotelData.reviews?.map((review) => ({
					...review,
					images:
						review.images?.map((img) => ({
							...img,
							image: formatImage(img.image),
						})) || [],
					member: review.member
						? {
								...review.member,
								profile_image: formatImage(review.member.profile_image),
							}
						: null,
				})) || [];

			return hotelData;
		});

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

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

		const hotel = await Hotels.findByPk(id, {
			include: [
				{
					model: HotelImages,
					as: 'images',
				},
				{
					model: HotelRooms,
					as: 'rooms',
					include: [{ model: HotelRoomImages, as: 'images' }],
				},
				{
					model: HotelBookings,
					as: 'bookings',
					include: [
						{
							model: Members,
							as: 'member',
							attributes: ['id', 'first_name', 'last_name', 'email'],
						},
					],
				},
				{
					model: HotelReviews,
					as: 'reviews',
					include: [
						{ model: HotelReviewImages, as: 'images' },
						{
							model: Members,
							as: 'member',
							attributes: ['id', 'first_name', 'last_name', 'profile_image'],
						},
					],
				},
			],
		});

		if (!hotel) {
			return errorHandler('Hotel not found', 404);
		}

		const hotelData = hotel.get({ plain: true });
		hotelData.additional_info = parseJsonSafe(hotelData.additional_info);
		hotelData.images =
			hotelData.images?.map((img) => ({
				...img,
				image: formatImage(img.image),
			})) || [];

		hotelData.rooms =
			hotelData.rooms?.map((room) => ({
				...room,
				images:
					room.images?.map((img) => ({
						...img,
						image: formatImage(img.image),
					})) || [],
			})) || [];

		hotelData.reviews =
			hotelData.reviews?.map((review) => ({
				...review,
				images:
					review.images?.map((img) => ({
						...img,
						image: formatImage(img.image),
					})) || [],
				member: review.member
					? {
							...review.member,
							profile_image: formatImage(review.member.profile_image),
						}
					: null,
			})) || [];

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

exports.updateHotel = async (req, res, next) => {
	const transaction = await sequelize.transaction();
	try {
		const { id } = req.params;
		const body = formatCase(req.body, 'snake');

		const additional_info = parseJsonSafe(req.body.additional_info);

		const hotel = await Hotels.findByPk(id);
		if (!hotel) {
			return errorHandler('Hotel not found', 404);
		}

		await hotel.update(
			{
				...body,
				additional_info: additional_info ? JSON.stringify(additional_info) : hotel.additional_info,
			},
			{ transaction },
		);

		let newHotelImages = [];
		const hotelImgs = req?.files?.hotelImgs;

		if (hotelImgs?.length > 0) {
			const existingImages = await HotelImages.findAll({
				where: { hotel_id: id },
			});

			for (const existingImage of existingImages) {
				deleteFile(existingImage.image, 'hotel-uploads');
				await existingImage.destroy({ transaction });
			}

			for (const img of hotelImgs) {
				const image = await HotelImages.create(
					{
						hotel_id: hotel.id,
						image: img.filename,
					},
					{ transaction },
				);
				newHotelImages.push(image);
			}
		}

		await transaction.commit();

		const updatedHotel = await Hotels.findByPk(id, {
			include: [
				{ model: HotelImages, as: 'images' },
				{
					model: HotelRooms,
					as: 'rooms',
					include: [{ model: HotelRoomImages, as: 'images' }],
				},
			],
		});

		const hotelData = updatedHotel.get({ plain: true });
		hotelData.images =
			hotelData.images?.map((img) => ({
				...img,
				image: formatImage(img.image),
			})) || [];

		hotelData.rooms =
			hotelData.rooms?.map((room) => ({
				...room,
				images:
					room.images?.map((img) => ({
						...img,
						image: formatImage(img.image),
					})) || [],
			})) || [];

		return res.status(200).json({ success: true, data: hotelData });
	} catch (error) {
		if (transaction) await transaction.rollback();
		next(error);
	}
};

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

		const hotel = await Hotels.findByPk(id, {
			include: [
				{ model: HotelImages, as: 'images' },
				{
					model: HotelRooms,
					as: 'rooms',
					include: [{ model: HotelRoomImages, as: 'images' }],
				},
			],
		});

		if (!hotel) {
			return errorHandler('Hotel not found', 404);
		}

		for (const image of hotel.images || []) {
			deleteFile(image.image, 'hotel-uploads');
		}

		for (const room of hotel.rooms || []) {
			for (const image of room.images || []) {
				deleteFile(image.image, 'hotel-uploads');
			}
		}

		await hotel.destroy({ transaction });

		await transaction.commit();

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