import { Injectable, Logger } from '@nestjs/common';
import { InjectQueue } from '@nestjs/bullmq';
import { Queue } from 'bullmq';
import { OnEvent } from '@nestjs/event-emitter';
import { PrismaService } from '../../../prisma/prisma.service';
import { BookingEventPayload } from '../core/booking-events.service';

interface NotificationRecord {
  id: string;
  staffId?: string;
  clientName: string;
  clientPhone: string;
  clientEmail?: string | null;
  cancelReason?: string | null;
  startAt: string | Date;
  service?: { name: string } | null;
  staff?: { firstName: string; lastName: string } | null;
}

interface NotificationGroupEvent {
  title: string;
  startAt: string | Date;
}

function extractRecord(data: Record<string, unknown>): NotificationRecord {
  return data['record'] as NotificationRecord;
}

@Injectable()
export class BookingNotificationService {
  private readonly logger = new Logger(BookingNotificationService.name);

  constructor(
    private readonly prisma: PrismaService,
    @InjectQueue('push-notification') private readonly pushQueue: Queue,
    @InjectQueue('email-send') private readonly emailQueue: Queue,
    @InjectQueue('booking-reminders') private readonly reminderQueue: Queue,
  ) {}

  @OnEvent('booking.record.created', { async: true })
  async onRecordCreated(payload: BookingEventPayload) {
    const record = extractRecord(payload.data);

    if (record.staffId) {
      await this.notifyStaff(record.staffId, {
        title: 'Новая запись',
        body: `${record.clientName} — ${record.service?.name ?? 'услуга'}, ${this.formatDate(record.startAt)}`,
        data: { type: 'booking_record', recordId: record.id },
      });
    }

    if (record.clientEmail) {
      await this.sendEmail(record.clientEmail, 'booking_confirmation', {
        clientName: record.clientName,
        serviceName: record.service?.name,
        staffName: record.staff ? `${record.staff.firstName} ${record.staff.lastName}` : undefined,
        date: this.formatDate(record.startAt),
        time: this.formatTime(record.startAt),
      });
    }

    await this.scheduleReminders(record);
  }

  @OnEvent('booking.record.confirmed', { async: true })
  async onRecordConfirmed(payload: BookingEventPayload) {
    const record = extractRecord(payload.data);

    if (record.clientEmail) {
      await this.sendEmail(record.clientEmail, 'booking_confirmed', {
        clientName: record.clientName,
        serviceName: record.service?.name,
        date: this.formatDate(record.startAt),
        time: this.formatTime(record.startAt),
      });
    }
  }

  @OnEvent('booking.record.cancelled', { async: true })
  async onRecordCancelled(payload: BookingEventPayload) {
    const record = extractRecord(payload.data);

    if (record.staffId) {
      await this.notifyStaff(record.staffId, {
        title: 'Запись отменена',
        body: `${record.clientName} — ${record.service?.name ?? 'услуга'}, ${this.formatDate(record.startAt)}`,
        data: { type: 'booking_cancelled', recordId: record.id },
      });
    }

    if (record.clientEmail) {
      await this.sendEmail(record.clientEmail, 'booking_cancelled', {
        clientName: record.clientName,
        serviceName: record.service?.name,
        date: this.formatDate(record.startAt),
        cancelReason: record.cancelReason,
      });
    }

    await this.removeReminders(record.id);
  }

  @OnEvent('booking.record.completed', { async: true })
  async onRecordCompleted(payload: BookingEventPayload) {
    const record = extractRecord(payload.data);

    if (record.clientEmail) {
      await this.sendEmail(record.clientEmail, 'booking_completed', {
        clientName: record.clientName,
        serviceName: record.service?.name,
      });
    }
  }

  @OnEvent('booking.group_event.cancelled', { async: true })
  async onGroupEventCancelled(payload: BookingEventPayload) {
    const groupEvent = payload.data['groupEvent'] as NotificationGroupEvent | undefined;
    const affectedRecords = payload.data['affectedRecords'] as NotificationRecord[] | undefined;

    if (Array.isArray(affectedRecords) && groupEvent) {
      for (const record of affectedRecords) {
        if (record.clientEmail) {
          await this.sendEmail(record.clientEmail, 'group_event_cancelled', {
            clientName: record.clientName,
            eventTitle: groupEvent.title,
            date: this.formatDate(groupEvent.startAt),
          });
        }
      }
    }
  }

  // ─── Reminder Scheduling ───────────────────────────────

  private async scheduleReminders(record: NotificationRecord) {
    const startAt = new Date(record.startAt).getTime();
    const now = Date.now();

    const reminder24h = startAt - 24 * 60 * 60 * 1000;
    if (reminder24h > now) {
      await this.reminderQueue.add(
        'reminder:24h',
        {
          recordId: record.id,
          clientName: record.clientName,
          clientEmail: record.clientEmail,
          clientPhone: record.clientPhone,
          serviceName: record.service?.name,
          startAt: record.startAt,
          type: '24h',
        },
        {
          delay: reminder24h - now,
          jobId: `reminder:24h:${record.id}`,
          removeOnComplete: true,
        },
      );
    }

    const reminder2h = startAt - 2 * 60 * 60 * 1000;
    if (reminder2h > now) {
      await this.reminderQueue.add(
        'reminder:2h',
        {
          recordId: record.id,
          clientName: record.clientName,
          clientEmail: record.clientEmail,
          clientPhone: record.clientPhone,
          serviceName: record.service?.name,
          startAt: record.startAt,
          type: '2h',
        },
        {
          delay: reminder2h - now,
          jobId: `reminder:2h:${record.id}`,
          removeOnComplete: true,
        },
      );
    }
  }

  private async removeReminders(recordId: string) {
    try {
      const job24 = await this.reminderQueue.getJob(`reminder:24h:${recordId}`);
      if (job24) await job24.remove();

      const job2h = await this.reminderQueue.getJob(`reminder:2h:${recordId}`);
      if (job2h) await job2h.remove();
    } catch {
      // Jobs may already be processed or removed
    }
  }

  // ─── Channel helpers ───────────────────────────────────

  private async notifyStaff(
    staffId: string,
    notification: { title: string; body: string; data: Record<string, string> },
  ) {
    await this.pushQueue.add('push:booking', {
      userId: staffId,
      ...notification,
    });
  }

  private async sendEmail(to: string, template: string, variables: Record<string, unknown>) {
    await this.emailQueue.add('email:booking', {
      to,
      template: `booking/${template}`,
      variables,
    });
  }

  // ─── Formatting ────────────────────────────────────────

  private formatDate(dateStr: string | Date): string {
    return new Date(dateStr).toLocaleDateString('ru-RU', {
      day: 'numeric',
      month: 'long',
      year: 'numeric',
    });
  }

  private formatTime(dateStr: string | Date): string {
    return new Date(dateStr).toLocaleTimeString('ru-RU', {
      hour: '2-digit',
      minute: '2-digit',
    });
  }
}
