import { Job } from 'bullmq';
import { createWorker } from '../config/redis';
import { NotificationJobData } from './NotificationQueueService';
import Appointment from '../models/Appointment';
import NotificationLog, {
    NotificationPurpose,
    NotificationStatus,
    NotificationType
} from '../models/NotificationLog';
import EmailService from './EmailService';
import WhatsAppService from './WhatsAppService';

// ─── Worker Service (Consumer) ───

class NotificationWorkerService {
    private readonly QUEUE_NAME = 'appointment-notifications';

    constructor() {
        this.initWorker();
    }

    private initWorker() {
        const worker = createWorker(this.QUEUE_NAME, async (job: Job<NotificationJobData>) => {
            console.log(`[Worker] Processing job ${job.id}: ${job.name}`);
            await this.processJob(job);
        }, {
            concurrency: 5
        });

        // Worker event listeners for observability
        worker.on('completed', (job: Job) => {
            console.log(`[Worker] Job ${job.id} completed`);
        });

        worker.on('failed', (job: Job | undefined, err: Error) => {
            console.error(`[Worker] Job ${job?.id} failed:`, err.message);
        });

        console.log('[Worker] Notification Worker initialized');
    }

    // ─── Core Job Processing Pipeline ───

    private async processJob(job: Job<NotificationJobData>) {
        const { appointmentId, purpose, scheduledTime, notificationVersion } = job.data;

        // ── Step 1: Fetch current appointment state ──
        const appointment = await Appointment.findById(appointmentId).populate({
            path: 'patientId',
            populate: { path: 'userId' }
        }).populate({
            path: 'doctorId',
            populate: { path: 'userId' }
        });

        if (!appointment) {
            console.log(`[Worker] Appointment ${appointmentId} not found. Skipping.`);
            return;
        }

        // ── Step 2: Validate appointment state ──
        // Check if appointment was cancelled
        if (appointment.status === 'Cancelled') {
            console.log(`[Worker] Appointment ${appointmentId} is Cancelled. Skipping.`);
            return;
        }

        // Check if notification version has changed (reschedule happened)
        // This is a defense-in-depth: if removeScheduledJobs() missed this job,
        // the version mismatch will catch it.
        if (appointment.notificationVersion !== notificationVersion) {
            console.log(`[Worker] Stale job: version mismatch (job: ${notificationVersion}, current: ${appointment.notificationVersion}). Skipping.`);
            return;
        }

        // Check if appointment time changed (belt-and-suspenders with version check)
        const currentTime = new Date(appointment.appointmentDate).toISOString();
        if (currentTime !== scheduledTime) {
            console.log(`[Worker] Appointment time changed. Skipping obsolete job.`);
            return;
        }

        // ── Step 3: Idempotency check per channel ──
        const emailIdempotencyKey = `${appointmentId}-${purpose}-${NotificationType.EMAIL}-v${notificationVersion}`;
        const whatsappIdempotencyKey = `${appointmentId}-${purpose}-${NotificationType.WHATSAPP}-v${notificationVersion}`;
        const hasEmail = Boolean((appointment.patientId as any)?.userId?.email);
        const hasPhone = Boolean((appointment.patientId as any)?.userId?.phone);
        const whatsappEnabled = WhatsAppService.isEnabled();

        if (!hasEmail) {
            console.warn(`[Worker] No email found for appointment ${appointmentId}. Skipping.`);
            return;
        }

        const [existingEmailLog, existingWhatsAppLog] = await Promise.all([
            NotificationLog.findOne({ idempotencyKey: emailIdempotencyKey }),
            NotificationLog.findOne({ idempotencyKey: whatsappIdempotencyKey })
        ]);

        const emailAlreadySent = existingEmailLog?.status === NotificationStatus.SENT || !hasEmail;
        const whatsappAlreadySent = existingWhatsAppLog?.status === NotificationStatus.SENT || !hasPhone || !whatsappEnabled;

        if (emailAlreadySent) {
            console.log(`[Worker] Email notification already sent for ${appointmentId}/${purpose}. Skipping.`);
            return;
        }

        // ── Step 4: Extract recipient details ──
        const patient = appointment.patientId as any;
        const doctor = appointment.doctorId as any;

        const patientEmail = patient.userId.email;
        const patientPhone = patient.userId.phone;
        const patientName = `${patient.userId.firstName} ${patient.userId.lastName}`;
        const patientLang = patient.userId.language || 'en';
        const doctorName = doctor?.userId?.firstName
            ? `${doctor.userId.firstName} ${doctor.userId.lastName}`
            : 'Your Doctor';
        const appointmentDate = new Date(appointment.appointmentDate).toLocaleString();

        // ── Step 5: Send notification ──
        try {
            let emailSent = emailAlreadySent;
            let whatsappSent = whatsappAlreadySent;

            switch (purpose) {
                case NotificationPurpose.APPOINTMENT_REMINDER_24H:
                case NotificationPurpose.APPOINTMENT_REMINDER_4H:
                case NotificationPurpose.APPOINTMENT_REMINDER_2H:
                case NotificationPurpose.APPOINTMENT_REMINDER_AT_TIME:
                    if (!emailAlreadySent) {
                        console.log(`[Worker] Sending ${purpose} email to ${patientEmail}`);
                        emailSent = await EmailService.sendAppointmentReminder(
                            patientEmail,
                            patientName,
                            appointmentDate,
                            doctorName,
                            patientLang
                        );
                    }

                    // WhatsApp send call intentionally disabled for now.
                    break;

                case NotificationPurpose.APPOINTMENT_CONFIRMATION:
                    if (!emailAlreadySent) {
                        console.log(`[Worker] Sending confirmation email to ${patientEmail}`);
                        emailSent = await EmailService.sendAppointmentConfirmation(
                            patientEmail,
                            patientName,
                            appointmentDate,
                            doctorName,
                            patientLang
                        );
                    }

                    // WhatsApp send call intentionally disabled for now.
                    break;

                default:
                    console.warn(`[Worker] Unknown purpose: ${purpose}`);
                    emailSent = false;
                    whatsappSent = false;
            }

            if (!emailAlreadySent) {
                await NotificationLog.findOneAndUpdate(
                    { idempotencyKey: emailIdempotencyKey },
                    {
                        appointmentId: appointment._id,
                        userId: patient.userId._id,
                        jobId: job.id,
                        type: NotificationType.EMAIL,
                        purpose,
                        status: emailSent ? NotificationStatus.SENT : NotificationStatus.FAILED,
                        sentAt: emailSent ? new Date() : undefined,
                        idempotencyKey: emailIdempotencyKey
                    },
                    { upsert: true, new: true }
                );
            }

            const attemptedEmail = !emailAlreadySent;
            const attemptedWhatsApp = false;
            const emailFailed = attemptedEmail && !emailSent;
            const whatsappFailed = attemptedWhatsApp && !whatsappSent;

            if (emailFailed || whatsappFailed) {
                throw new Error(`Notification delivery failed for purpose: ${purpose}`);
            }

            void hasPhone;
            void whatsappEnabled;
            void existingWhatsAppLog;
            void whatsappAlreadySent;
            void patientPhone;
            void whatsappIdempotencyKey;

            console.log(`[Worker] ✓ ${purpose} notification sent for appointment ${appointmentId}`);

        } catch (error: any) {
            console.error(`[Worker] ✗ Failed to send ${purpose} for ${appointmentId}:`, error.message);

            // Re-throw to trigger BullMQ retry
            throw error;
        }
    }
}

export default NotificationWorkerService;
