const { Op } = require('sequelize');
const {
  Bookings,
  sequelize,
  HostReviews,
  RecentlyViewedListings,
  Users,
  Listings,
  HostNotifications,
} = require('../models');
const { calculateNights } = require('../helpers');
const { formatTimestamp } = require('../util');
const moment = require('moment');

exports.getEarningsByDate = async (date, userId) => {
  const startOfDay = `${date} 00:00:00`;
  const endOfDay = `${date} 23:59:59`;

  try {
    const result = await Bookings.findAll({
      attributes: [[sequelize.fn('SUM', sequelize.col('amount_paid')), 'totalEarnings']],
      where: {
        host_id: userId,
        status: 'Done',
        updatedAt: {
          [Op.between]: [startOfDay, endOfDay],
        },
      },
    });

    return parseFloat(result[0]?.dataValues?.totalEarnings || 0);
  } catch (error) {
    // console.error('Error in getEarningsByDate:', error.message);
    throw new Error('Failed to fetch earnings for the date.');
  }
};

exports.getEarningsByMonth = async (month, userId) => {
  const startOfMonth = `${month}-01 00:00:00`;
  const endOfMonth = `${month}-31 23:59:59`;

  const lastDayOfMonth = new Date(month + '-01');
  lastDayOfMonth.setMonth(lastDayOfMonth.getMonth() + 1);
  lastDayOfMonth.setDate(0);

  const endDate = `${month}-${lastDayOfMonth.getDate()} 23:59:59`;

  try {
    const result = await Bookings.findAll({
      attributes: [[sequelize.fn('SUM', sequelize.col('amount_paid')), 'totalEarnings']],
      where: {
        host_id: userId,
        status: 'Done',
        updatedAt: {
          [Op.between]: [startOfMonth, endDate],
        },
      },
    });

    return parseFloat(result[0]?.dataValues?.totalEarnings || 0);
  } catch (error) {
    // console.error('Error in getEarningsByMonth:', error.message);
    throw new Error('Failed to fetch earnings for the month');
  }
};

exports.getHostRating = async (userId) => {
  try {
    const result = await HostReviews.findAll({
      attributes: [[sequelize.fn('AVG', sequelize.col('rating')), 'averageRating']],
      where: {
        host_id: userId,
      },
    });

    return parseFloat(result[0]?.dataValues?.averageRating || 0);
  } catch (error) {
    // console.error('Error in getHostRating:', error.message);
    throw new Error('Failed to fetch host rating');
  }
};

exports.getBookingCountLast30Days = async (userId) => {
  try {
    const thirtyDaysAgo = new Date();
    thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
    const thirtyDaysAgoDate = thirtyDaysAgo.toISOString();

    const result = await Bookings.count({
      where: {
        host_id: userId,
        // status: 'Done',
        createdAt: {
          [Op.gte]: thirtyDaysAgoDate,
        },
      },
    });

    return result;
  } catch (error) {
    // console.error('Error in getBookingCountLast30Days:', error.message);
    throw new Error('Failed to fetch booking count for the last 30 days');
  }
};

exports.getTotalViews = async (userId) => {
  try {
    const thirtyDaysAgo = new Date();
    thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
    const thirtyDaysAgoDate = thirtyDaysAgo.toISOString();

    const result = await RecentlyViewedListings.count({
      where: {
        host_id: userId,
        createdAt: {
          [Op.gte]: thirtyDaysAgoDate,
        },
      },
    });

    return result;
  } catch (error) {
    // console.error('Error in getTotalViews:', error.message);
    throw new Error('Failed to fetch total views in the last 30 days');
  }
};

exports.getUpcomingBookings = async (userId) => {
  try {
    const upcomingBookings = await Bookings.findAll({
      where: {
        host_id: userId,
        status: {
          [Op.in]: ['Upcoming', 'Ongoing'],
        },
      },
      order: [['createdAt', 'DESC']],
      limit: 5,
      include: [
        {
          model: Users,
          as: 'customers',
        },
        {
          model: Listings,
          as: 'listing',
        },
      ],
    });

    return upcomingBookings.map((booking) => {
      const numberOfNights = calculateNights(booking.check_in, booking.check_out);

      return {
        id: booking.id,
        customerName: booking.customers
          ? `${booking.customers.firstname || ''} ${booking.customers.lastname || ''}`.trim()
          : 'N/A',
        listingName: booking.listing ? booking.listing.name : 'N/A',
        pax: booking.pax,
        checkIn: booking.check_in,
        checkOut: booking.check_out,
        numberOfNights,
        status: booking.status,
      };
    });
  } catch (error) {
    // console.error('Error in getUpcomingBookings:', error.message);
    throw new Error('Failed to fetch upcoming bookings');
  }
};

