const { QueryTypes, Op } = require('sequelize');
const { Listings, ListingImages, sequelize } = require('../../models');
const { deleteFile, parseJsonSafe } = require('../../helpers');
const { paginate } = require('../../util');

exports.getMyListings = async (req, res, next) => {
	try {
		const { userId } = req;
		const { search, status, sortBy, sort } = req.query;
		const location_type = req.query.location_type;

		const location_type_array = location_type ? (Array.isArray(location_type) ? location_type : [location_type]) : null;

		let sqlString = `
		SELECT 
			l.id, l.name, l.description, l.price, 
			l.address_country, l.address_city,
			(SELECT ROUND(AVG(rating),2) FROM listing_reviews lr WHERE lr.listing_id = l.id) as averageRating
		FROM 
			listings l 
		WHERE 
			l.is_deleted = false AND l.host_id = ?`;

		const replacements = [userId];

		if (search) {
			const str = `%` + search + `%`;

			sqlString += ` AND (l.name LIKE ? OR l.description LIKE ? )`;
			replacements.push(str, str);
		}

		if (location_type_array?.length > 0) {
			const conditions = location_type_array.map(() => {
				return `JSON_EXTRACT(location_type,'$') LIKE ?`;
			});

			sqlString += ` AND (${conditions.join(' OR ')})`;
			replacements.push(...location_type_array.map((lt) => `%${lt}%`));
		}

		if (status) {
			sqlString += ` AND l.status = ?`;
			replacements.push(status);
		}

		if (sortBy && sort) {
			if (['averageRating', 'price'].includes(sortBy) && ['ASC', 'DESC'].includes(sort)) {
				sqlString += ` ORDER BY ${sortBy} ${sort}`;
			}
		}

		const listings = await sequelize.query(sqlString, {
			type: QueryTypes.SELECT,
			replacements,
		});

		const listingsImages = await ListingImages.findAll({
			where: {
				listing_id: {
					[Op.in]: listings.map((listing) => listing.id),
				},
			},
			attributes: ['listing_id', 'image'],
		});

		const jsonSafeListings = listings.map((listing) => {
			const listingImages = listingsImages.filter((img) => img.listing_id === listing.id);

			const data = {
				...listing,
				listingImages: listingImages.map((img) => ({
					image: `${process.env.BASE_URL}/public/host-uploads/` + img.image,
				})),
			};

			data.address = data.address_country && data.address_city ? `${data.address_country}, ${data.address_city}` : null;
			delete data.address_city;
			delete data.address_country;

			return data;
		});
		const paginated = paginate(jsonSafeListings, req.query.page, req.query.limit);

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

exports.updateListing = async (req, res, next) => {
	try {
		const { userId: hostId } = req;
		const { listingId } = req.params;
		const { data } = req.body;
		const { listingImgs } = req.files || {};

		const listing = await Listings.findByPk(listingId);

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

		const isOwner = listing.host_id === hostId;

		if (!isOwner) {
			deleteUploadedFiles();
			return res.status(403).json({
				success: false,
				message: 'You are not authorized to update this listing',
			});
		}

		if (data) {
			for (const key of Object.keys(jsonColumns)) {
				if (!(key in data)) continue;
				if (key === 'locationType' && !data[key]) {
					return res.status(400).json({
						success: false,
						message: `Required data for ${key}`,
					});
				}
				if (['amenities', 'services'].includes(key) && (data[key] === null || data[key].length === 0)) {
					continue;
				}
				if (!Array.isArray(data[key]) || data[key].length === 0) {
					deleteUploadedFiles();
					return res.status(400).json({
						success: false,
						message: `Invalid or empty data for ${key}`,
					});
				}
			}

			for (const key in data) {
				if (Object.keys(listingColumns).includes(key)) {
					listing[listingColumns[key]] = data[key];
					continue;
				}
				if (Object.keys(jsonColumns).includes(key)) {
					listing[jsonColumns[key]] = data[key] === null ? null : JSON.stringify(data[key]);
				}
			}
		}

		const categories = data?.categories;
		let singleCategory = null;
		if (categories?.length === 1) {
			singleCategory = categories[0];
		}

		const nullCategory = categories === null || categories === undefined || categories?.length === 0;
		if (categories?.length > 1) {
			if (categories.length !== 1 && listingImgs?.length != categories?.length) {
				deleteUploadedFiles();
				return res.status(400).json({
					success: false,
					message: 'A pair of Group name and image is required or must have 1 groupname for all images',
				});
			}
		}
		if (listingImgs && listingImgs.length > 0) {
			const listingImages = listingImgs.map((img, index) => ({
				listing_id: listing.id,
				image: img.filename,
				category: nullCategory ? null : (singleCategory ?? categories[index]),
			}));

			const oldListingImages = await ListingImages.findAll({
				where: {
					listing_id: listing.id,
				},
			});

			for (const oldImage of oldListingImages) {
				const oldImagePath = oldImage.image;

				deleteFile(oldImagePath, 'host-uploads');

				await oldImage.destroy();
			}

			await ListingImages.bulkCreate(listingImages);
		}

		await listing.save();

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

	function deleteUploadedFiles() {
		const { listingImgs } = req.files || {};

		if (listingImgs?.length > 0) {
			try {
				for (const img of listingImgs) {
					deleteFile(img.filename, 'host-uploads');
				}
			} catch (error) {
				console.log('Failed to delete', error);
			}
		}
	}
};

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

		const listing = await Listings.findByPk(listingId);

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

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

		if (listing.is_deleted) {
			return res.status(400).json({
				success: false,
				message: 'Listing already deleted',
			});
		}

		listing.is_deleted = true;

		await listing.save();

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

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

		const listing = await Listings.findByPk(listingId);

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

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

		await listing.destroy();

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

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

		const [_listing] = await sequelize.query(
			`
            SELECT listings.*, users.firstname, users.lastname, users.id as host_id, users.hosting_date, users.image as host_image, users.about_me,
				(SELECT ROUND(AVG(rating),2) as rating FROM listing_reviews WHERE listing_reviews.listing_id = listings.id) as listingAverageRating,
				(SELECT ROUND(AVG(rating),2) as rating FROM host_reviews WHERE host_reviews.host_id = users.id) as hostAverageRating,
				(SELECT COUNT(*) as hostReviewsCount FROM host_reviews WHERE host_reviews.host_id = users.id) as hostReviewsCount
				FROM listings
				LEFT JOIN users ON users.id = listings.host_id
            WHERE listings.id = ? AND listings.host_id = ?`,
			{
				type: QueryTypes.SELECT,
				replacements: [listingId, userId],
			},
		);

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

		const images = await ListingImages.findAll({
			where: {
				listing_id: _listing.id,
			},
			attributes: ['image'],
		});

		const listing = {
			id: _listing.id,
			name: _listing.name,
			description: _listing.description,
			address:
				_listing.address_city && _listing.address_country
					? `${_listing.address_country}, ${_listing.address_city}`
					: null,
			longitude: _listing.longitude,
			latitude: _listing.latitude,
			price: _listing.price,
			averageRating: _listing.listingAverageRating,
			minCapacity: _listing.min_capacity,
			maxCapacity: _listing.max_capacity,
			checkInTime: _listing.check_in_time,
			checkOutTime: _listing.check_out_time,
			cancellationPolicy: _listing.cancellation_policy,
			bedroom: _listing.bedroom,
			kitchen: _listing.kitchen,
			bathroom: _listing.bathroom,
			safetyAndSecurity: _listing.safety_and_security,
			guidelines: _listing.guidelines,
			status: _listing.status,
			isDeleted: _listing.is_deleted,
			amenities: parseJsonSafe(_listing.amenities),
			services: parseJsonSafe(_listing.services),
			locationType: parseJsonSafe(_listing.location_type),
			listingImages: images.map((img) => ({
				image: `${process.env.BASE_URL}/public/host-uploads/` + img.image,
			})),
			host: {
				fullname: _listing.firstname + ' ' + _listing.lastname,
				id: _listing.host_id,
				hostingDate: _listing.hosting_date,
				aboutMe: _listing.about_me,
				averageRating: _listing.hostAverageRating,
				image: _listing.host_image ? `${process.env.BASE_URL}/public/host-uploads/` + _listing.host_image : null,
				hostReviewsCount: _listing.hostReviewsCount,
			},
		};

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

const listingColumns = {
	name: 'name',
	description: 'description',
	longitude: 'longitude',
	latitude: 'latitude',
	price: 'price',
	minCapacity: 'min_capacity',
	maxCapacity: 'max_capacity',
	checkInTime: 'check_in_time',
	checkOutTime: 'check_out_time',
	cancellationPolicy: 'cancellation_policy',
	status: 'status',
	isDeleted: 'is_deleted',
	addressCountry: 'address_country',
	addressStreet: 'address_street',
	addressApt: 'address_apt',
	addressCity: 'address_city',
	addressState: 'address_state',
	addressZip: 'address_zip',
	bedroom: 'bedroom',
	kitchen: 'kitchen',
	bathroom: 'bathroom',
	safetyAndSecurity: 'safety_and_security',
	guidelines: 'guidelines',
};

const jsonColumns = {
	locationType: 'location_type',
	amenities: 'amenities',
	services: 'services',
};
