const { Expo } = require('expo-server-sdk');
const { PushToken, User, Booking, UserNotification } = require('../models');
const { Op } = require('sequelize');

const expo = new Expo();

const sendPush = async (pushToken, title, body, data = {}) => {
  if (!Expo.isExpoPushToken(pushToken)) {
    console.log(`[Push] Invalid token: ${pushToken}`);
    return;
  }

  try {
    const message = {
      to: pushToken,
      sound: 'default',
      title,
      body,
      data,
      channelId: data.channelId || 'default',
    };

    const ticket = await expo.sendPushNotificationsAsync([message]);
    console.log(`[Push] Sent to ${pushToken}: ${ticket[0]?.status}`);
    return ticket[0];
  } catch (error) {
    console.error('[Push] Send error:', error.message);
  }
};

const createInAppNotification = async (userEmail, userId, type, title, body, data) => {
  try {
    await UserNotification.create({
      user_email: userEmail,
      user_id: userId || 0,
      type,
      title,
      body,
      data: data ? JSON.stringify(data) : null,
    });
  } catch (e) {
    console.error('[Notif] In-app create error:', e.message);
  }
};

const sendPushAndNotify = async (pushToken, userEmail, userId, type, title, body, data = {}) => {
  await sendPush(pushToken, title, body, data);
  await createInAppNotification(userEmail, userId, type, title, body, data);
};

const notifyBookingStatusChange = async (booking) => {
  const statusLabels = {
    pending: 'Pending',
    POP_uploaded: 'POP Uploaded',
    pending_approval: 'Pending Approval',
    confirmed: 'Confirmed',
    completed: 'Completed',
    cancelled: 'Cancelled',
  };

  const label = statusLabels[booking.booking_status] || booking.booking_status;
  const customerEmail = booking.customer_email;
  const reference = booking.booking_reference;

  const title = `Booking ${label}`;
  const body = `Your booking ${reference} is now ${label.toLowerCase()}.`;
  const data = { type: 'booking_status', bookingId: booking.id, bookingRef: reference, screen: 'BookingDetail' };

  const tokens = await PushToken.findAll({ where: { email: customerEmail } });
  for (const t of tokens) {
    await sendPush(t.token, title, body, data);
  }

  const adminUsers = await User.findAll({ where: { role: { [Op.in]: ['admin', 'manager'] } } });
  const adminEmails = adminUsers.map((u) => u.email);
  if (adminEmails.length > 0) {
    const adminTokens = await PushToken.findAll({ where: { email: adminEmails } });
    const adminTitle = `Booking ${label}`;
    const adminBody = `Booking ${reference} by ${booking.customer_name} is now ${label.toLowerCase()}.`;
    for (const t of adminTokens) {
      await sendPush(t.token, adminTitle, adminBody, { ...data, role: 'admin' });
    }
  }

  const EmailService = require('./EmailService');
  await EmailService.sendEmail(
    booking.customer_email,
    `Booking ${label} - ${reference}`,
    `<p>Hi ${booking.customer_name},</p><p>Your booking <strong>${reference}</strong> is now <strong>${label.toLowerCase()}</strong>.</p><p>Check the app for details.</p>`
  );
};

const notifyNewBooking = async (booking) => {
  const title = 'New Booking';
  const body = `${booking.customer_name} booked ${booking.booking_reference} - ${booking.number_of_passengers} passenger(s).`;
  const data = { type: 'new_booking', bookingId: booking.id, bookingRef: booking.booking_reference, screen: 'AdminBookingDetail' };

  const adminUsers = await User.findAll({ where: { role: { [Op.in]: ['admin', 'manager'] } } });
  const adminEmails = adminUsers.map((u) => u.email);

  const adminTokens = await PushToken.findAll({ where: { email: adminEmails } });
  for (const t of adminTokens) {
    await sendPush(t.token, title, body, data);
  }

  const subject = `New Booking - ${booking.booking_reference}`;
  const htmlBody = `${booking.customer_name} booked ${booking.booking_reference} - ${booking.number_of_passengers} passenger(s).`;
  for (const u of adminUsers) {
    const EmailService = require('./EmailService');
    await EmailService.sendEmail(u.email, subject, `<p>${htmlBody}</p>`);
  }
};

const notifyBookingReminder = async (booking, hoursUntil) => {
  const title = 'Booking Reminder';
  const body = `Your booking ${booking.booking_reference} departs in ${hoursUntil} hour(s).`;
  const data = { type: 'reminder', bookingId: booking.id, bookingRef: booking.booking_reference, screen: 'BookingDetail' };

  const tokens = await PushToken.findAll({ where: { email: booking.customer_email } });
  for (const t of tokens) {
    await sendPush(t.token, title, body, data);
  }

  const EmailService = require('./EmailService');
  await EmailService.notifyTripReminder(
    booking.customer_email,
    booking.customer_name,
    booking.booking_reference,
    booking.schedule?.departure_day || 'Unknown',
    booking.schedule?.departure_time || 'Unknown',
    hoursUntil
  );
};

const notifyNewTrip = async (schedule) => {
  const title = 'New Trip Available';
  const routeName = `${schedule.route?.origin || ''} → ${schedule.route?.destination || ''}`;
  const body = `${routeName} - ${schedule.departure_day}s at ${schedule.departure_time}`;
  const data = { type: 'new_trip', scheduleId: schedule.id, screen: 'TripDetail' };

  const tokens = await PushToken.findAll();
  const sentEmails = new Set();
  for (const t of tokens) {
    await sendPush(t.token, title, body, data);
    if (!sentEmails.has(t.email)) {
      sentEmails.add(t.email);
      const user = await User.findOne({ where: { email: t.email } });
      if (user) {
        const EmailService = require('./EmailService');
        await EmailService.notifyNewTripAvailable(
          t.email,
          user.full_name || 'Valued Customer',
          routeName,
          schedule.departure_day,
          schedule.departure_time,
          schedule.price_per_seat
        );
      }
    }
  }
};

const notifyTripStarted = async (booking) => {
  const title = 'Trip Started';
  const body = `Your trip ${booking.booking_reference} has started! Track in real-time.`;
  const data = { type: 'trip_start', bookingId: booking.id, bookingRef: booking.booking_reference, screen: 'TripDetail', scheduleId: booking.schedule_id };

  const tokens = await PushToken.findAll({ where: { email: booking.customer_email } });
  for (const t of tokens) {
    await sendPush(t.token, title, body, data);
  }

  const EmailService = require('./EmailService');
  await EmailService.notifyTripStatus(booking.customer_email, booking.customer_name, booking.booking_reference, 'Started');
};

const notifyTripEnded = async (booking) => {
  const title = 'Trip Ended';
  const body = `Your trip ${booking.booking_reference} has ended. We hope you enjoyed the journey!`;
  const data = { type: 'trip_end', bookingId: booking.id, bookingRef: booking.booking_reference, screen: 'BookingDetail' };

  const tokens = await PushToken.findAll({ where: { email: booking.customer_email } });
  for (const t of tokens) {
    await sendPush(t.token, title, body, data);
  }

  const EmailService = require('./EmailService');
  await EmailService.notifyTripStatus(booking.customer_email, booking.customer_name, booking.booking_reference, 'Ended');
};

module.exports = { sendPush, createInAppNotification, notifyBookingStatusChange, notifyNewBooking, notifyBookingReminder, notifyNewTrip, notifyTripStarted, notifyTripEnded };
