const { QueryTypes, Op } = require('sequelize');
const {
  ListingImages,
  sequelize,
  RecentlyViewedListings,
  Listings,
  ListingReviews,
  Users,
  ListingAddOns,
} = require('../../models');
const { paginate } = require('../../util');
const { parseJsonSafe } = require('../../helpers');
const jwt = require('jsonwebtoken');
const { formatCaseDeep } = require('../../util/formatter');

exports.getListings = async (req, res, next) => {
  try {
    const {
      search,
      status,
      sortBy,
      sort,
      page,
      limit,
      long,
      lat,
      location_type,
      pax,
      checkInDate,
      checkOutDate,
    } = req.query;
    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, l.latitude, l.longitude,
				l.max_capacity,
				(SELECT ROUND(AVG(lr.rating), 2) FROM listing_reviews lr WHERE lr.listing_id = l.id) AS averageRating
			`;

    const user = await getUser(req);

    if (user) {
      sqlString += `,
				EXISTS (SELECT 1 FROM favorite_folder_listings f WHERE f.listing_id = l.id AND f.user_id = "${user.id}") AS isFavorite
			`;
    }

    sqlString += `FROM 
				listings l
			LEFT JOIN
				users u ON u.id = l.host_id 
			WHERE 
				l.is_deleted = false AND u.status = true
		`;

    const replacements = [];

    if (search) {
      const str = `%${search}%`;
      sqlString += ` AND (l.name LIKE ? OR l.description LIKE ? )`;
      replacements.push(str, str);
    }

    if (location_type_array && location_type_array?.length > 0) {
      const conditions = location_type_array.map(() => `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 (long && lat) {
      sqlString += ` 
				AND (6371 * acos(
					cos(radians(?)) * cos(radians(l.latitude)) * cos(radians(l.longitude) - radians(?)) +
					sin(radians(?)) * sin(radians(l.latitude))
				)) <= 10`;
      replacements.push(lat, long, lat);
    }

    if (pax) {
      sqlString += ` AND l.max_capacity >= ?`;
      replacements.push(parseInt(pax));
    }

    if (checkInDate && checkOutDate) {
      sqlString += ` AND l.id NOT IN (
				SELECT DISTINCT b.listing_id 
				FROM bookings b
				WHERE b.status = 'Upcoming'
				AND (
					(? BETWEEN b.check_in AND DATE_SUB(b.check_out, INTERVAL 1 DAY))
					OR (? BETWEEN b.check_in AND DATE_SUB(b.check_out, INTERVAL 1 DAY))
					OR (b.check_in BETWEEN ? AND DATE_SUB(?, INTERVAL 1 DAY))
				)
			)`;
      replacements.push(checkInDate, checkOutDate, checkInDate, checkOutDate);
    }

    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);
      return {
        ...listing,
        isFavorite: !!listing?.isFavorite,
        listingImages: listingImages.map((img) => ({
          image: `${process.env.BASE_URL}/public/host-uploads/${img.image}`,
        })),
        address:
          listing.address_country && listing.address_city
            ? `${listing.address_country}, ${listing.address_city}`
            : null,
      };
    });

    if (!page || !limit) {
      return res.status(200).json({ success: true, data: jsonSafeListings });
    }

    const paginated = paginate(jsonSafeListings, parseInt(page), parseInt(limit));
    return res.status(200).json({ success: true, ...paginated });
  } catch (error) {
    next(error);
  }
};

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

    let sqlString = `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`;

    const user = await getUser(req);

    if (user) {
      sqlString += `,
				EXISTS (SELECT 1 FROM favorite_folder_listings f WHERE f.listing_id = listings.id AND f.user_id = "${user.id}") AS isFavorite
			`;
    }

    const [_listing] = await sequelize.query(
      sqlString +
        `
				FROM listings
				LEFT JOIN users ON users.id = listings.host_id
            WHERE listings.id = ?`,
      {
        type: QueryTypes.SELECT,
        replacements: [listingId],
      },
    );

    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 addOns = await ListingAddOns.findAll({
      where: {
        listing_id: _listing.id,
      },
      attributes: ['id', 'name', 'description', 'price'],
    });

    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,
      bathroom: _listing.bathroom,
      kitchen: _listing.kitchen,
      safetyAndSecurity: _listing.safety_and_security,
      guidelines: _listing.guidelines,
      amenities: parseJsonSafe(_listing.amenities),
      services: parseJsonSafe(_listing.services),
      locationType: parseJsonSafe(_listing.location_type),
      isFavorite: !!_listing?.isFavorite,

      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/user-uploads/` + _listing.host_image
          : null,
        reviewsCount: _listing.hostReviewsCount,
      },
    };

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

exports.getListingImages = async (req, res, next) => {
  try {
    const { listingId } = req.params;
    const { checkExistence = 'true' } = req.query;

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

    if (!images || images.length === 0) {
      return res.status(200).json({
        success: true,
        data: [],
        message: 'No images found for this listing',
      });
    }

    // Import image helper
    const { groupImagesByCategory } = require('../../helpers/image-helper');

    // Group and validate images
    const groupedImages = await groupImagesByCategory(images, 'host-uploads', {
      checkExistence: checkExistence === 'true',
      fallbackImage: null, // Could set a placeholder image URL here
    });

    // Log if some images are missing
    const totalRequested = images.length;
    const totalReturned = groupedImages.reduce((sum, group) => sum + group.images.length, 0);

    if (totalReturned < totalRequested) {
      const missingCount = totalRequested - totalReturned;
      console.warn(`[Image Loading] ${missingCount} images not found for listing ${listingId}`);
    }

    return res.status(200).json({
      success: true,
      data: groupedImages,
      meta: {
        totalImages: totalReturned,
        missingImages: totalRequested - totalReturned,
      },
    });
  } catch (error) {
    console.error('[Image Loading Error]', {
      listingId: req.params.listingId,
      error: error.message,
    });
    next(error);
  }
};

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

  try {
    const listing = await Listings.findByPk(listingId);

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

    const startOfMonth = new Date();
    startOfMonth.setDate(1);
    startOfMonth.setHours(0, 0, 0, 0);

    const existing = await RecentlyViewedListings.findOne({
      where: {
        user_id: userId,
        listing_id: listingId,
        createdAt: {
          [Op.gte]: startOfMonth,
        },
      },
    });

    if (!existing) {
      await RecentlyViewedListings.create({
        user_id: userId,
        listing_id: listingId,
        host_id: listing.host_id,
      });
    }

    return res.status(201).json({
      success: true,
      message: 'Recent view created',
    });
  } catch (error) {
    next(error);
  }
};

exports.getRecentlyViewed = async (req, res, next) => {
  const { userId } = req;

  try {
    const recentViews = await RecentlyViewedListings.findAll({
      where: {
        user_id: userId,
      },
      order: [['createdAt', 'DESC']],
      attributes: ['id'],
      include: [
        {
          model: Listings,
          as: 'listing',
          attributes: [
            'id',
            'name',
            'description',
            'price',
            'location',
            'address_city',
            'address_country',
          ],
          include: [
            {
              model: ListingImages,
              as: 'listingImages',
              attributes: ['image'],
            },
            {
              model: ListingReviews,
              as: 'listingReviews',
              attributes: ['rating'],
            },
          ],
        },
      ],
    });

    let data = recentViews.map((recentView) => {
      const listings = recentView.dataValues.listing.dataValues;

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

      let averageRating = null;
      if (listings?.listingReviews?.length > 0) {
        averageRating = (
          listings?.listingReviews?.reduce((acc, review) => acc + review.rating, 0) /
          listings?.listingReviews?.length
        ).toFixed(2);
      }

      delete listings.listingImages;
      delete listings.listingReviews;
      return {
        id: recentView.id,
        listing: {
          ...listings,
          listingImages,
          averageRating,
        },
      };
    });

    data = formatCaseDeep(data);

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

exports.deleteRecentlyViewed = async (req, res, next) => {
  const { recentlyViewedListingId } = req.params;

  if (!recentlyViewedListingId) {
    return res.status(400).json({
      success: false,
      message: 'Id is required',
    });
  }
  try {
    const recentView = await RecentlyViewedListings.findByPk(recentlyViewedListingId);

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

    await recentView.destroy();

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

async function getUser(req) {
  const authHeader = req.get('Authorization');

  if (!authHeader) {
    return false;
  }

  const token = authHeader.split(' ')[1];
  const decodedToken = jwt.verify(token, process.env.SECRETKEY);

  if (!decodedToken) {
    return false;
  }

  const user = await Users.findByPk(decodedToken.userId);

  if (!user) {
    return false;
  }

  return user;
}
