import db from '../database/models/index.js';
import { Op } from 'sequelize';
import { APPLICATION_STATUS, AUDIT_ACTIONS } from '../common/config/constants.js';
import { NotificationService } from './NotificationService.js';
import { safeAuditLogCreate } from './applicationAuditLog.service.js';
import { pickApplicationAuditContext } from './applicationAuditLog.service.js';
import logger from '../common/utils/logger.js';
import { getQuestionnaireReminderSettings } from './questionnaireReminderSettings.js';
import { getFrontendBaseUrl } from '../common/utils/frontendUrl.js';

const TEMPLATE = 'QUESTIONNAIRE_REMINDER';

export class QuestionnaireReminderService {
    static async isEnabled(): Promise<boolean> {
        return (await getQuestionnaireReminderSettings()).enabled;
    }

    static async getIntervalDays(): Promise<number> {
        return (await getQuestionnaireReminderSettings()).intervalDays;
    }

    static async getFirstReminderAfterDays(): Promise<number> {
        return (await getQuestionnaireReminderSettings()).firstAfterDays;
    }

    static async getMaxReminders(): Promise<number> {
        return (await getQuestionnaireReminderSettings()).maxCount;
    }

    static async getPendingSince(applicationId: string, fallbackCreatedAt: Date): Promise<Date> {
        const row = await db.ApplicationStatusHistory.findOne({
            where: {
                applicationId,
                newStatus: APPLICATION_STATUS.QUESTIONNAIRE_PENDING
            },
            order: [['createdAt', 'ASC']],
            attributes: ['createdAt']
        });
        return row?.createdAt ? new Date(row.createdAt) : new Date(fallbackCreatedAt);
    }

    static async countQuestionnaireRemindersSent(applicationId: string): Promise<number> {
        const logs = await db.AuditLog.findAll({
            where: {
                entityType: 'application',
                entityId: applicationId,
                action: AUDIT_ACTIONS.REMINDER_SENT
            },
            attributes: ['newData', 'createdAt'],
            order: [['createdAt', 'DESC']]
        });
        return logs.filter((log: any) => log.newData?.template === TEMPLATE).length;
    }

    static async getLastQuestionnaireReminderAt(applicationId: string): Promise<Date | null> {
        const logs = await db.AuditLog.findAll({
            where: {
                entityType: 'application',
                entityId: applicationId,
                action: AUDIT_ACTIONS.REMINDER_SENT
            },
            attributes: ['newData', 'createdAt'],
            order: [['createdAt', 'DESC']],
            limit: 20
        });
        const hit = logs.find((log: any) => log.newData?.template === TEMPLATE);
        return hit?.createdAt ? new Date(hit.createdAt) : null;
    }

    static async sendReminderForApplication(application: any, source: 'scheduled' | 'manual' = 'scheduled') {
        const link = `${getFrontendBaseUrl()}/questionnaire/${application.applicationId}`;

        await NotificationService.sendQuestionnaireReminder(
            application.email,
            application.phone || application.mobileNumber,
            application.applicantName,
            {
                location: application.preferredLocation,
                link
            }
        );

        await safeAuditLogCreate({
            userId: null,
            action: AUDIT_ACTIONS.REMINDER_SENT,
            entityType: 'application',
            entityId: application.id,
            newData: {
                template: TEMPLATE,
                sentAt: new Date().toISOString(),
                source,
                context: pickApplicationAuditContext(application)
            }
        });
    }

    /**
     * BullMQ sweep: email/WhatsApp prospects still in Questionnaire Pending.
     * Not part of internal SLA matrix — uses same template as DD Admin bulk reminder.
     */
    static async processScheduledReminders(): Promise<{ scanned: number; sent: number; skipped: number }> {
        if (!(await this.isEnabled())) {
            logger.info('[QuestionnaireReminder] Scheduler disabled');
            return { scanned: 0, sent: 0, skipped: 0 };
        }

        const intervalDays = await this.getIntervalDays();
        const firstAfterDays = await this.getFirstReminderAfterDays();
        const maxReminders = await this.getMaxReminders();
        const now = Date.now();
        const msDay = 24 * 60 * 60 * 1000;

        const applications = await db.Application.findAll({
            where: {
                overallStatus: APPLICATION_STATUS.QUESTIONNAIRE_PENDING,
                email: { [Op.ne]: null }
            },
            attributes: [
                'id',
                'applicationId',
                'email',
                'phone',
                'mobileNumber',
                'applicantName',
                'preferredLocation',
                'createdAt'
            ]
        });

        let sent = 0;
        let skipped = 0;

        for (const app of applications) {
            const pendingSince = await this.getPendingSince(app.id, app.createdAt);
            const daysPending = (now - pendingSince.getTime()) / msDay;

            if (daysPending < firstAfterDays) {
                skipped++;
                continue;
            }

            const reminderCount = await this.countQuestionnaireRemindersSent(app.id);
            if (reminderCount >= maxReminders) {
                skipped++;
                continue;
            }

            const lastAt = await this.getLastQuestionnaireReminderAt(app.id);
            if (lastAt) {
                const daysSinceLast = (now - lastAt.getTime()) / msDay;
                if (daysSinceLast < intervalDays) {
                    skipped++;
                    continue;
                }
            }

            try {
                await this.sendReminderForApplication(app, 'scheduled');
                sent++;
                logger.info(
                    `[QuestionnaireReminder] Sent to ${app.applicationId} (${app.email}) — reminder #${reminderCount + 1}`
                );
            } catch (err: any) {
                skipped++;
                logger.error(
                    `[QuestionnaireReminder] Failed for ${app.applicationId}: ${err?.message || err}`
                );
            }
        }

        logger.info(
            `[QuestionnaireReminder] Sweep complete: scanned=${applications.length} sent=${sent} skipped=${skipped}`
        );
        return { scanned: applications.length, sent, skipped };
    }
}
