import { env } from '../config/env';
import { BookingRepository } from '../models/repositories/BookingRepository';
import { UserEmailSettingsRepository } from '../models/repositories/UserEmailSettingsRepository';
import { UserEmailSettingsService } from './UserEmailSettingsService';
import { generateIcs } from '../utils/icsGenerator';
import { buildHostOutcomeUrl, buildSurveyUrl } from '../utils/surveyToken';
import { Booking } from '../models/entities/Booking.entity';
import { createPlatformMailSender, SendMailOptions } from './mail/MailSender';

export class NotificationService {
  constructor(
    private bookingRepo = new BookingRepository(),
    private settingsRepo = new UserEmailSettingsRepository(),
    private emailSettingsService = new UserEmailSettingsService(),
  ) {}

  private async resolveMailForBooking(booking: Booking) {
    const settings = await this.settingsRepo.findByUserId(booking.userId);
    if (!this.emailSettingsService.isNotificationsEnabled(settings)) {
      return null;
    }
    const host = booking.user;
    const from = this.emailSettingsService.resolveFrom(settings, host.email, host.fullName);
    const send = (opts: Omit<SendMailOptions, 'from'>) =>
      this.emailSettingsService.sendMail(settings, host.email, host.fullName, {
        from,
        ...opts,
      });
    return { send, from, settings };
  }

  async sendConfirmationEmail(bookingId: string, cancelToken: string): Promise<void> {
    const booking = await this.bookingRepo.findById(bookingId);
    if (!booking || !booking.user || !booking.eventType) return;

    const mail = await this.resolveMailForBooking(booking);
    if (!mail) return;

    const ics = generateIcs(booking, booking.user, booking.eventType, cancelToken);
    const cancelUrl = `${env.bookingAppUrl}/cancel/${cancelToken}`;

    const html = `
      <h2>Booking Confirmed</h2>
      <p>Hi ${booking.guestName},</p>
      <p>Your meeting with ${booking.user.fullName} is confirmed.</p>
      <p><strong>When:</strong> ${booking.startTime.toISOString()}</p>
      <p><strong>Event:</strong> ${booking.eventType.title}</p>
      ${booking.eventType.location ? `<p><strong>Location:</strong> ${booking.eventType.location}</p>` : ''}
      ${booking.eventType.meetingUrl ? `<p><strong>Meeting link:</strong> <a href="${booking.eventType.meetingUrl}">${booking.eventType.meetingUrl}</a></p>` : ''}
      <p>We will send you a friendly reminder before your meeting.</p>
      <p><a href="${cancelUrl}">Cancel booking</a></p>
    `;

    await mail.send({
      to: booking.guestEmail,
      subject: `Confirmed: ${booking.eventType.title} with ${booking.user.fullName}`,
      html,
      attachments: [{ filename: 'booking.ics', content: ics, contentType: 'text/calendar' }],
    });

    await mail.send({
      to: booking.user.email,
      subject: `New booking: ${booking.guestName}`,
      html: `
        <h2>New Booking</h2>
        <p><strong>Guest:</strong> ${booking.guestName} (${booking.guestEmail})</p>
        <p><strong>When:</strong> ${booking.startTime.toISOString()}</p>
        <p><strong>Event:</strong> ${booking.eventType.title}</p>
        <p>Your personal assistant will send reminders and follow-ups automatically.</p>
      `,
      attachments: [{ filename: 'booking.ics', content: ics, contentType: 'text/calendar' }],
    });
  }

  async sendCancellationEmail(bookingId: string, cancelledBy: string): Promise<void> {
    const booking = await this.bookingRepo.findById(bookingId);
    if (!booking || !booking.user || !booking.eventType) return;

    const mail = await this.resolveMailForBooking(booking);
    if (!mail) return;

    const html = `
      <h2>Booking Cancelled</h2>
      <p>The meeting "${booking.eventType.title}" scheduled for ${booking.startTime.toISOString()} has been cancelled by ${cancelledBy}.</p>
    `;

    await mail.send({
      to: booking.guestEmail,
      subject: `Cancelled: ${booking.eventType.title}`,
      html,
    });

    await mail.send({
      to: booking.user.email,
      subject: `Cancelled: ${booking.eventType.title}`,
      html,
    });
  }

  async sendRescheduleEmail(bookingId: string): Promise<void> {
    const booking = await this.bookingRepo.findById(bookingId);
    if (!booking || !booking.user || !booking.eventType) return;

    const mail = await this.resolveMailForBooking(booking);
    if (!mail) return;

    const html = `
      <h2>Booking Rescheduled</h2>
      <p>Your meeting "${booking.eventType.title}" has been rescheduled to ${booking.startTime.toISOString()}.</p>
    `;

    await mail.send({
      to: booking.guestEmail,
      subject: `Rescheduled: ${booking.eventType.title}`,
      html,
    });

    await mail.send({
      to: booking.user.email,
      subject: `Rescheduled: ${booking.eventType.title}`,
      html,
    });
  }