exports.getBookingsCountByStatus = async (userId, date) => {
  const whereConditions = {
    host_id: userId,
  };

  if (date) {
    const [year, month, day] = date.split('-');
    if (day) {
      whereConditions.createdAt = {
        [Op.between]: [`${year}-${month}-${day} 00:00:00`, `${year}-${month}-${day} 23:59:59`],
      };
    } else if (month) {
      whereConditions.createdAt = {
        [Op.between]: [`${year}-${month}-01 00:00:00`, `${year}-${month}-31 23:59:59`],
      };
    }
  }

  try {
    const statusCounts = await Bookings.findAll({
      attributes: ['status', [sequelize.fn('COUNT', sequelize.col('status')), 'count']],
      where: whereConditions,
      group: ['status'],
      raw: true,
    });

    const allStatuses = ['For Approval', 'Done', 'Upcoming', 'Cancelled', 'Ongoing', 'Declined'];

    const counts = allStatuses.reduce((acc, status) => {
      acc[status] = 0;
      return acc;
    }, {});

    statusCounts.forEach((statusData) => {
      counts[statusData.status] = parseInt(statusData.count, 10);
    });

    return counts;
  } catch (error) {
    // console.error('Error in getBookingsCountByStatus:', error.message);
    throw new Error('Failed to fetch booking counts per status');
  }
};

exports.getOccupancyRate = async (userId) => {
  const today = moment();
  const startDate = today.clone().subtract(30, 'days').startOf('day').format('YYYY-MM-DD 00:00:00'); // 30 days ago at midnight
  const endDate = today.endOf('day').format('YYYY-MM-DD 23:59:59'); // current day until the end of the day

  try {
    const totalListings = await Listings.count({
      where: { host_id: userId },
    });

    if (totalListings === 0) {
      return '0.00%';
    }

    const bookedListings = await Bookings.findAll({
      attributes: ['listing_id'],
      where: {
        host_id: userId,
        check_in: {
          [Op.lte]: endDate,
        },
        check_out: {
          [Op.gte]: startDate,
        },
      },
      group: ['listing_id'],
    });

    const occupancyRate = (bookedListings.length / totalListings) * 100;

    return `${occupancyRate.toFixed(2)}%`;
  } catch (error) {
    // console.error('Error in getOccupancyRate:', error.message);
    throw new Error('Failed to fetch occupancy rate');
  }
};

exports.getBookingConversionRate = async (userId) => {
  const today = moment();
  const startDate = today.clone().subtract(30, 'days').startOf('day').format('YYYY-MM-DD 00:00:00'); // 30 days ago at midnight
  const endDate = today.endOf('day').format('YYYY-MM-DD 23:59:59'); // current day until the end of the day

  try {
    const totalViews = await RecentlyViewedListings.count({
      where: {
        host_id: userId,
        createdAt: {
          [Op.between]: [startDate, endDate],
        },
      },
    });

    if (totalViews === 0) {
      return '0.00%';
    }

    // Count unique customers who made bookings in the last 30 days
    // This gives a more accurate conversion rate that won't exceed 100%
    const uniqueBookingCustomers = await Bookings.findAll({
      attributes: ['customer_id'],
      where: {
        host_id: userId,
        status: {
          [Op.in]: ['Upcoming', 'Done', 'For Approval', 'Pending'],
        },
        createdAt: {
          [Op.between]: [startDate, endDate],
        },
      },
      group: ['customer_id'],
    });

    // For a more meaningful conversion rate, we compare unique customers who booked
    // vs total views (assuming each view represents potential interest)
    const conversionRate = Math.min((uniqueBookingCustomers.length / totalViews) * 100, 100);

    return `${conversionRate.toFixed(2)}%`;
  } catch (error) {
    // console.error('Error in getBookingConversionRate:', error.message);
    throw new Error('Failed to fetch booking conversion rate');
  }
};

exports.getNotifications = async (userId) => {
  try {
    const notifications = await HostNotifications.findAll({
      where: { host_id: userId },
      order: [['createdAt', 'DESC']],
      attributes: ['id', 'booking_id', 'notif_type', 'message', 'status', 'createdAt'],
      limit: 5,
    });

    const mappedNotifications = notifications.map((notification) => ({
      bookingId: notification.booking_id,
      type: notification.notif_type,
      message: notification.message,
      status: notification.status,
      dateTime: formatTimestamp(notification.createdAt),
    }));

    return mappedNotifications;
  } catch (error) {
    // console.error('Error fetching notifications:', error.message);
    throw new Error('Unable to fetch notifications');
  }
};

exports.getMonthlyOccupancyRate = async (userId) => {
  const currentYear = moment().year();
  const months = Array.from({ length: 12 }, (_, index) => index + 1); // [1, 2, ..., 12]

  try {
    const occupancyRates = await Promise.all(
      months.map(async (month) => {
        const startOfMonth = moment()
          .year(currentYear)
          .month(month - 1)
          .startOf('month')
          .format('YYYY-MM-DD 00:00:00');
        const endOfMonth = moment()
          .year(currentYear)
          .month(month - 1)
          .endOf('month')
          .format('YYYY-MM-DD 23:59:59');

        const totalListings = await Listings.count({
          where: { host_id: userId },
        });

        if (totalListings === 0) {
          return {
            month: moment()
              .month(month - 1)
              .format('MMMM'),
            rate: '0.00%',
          };
        }

        const bookedListings = await Bookings.findAll({
          attributes: ['listing_id'],
          where: {
            host_id: userId,
            check_in: {
              [Op.lte]: endOfMonth,
            },
            check_out: {
              [Op.gte]: startOfMonth,
            },
          },
          group: ['listing_id'],
        });

        const occupancyRate = (bookedListings.length / totalListings) * 100;

        return {
          month: moment()
            .month(month - 1)
            .format('MMMM'),
          rate: `${occupancyRate.toFixed(2)}%`,
        };
      }),
    );

    return occupancyRates;
  } catch (error) {
    throw new Error('Unable to fetch monthly occupancy rates');
  }
};
