const { Op } = require('sequelize');
const { calculateTotalAmount, calculateBalance, validatePax } = require('../../helpers');
const { Bookings, Listings, BookingAddOns, ListingImages } = require('../../models');
const { generateReferenceNumber } = require('../../util');

exports.createBooking = (req, res, next) => {
	const { userId } = req;
	const { listingId, checkIn, checkOut, pax, paymentOption, phoneNo, paymentMethod, amountPaid, addOns } = req.body;

	Listings.findOne({
		where: { id: listingId, status: 'Active' },
	})
		.then((listing) => {
			if (!listing) {
				return res.status(404).json({ success: false, message: 'Listing not found' });
			}

			validatePax(pax, listing.max_capacity);

			const price = listing.price;
			const hostId = listing.host_id;
			const totalAmount = calculateTotalAmount(price, checkIn, checkOut);

			let addOnTotal = 0.0;
			if (addOns && Array.isArray(addOns)) {
				addOns.forEach((addon) => {
					addOnTotal += parseFloat((addon.price * addon.quantity).toFixed(2));
				});
			}

			const finalTotalAmount = parseFloat((totalAmount + addOnTotal).toFixed(2));
			const balance = calculateBalance(finalTotalAmount, amountPaid);
			const bookingNo = generateReferenceNumber();

			let paymentStatus;
			if (amountPaid === 0) {
				paymentStatus = 'Unpaid';
			} else if (amountPaid === finalTotalAmount) {
				paymentStatus = 'Fully Paid';
			} else {
				paymentStatus = 'Partially Paid';
			}

			return Bookings.create({
				customer_id: userId,
				host_id: hostId,
				listing_id: listingId,
				booking_no: bookingNo,
				check_in: checkIn,
				check_out: checkOut,
				pax,
				payment_option: paymentOption,
				payment_method: paymentMethod,
				payment_status: paymentStatus,
				phone_no: phoneNo,
				price_per_night: price,
				add_ons_total: addOnTotal,
				service_fee: 0.0,
				total_amount: finalTotalAmount,
				amount_paid: amountPaid,
				balance: balance,
			}).then((booking) => {
				if (addOns && Array.isArray(addOns)) {
					const addOnPromises = addOns.map((addon) => {
						return BookingAddOns.create({
							booking_id: booking.id,
							add_on_name: addon.name,
							quantity: addon.quantity,
							price: addon.price,
							total_amount: parseFloat((addon.price * addon.quantity).toFixed(2)),
						});
					});

					return Promise.all(addOnPromises);
				}
			});
		})
		.then(() => {
			res.status(201).json({ success: true, message: 'Booking created successfully' });
		})
		.catch((err) => {
			next(err);
		});
};

exports.getAllMyBookings = (req, res, next) => {
	const { userId } = req;
	const { status, search, startDate, endDate } = req.query;

	const filter = {
		customer_id: userId,
	};

	if (status) {
		filter.status = status;
	}

	if (startDate) {
		filter[Op.and] = [
			{
				[Op.or]: [{ check_in: { [Op.gte]: startDate } }, { check_out: { [Op.gte]: startDate } }],
			},
		];
	}

	if (endDate) {
		const endOfDay = new Date(endDate);
		endOfDay.setHours(23, 59, 59, 999);

		if (!filter[Op.and]) {
			filter[Op.and] = [];
		}
		filter[Op.and].push({
			[Op.or]: [{ check_in: { [Op.lte]: endDate } }, { check_out: { [Op.lte]: endDate } }],
		});
	}

	const listingFilter = {};

	if (search) {
		listingFilter[Op.or] = [
			{
				name: { [Op.like]: `%${search}%` },
			},
			{
				address_city: { [Op.like]: `%${search}%` },
			},
			{
				address_country: { [Op.like]: `%${search}%` },
			},
		];
	}

	Bookings.findAll({
		where: filter,
		include: [
			{
				model: Listings,
				as: 'listing',
				attributes: ['name', 'price', 'description', 'address_city', 'address_country'],
				include: [
					{
						model: ListingImages,
						as: 'listingImages',
						attributes: ['image'],
					},
				],
				where: listingFilter,
			},
		],
		attributes: ['id', 'booking_no', 'check_in', 'check_out', 'status'],
		order: [['createdAt', 'DESC']],
	})
		.then((bookings) => {
			const mappedBookings = bookings.map((booking) => {
				const listingImage = booking.listing?.listingImages?.[0];
				const hasAddress = booking?.listing?.address_city && booking?.listing?.address_country;
				const address = hasAddress ? `${booking.listing.address_city}, ${booking.listing.address_country}` : 'N/A';

				return {
					id: booking.id,
					bookingNo: booking.booking_no,
					checkIn: booking.check_in,
					checkOut: booking.check_out,
					status: booking.status,
					listing: {
						image: listingImage ? `${process.env.BASE_URL}/public/host-uploads/` + listingImage.image : null,
						address,
						name: booking?.listing?.name ? booking.listing.name : 'N/A',
						description: booking?.listing?.description ? booking.listing.description : 'N/A',
					},
				};
			});

			return res.status(200).json({
				success: true,
				bookings: mappedBookings,
			});
		})
		.catch((err) => {
			next(err);
		});
};

exports.cancelBooking = async (req, res, next) => {
	try {
		const { bookingNumber } = req.params;
		const { userId } = req;

		const booking = await Bookings.findOne({
			where: { booking_no: bookingNumber },
		});

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

		if (booking.customer_id !== userId) {
			return res.status(403).json({ success: false, message: 'You are not authorized to cancel this booking' });
		}

		if (['Cancelled', 'Done'].includes(booking.status)) {
			return res.status(400).json({ success: false, message: 'Booking has already been ' + booking.status });
		}

		booking.status = 'Cancelled';
		booking.save();

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