  async sendReminderEmail(bookingId: string, hoursBefore: 24 | 1): Promise<void> {
    const booking = await this.bookingRepo.findById(bookingId);
    if (!booking || !booking.user || !booking.eventType) return;
    if (booking.status !== 'confirmed') return;

    const mail = await this.resolveMailForBooking(booking);
    if (!mail) return;

    const label = hoursBefore === 24 ? '24 hours' : '1 hour';
    const html = `
      <h2>Meeting reminder</h2>
      <p>Hi ${booking.guestName},</p>
      <p>This is a friendly reminder that your meeting <strong>${booking.eventType.title}</strong> with ${booking.user.fullName} starts in about ${label}.</p>
      <p><strong>When:</strong> ${booking.startTime.toISOString()}</p>
      ${booking.eventType.location ? `<p><strong>Location:</strong> ${booking.eventType.location}</p>` : ''}
      ${booking.eventType.meetingUrl ? `<p><strong>Meeting link:</strong> <a href="${booking.eventType.meetingUrl}">${booking.eventType.meetingUrl}</a></p>` : ''}
      <p>See you soon!</p>
    `;

    if (mail.settings?.sendReminderToGuest !== false) {
      await mail.send({
        to: booking.guestEmail,
        subject: `Reminder (${label}): ${booking.eventType.title}`,
        html,
      });
    }

    if (mail.settings?.sendReminderToHost !== false) {
      await mail.send({
        to: booking.user.email,
        subject: `Reminder (${label}): ${booking.guestName} — ${booking.eventType.title}`,
        html: `
          <h2>Upcoming meeting in ${label}</h2>
          <p><strong>Guest:</strong> ${booking.guestName} (${booking.guestEmail})</p>
          <p><strong>When:</strong> ${booking.startTime.toISOString()}</p>
          <p><strong>Event:</strong> ${booking.eventType.title}</p>
        `,
      });
    }

    const patch =
      hoursBefore === 24
        ? { reminder24hSentAt: new Date() }
        : { reminder1hSentAt: new Date() };
    await this.bookingRepo.update(bookingId, patch);
  }

  async sendPostMeetingSurveyEmail(bookingId: string): Promise<void> {
    const booking = await this.bookingRepo.findById(bookingId);
    if (!booking || !booking.user || !booking.eventType) return;
    if (booking.status !== 'confirmed') return;
    if (booking.meetingOutcome === 'no_show') return;

    const mail = await this.resolveMailForBooking(booking);
    if (!mail || mail.settings?.sendPostMeetingSurvey === false) return;

    const surveyUrl = buildSurveyUrl(bookingId);
    const hostUrl = buildHostOutcomeUrl(bookingId);

    await mail.send({
      to: booking.user.email,
      subject: `Did your meeting with ${booking.guestName} take place?`,
      html: `
        <h2>Meeting follow-up</h2>
        <p>Hi ${booking.user.fullName},</p>
        <p>Your meeting <strong>${booking.eventType.title}</strong> with ${booking.guestName} was scheduled for ${booking.startTime.toISOString()}.</p>
        <p>Please confirm whether the meeting was held. This helps us avoid unnecessary follow-ups for no-shows.</p>
        <p><a href="${hostUrl}">Confirm meeting outcome in dashboard</a></p>
      `,
    });

    await mail.send({
      to: booking.guestEmail,
      subject: `How was your meeting with ${booking.user.fullName}?`,
      html: `
        <h2>We hope your meeting went well!</h2>
        <p>Hi ${booking.guestName},</p>
        <p>Thank you for meeting with ${booking.user.fullName}. We'd love your quick feedback.</p>
        <p><a href="${surveyUrl}">Rate your experience</a></p>
      `,
    });

    await this.bookingRepo.update(bookingId, { postMeetingSurveySentAt: new Date() });
  }

  async sendFollowUpEmail(bookingId: string): Promise<void> {
    const booking = await this.bookingRepo.findById(bookingId);
    if (!booking || !booking.user || !booking.eventType) return;
    if (booking.status !== 'confirmed') return;
    if (booking.meetingOutcome === 'no_show') return;
    if (booking.guestRating) return;

    const mail = await this.resolveMailForBooking(booking);
    if (!mail || mail.settings?.sendFollowUpEnabled === false) return;

    const surveyUrl = buildSurveyUrl(bookingId);

    await mail.send({
      to: booking.guestEmail,
      subject: `Following up — ${booking.eventType.title}`,
      html: `
        <p>Hi ${booking.guestName},</p>
        <p>Just checking in after your recent meeting with ${booking.user.fullName}. If you have a moment, we'd appreciate your feedback:</p>
        <p><a href="${surveyUrl}">Share quick feedback</a></p>
        <p>Thank you!</p>
      `,
    });

    await this.bookingRepo.update(bookingId, { followUpSentAt: new Date() });
  }

  async sendSyncFailureEmail(userEmail: string, provider: string, userId?: string): Promise<void> {
    const platform = createPlatformMailSender();
    let from = env.EMAIL_FROM;

    if (userId) {
      const settings = await this.settingsRepo.findByUserId(userId);
      if (settings && this.emailSettingsService.isNotificationsEnabled(settings)) {
        from = this.emailSettingsService.resolveFrom(settings, userEmail, settings.fromName ?? '');
        await this.emailSettingsService.sendMail(settings, userEmail, settings.fromName ?? '', {
          from,
          to: userEmail,
          subject: 'Calendar sync failed — action required',
          html: `<p>Your ${provider} calendar connection needs to be reconnected. Please log in to your dashboard.</p>`,
        });
        return;
      }
    }

    await platform.send({
      from,
      to: userEmail,
      subject: 'Calendar sync failed — action required',
      html: `<p>Your ${provider} calendar connection needs to be reconnected. Please log in to your dashboard.</p>`,
    });
  }
}
