import db from '../database/models/index.js';
import { Op } from 'sequelize';
import {
    RESIGNATION_STAGES,
    ROLES,
    TERMINATION_STAGES
} from '../common/config/constants.js';
import { NotificationService } from './NotificationService.js';
import {
    formatLwdDisplay,
    isLwdReached,
    LWD_FNF_READY_REMINDER_ACTION,
    msUntilLwdMorning
} from '../common/utils/offboardingLwd.js';
import logger from '../common/utils/logger.js';
import { getFrontendBaseUrl } from '../common/utils/frontendUrl.js';

const FNF_PUSH_ROLES = [ROLES.DD_LEAD, ROLES.DD_HEAD, ROLES.NBH, ROLES.DD_ADMIN, ROLES.SUPER_ADMIN];

export type LwdReminderJobPayload = {
    requestType: 'resignation' | 'termination';
    requestId: string;
};

export class OffboardingLwdReminderService {
    static async wasReminderSent(requestType: 'resignation' | 'termination', entityId: string): Promise<boolean> {
        if (requestType === 'resignation') {
            const row = await db.ResignationAudit.findOne({
                where: { resignationId: entityId, action: LWD_FNF_READY_REMINDER_ACTION },
                attributes: ['id']
            });
            return !!row;
        }
        const row = await db.TerminationAudit.findOne({
            where: { terminationRequestId: entityId, action: LWD_FNF_READY_REMINDER_ACTION },
            attributes: ['id']
        });
        return !!row;
    }

    static async notifyAdminsToPushFnf(params: {
        requestType: 'resignation' | 'termination';
        caseCode: string;
        entityId: string;
        dealerName: string;
        lwd: Date | string;
        detailPath: string;
    }): Promise<void> {
        const portalBase = getFrontendBaseUrl();
        const lwdStr = formatLwdDisplay(params.lwd);
        const link = `${portalBase}${params.detailPath}`;

        const admins = await db.User.findAll({
            where: { roleCode: { [Op.in]: FNF_PUSH_ROLES }, isActive: true },
            attributes: ['id', 'email', 'fullName', 'mobileNumber']
        });

        for (const admin of admins) {
            const phone = (admin as any).mobileNumber || '';
            await NotificationService.notify(admin.id, admin.email, {
                title: `LWD reached — Push to F&F: ${params.caseCode}`,
                message: `Last Working Day (${lwdStr}) has been reached for ${params.dealerName}. You may now initiate Full & Final settlement using Push to F&F.`,
                channels: phone ? ['system', 'email', 'whatsapp'] : ['system', 'email'],
                templateCode: 'FNF_LWD_READY',
                placeholders: {
                    recipientName: admin.fullName || 'Team Member',
                    dealerName: params.dealerName,
                    requestId: params.caseCode,
                    requestType: params.requestType === 'resignation' ? 'Resignation' : 'Termination',
                    lwd: lwdStr,
                    link,
                    ctaLabel: 'Open case & Push to F&F',
                    phone: String(phone || '')
                }
            }).catch((e: unknown) =>
                logger.error('[OffboardingLwdReminder] notify failed:', e)
            );
        }

        if (params.requestType === 'resignation') {
            await db.ResignationAudit.create({
                userId: null,
                resignationId: params.entityId,
                action: LWD_FNF_READY_REMINDER_ACTION,
                remarks: `Admin notified: LWD (${lwdStr}) reached — Push to F&F is available.`,
                details: { lwd: lwdStr, link }
            });
        } else {
            await db.TerminationAudit.create({
                userId: null,
                terminationRequestId: params.entityId,
                action: LWD_FNF_READY_REMINDER_ACTION,
                remarks: `Admin notified: LWD (${lwdStr}) reached — Push to F&F is available.`,
                details: { lwd: lwdStr, link }
            });
        }
    }

    /** Process a single resignation or termination (from delayed job or batch). */
    static async processOne(payload: LwdReminderJobPayload): Promise<void> {
        if (payload.requestType === 'resignation') {
            await this.processResignation(payload.requestId);
        } else {
            await this.processTermination(payload.requestId);
        }
    }

    static async processResignation(resignationId: string): Promise<void> {
        const resignation = await db.Resignation.findByPk(resignationId, {
            include: [
                { model: db.User, as: 'dealer', attributes: ['id', 'fullName'] },
                { model: db.Outlet, as: 'outlet', attributes: ['name'] }
            ]
        });
        if (!resignation) return;
        if (resignation.currentStage !== RESIGNATION_STAGES.AWAITING_FNF) return;

        const existingFnf = await db.FnF.findOne({ where: { resignationId: resignation.id } });
        if (existingFnf) return;
        if (await this.wasReminderSent('resignation', resignation.id)) return;

        const lwd =
            resignation.lastOperationalDateServices || resignation.lastOperationalDateSales;
        if (!isLwdReached(lwd)) return;

        const dealerName =
            (resignation as any).dealer?.fullName ||
            (resignation as any).outlet?.name ||
            resignation.resignationId;

        await this.notifyAdminsToPushFnf({
            requestType: 'resignation',
            caseCode: resignation.resignationId,
            entityId: resignation.id,
            dealerName,
            lwd: lwd!,
            detailPath: `/dealer-resignation/${resignation.id}`
        });
    }

    static async processTermination(terminationId: string): Promise<void> {
        const termination = await db.TerminationRequest.findByPk(terminationId, {
            include: [{ model: db.Dealer, as: 'dealer', attributes: ['legalName', 'businessName'] }]
        });
        if (!termination) return;

        const awaitingFnf =
            termination.currentStage === TERMINATION_STAGES.TERMINATED ||
            termination.currentStage === TERMINATION_STAGES.LEGAL_LETTER;
        if (!awaitingFnf) return;

        const status = String(termination.status || '');
        if (status === 'F&F Initiated' || status === 'FNF_INITIATED' || status === 'Settled') return;

        const existingFnf = await db.FnF.findOne({
            where: { terminationRequestId: termination.id }
        });
        if (existingFnf) return;
        if (await this.wasReminderSent('termination', termination.id)) return;

        if (!isLwdReached(termination.proposedLwd)) return;

        if (status === 'Awaiting F&F (LWD Pending)') {
            await termination.update({ status: 'Awaiting F&F' });
        }

        const dealerName =
            (termination as any).dealer?.businessName ||
            (termination as any).dealer?.legalName ||
            termination.requestId;

        await this.notifyAdminsToPushFnf({
            requestType: 'termination',
            caseCode: termination.requestId,
            entityId: termination.id,
            dealerName,
            lwd: termination.proposedLwd,
            detailPath: `/termination/${termination.id}`
        });
    }

    /** Daily / batch sweep for any cases that missed a delayed job. */
    static async processAllPendingReminders(): Promise<{ resignation: number; termination: number }> {
        let resignationCount = 0;
        let terminationCount = 0;

        const resignations = await db.Resignation.findAll({
            where: { currentStage: RESIGNATION_STAGES.AWAITING_FNF },
            attributes: ['id']
        });
        for (const r of resignations) {
            const before = await this.wasReminderSent('resignation', r.id);
            await this.processResignation(r.id);
            const after = await this.wasReminderSent('resignation', r.id);
            if (!before && after) resignationCount++;
        }

        const terminations = await db.TerminationRequest.findAll({
            where: {
                currentStage: { [Op.in]: [TERMINATION_STAGES.TERMINATED, TERMINATION_STAGES.LEGAL_LETTER] },
                status: {
                    [Op.in]: ['Awaiting F&F', 'Awaiting F&F (LWD Pending)', 'Terminated']
                }
            },
            attributes: ['id']
        });
        for (const t of terminations) {
            const before = await this.wasReminderSent('termination', t.id);
            await this.processTermination(t.id);
            const after = await this.wasReminderSent('termination', t.id);
            if (!before && after) terminationCount++;
        }

        logger.info(
            `[OffboardingLwdReminder] Batch complete: ${resignationCount} resignation, ${terminationCount} termination reminders sent`
        );
        return { resignation: resignationCount, termination: terminationCount };
    }

    /** Queue a delayed reminder at LWD morning (no-op if LWD already passed — batch job will pick up). */
    static async scheduleDelayedReminderIfNeeded(
        payload: LwdReminderJobPayload,
        lwd: Date | string
    ): Promise<void> {
        if (process.env.ENABLE_REDIS !== 'true') return;

        const delay = msUntilLwdMorning(lwd);
        if (delay <= 0) {
            await this.processOne(payload);
            return;
        }

        try {
            const { offboardingLwdQueue } = await import('../common/queues/offboarding-lwd.queue.js');
            const jobId = `lwd-fnf-${payload.requestType}-${payload.requestId}`;
            const existing = await offboardingLwdQueue.getJob(jobId);
            if (existing) await existing.remove();

            await offboardingLwdQueue.add('sendLwdFnfReminder', payload, {
                jobId,
                delay,
                removeOnComplete: true,
                removeOnFail: false
            });
            logger.info(
                `[OffboardingLwdReminder] Scheduled reminder in ${Math.round(delay / 3600000)}h for ${payload.requestType} ${payload.requestId}`
            );
        } catch (err) {
            logger.error('[OffboardingLwdReminder] Failed to schedule delayed job:', err);
        }
    }
}
