import db from '../database/models/index.js';
const { SLATracking, SLAConfiguration, SLABreach, Application, User, SLAReminder, SLAEscalationConfig } = db;
import { Op } from 'sequelize';
import { NotificationService } from './NotificationService.js';

export class SLAService {
    /**
     * Periodically check for SLA breaches, reminders and escalations
     */
    static async checkBreaches() {
        console.log('[SLA Service] Starting SLA status check...');
        const now = new Date();
        
        // 1. Handle Active Tracks (Reminders and Initial Breach)
        const activeTracking = await SLATracking.findAll({
            where: { isActive: true, endTime: null },
            include: [{ model: Application, as: 'application' }]
        });

        for (const track of activeTracking) {
            const config = await SLAConfiguration.findOne({
                where: { activityName: track.stageName, isActive: true },
                include: [
                    { model: SLAReminder, as: 'reminders' },
                    { model: SLAEscalationConfig, as: 'escalationConfigs' }
                ]
            });

            if (!config) continue;

            const startTime = new Date(track.startTime);
            const tatMs = this.getTatInMs(config.tatHours, config.tatUnit);
            const deadline = new Date(startTime.getTime() + tatMs);

            // CASE A: Not Breached Yet - Check Reminders
            if (!track.isBreached && now < deadline) {
                await this.processReminders(track, config, now, deadline);
            } 
            // CASE B: Just Breached - Mark it and trigger Level 1 Escalation
            else if (!track.isBreached && now >= deadline) {
                await this.triggerBreach(track, now);
            }
            // CASE C: Already Breached - Check for Escalations
            else if (track.isBreached) {
                await this.processEscalations(track, config, now, deadline);
            }
        }
    }

    /**
     * Start tracking SLA for a new stage
     */
    static async startTrack(applicationId: string, stageName: string) {
        console.log(`[SLA Service] Starting SLA track for App: ${applicationId}, Stage: ${stageName}`);
        
        // Ensure NO other active tracks for this application exist
        await SLATracking.update(
            { isActive: false, endTime: new Date() },
            { where: { applicationId, isActive: true, endTime: null } }
        );

        const config = await SLAConfiguration.findOne({
            where: { activityName: stageName, isActive: true }
        });

        if (config) {
            await SLATracking.create({
                applicationId,
                stageName,
                startTime: new Date(),
                isActive: true
            });
        }
    }

    /**
     * Stop tracking SLA for a stage
     */
    static async stopTrack(applicationId: string, stageName: string) {
        console.log(`[SLA Service] Stopping SLA track for App: ${applicationId}, Stage: ${stageName}`);
        await SLATracking.update(
            { isActive: false, endTime: new Date() },
            { where: { applicationId, stageName, isActive: true } }
        );
    }

    private static getTatInMs(value: number, unit: string): number {
        let factor = unit === 'days' ? 24 * 60 * 60 * 1000 : 60 * 60 * 1000;
        
        // Debug Mode: 1 hour = 1 minute (60x speedup)
        if (process.env.DEBUG_SLA_FAST_MODE === 'true') {
            factor = factor / 60;
        }
        
        return value * factor;
    }

    private static async processReminders(track: any, config: any, now: Date, deadline: Date) {
        const msRemaining = deadline.getTime() - now.getTime();
        
        for (const reminder of config.reminders || []) {
            const reminderMs = this.getTatInMs(reminder.timeValue, reminder.timeUnit);
            
            if (msRemaining <= reminderMs) {
                const metadata = track.metadata || {};
                const reminderKey = `reminder_sent_${reminder.id}`;
                
                if (!metadata[reminderKey]) {
                    const timeStr = `${reminder.timeValue} ${reminder.timeUnit}`;
                    console.log(`[SLA Service] Sending reminder for ${track.stageName} (${timeStr} remaining)`);
                    
                    await this.notifyStakeholder(track, 'SLA_REMINDER', {
                        title: `SLA Reminder: ${track.stageName}`,
                        message: `The application ${track.application?.applicationId} is approaching its SLA deadline for ${track.stageName}.`
                    });

                    // §9.4.1 — Auto-log in Work Notes
                    await this.logWorkNote(track.applicationId, `[SLA] Reminder sent: ${track.stageName} has ${timeStr} remaining.`);
                    
                    metadata[reminderKey] = true;
                    await track.update({ metadata });
                }
            }
        }
    }

    private static async triggerBreach(track: any, now: Date) {
        console.log(`[SLA Service] Breach detected for ${track.stageName}: ${track.application?.applicationId}`);
        await track.update({ isBreached: true });
        
        await SLABreach.create({
            trackingId: track.id,
            applicationId: track.applicationId,
            stageCode: track.stageName,
            breachedAt: now,
            severity: 'High',
            status: 'Open'
        });

        await this.notifyStakeholder(track, 'SLA_BREACH', {
            title: `SLA BREACHED: ${track.stageName}`,
            message: `The application ${track.application?.applicationId} has breached its SLA for ${track.stageName}.`
        });

        // §9.4.1 — Auto-log in Work Notes
        await this.logWorkNote(track.applicationId, `[SLA] BREACH: ${track.stageName} has exceeded its Turnaround Time (TAT).`);
    }

    private static async processEscalations(track: any, config: any, now: Date, deadline: Date) {
        const msSinceBreach = now.getTime() - deadline.getTime();

        for (const esc of config.escalationConfigs || []) {
            const escMs = this.getTatInMs(esc.timeValue, esc.timeUnit);
            
            if (msSinceBreach >= escMs) {
                const metadata = track.metadata || {};
                const escKey = `esc_sent_L${esc.level}`;

                if (!metadata[escKey]) {
                    console.log(`[SLA Service] Escalating ${track.stageName} to Level ${esc.level} (Role: ${esc.notifyRole || 'N/A'}, Email: ${esc.notifyEmail || 'N/A'})`);
                    
                    const { User, Application, District, Region, Zone } = db;
                    let targetEmail = esc.notifyEmail;
                    let recipientId = null;

                    // Runtime Resolution: Resolve role to a specific user/email if role is provided
                    if (esc.notifyRole) {
                        const app = await Application.findByPk(track.applicationId, {
                            include: [{
                                model: District,
                                as: 'district',
                                include: [
                                    { model: Region, as: 'region' },
                                    { model: Zone, as: 'zone' }
                                ]
                            }]
                        });

                        if (app?.district) {
                            const d = app.district;
                            const r = d.region;
                            const z = d.zone;

                            // Map geography-bound roles
                            const roleMap: Record<string, string | null> = {
                                'ASM': d.asmId,
                                'DD-ZM': d.zmId,
                                'RBM': r?.rbmId || null,
                                'ZBH': z?.zbhId || null
                            };

                            if (roleMap[esc.notifyRole]) {
                                recipientId = roleMap[esc.notifyRole];
                            }
                        }

                        // Fallback/National roles: Resolve by roleCode singleton
                        if (!recipientId) {
                            const user = await User.findOne({ 
                                where: { roleCode: esc.notifyRole, status: 'active' },
                                order: [['createdAt', 'DESC']]
                            });
                            if (user) recipientId = user.id;
                        }
                    }

                    // Resolve final email and phone if we have a recipientId
                    let phone = null;
                    if (recipientId) {
                        const user = await User.findByPk(recipientId);
                        if (user) {
                            targetEmail = user.email;
                            phone = user.mobileNumber || user.phone || null;
                        }
                    }

                    if (targetEmail) {
                        await NotificationService.notify(recipientId, targetEmail, {
                            title: `SLA ESCALATION [L${esc.level}]: ${track.stageName}`,
                            message: `The application ${track.application?.applicationId} remains incomplete after SLA breach for ${track.stageName}.`,
                            channels: phone ? ['email', 'system', 'whatsapp'] : ['email', 'system'],
                            templateCode: 'SLA_ESCALATION',
                            placeholders: {
                                applicationId: track.application?.applicationId || '',
                                stageName: track.stageName,
                                level: esc.level,
                                timeValue: esc.timeValue,
                                timeUnit: esc.timeUnit,
                                phone: phone || ''
                            }
                        });
                    }

                    // §9.4.1 — Auto-log in Work Notes
                    await this.logWorkNote(track.applicationId, `[SLA] ESCALATION Level ${esc.level}: Escalated to ${esc.notifyEmail} for stage ${track.stageName}.`);

                    metadata[escKey] = true;
                    await track.update({ metadata });
                }
            }
        }
    }

    private static async logWorkNote(applicationId: string, text: string) {
        try {
            const { Worknote, User } = db;
            // Find a system user or admin to be the author
            const admin = await User.findOne({ where: { role: 'Super Admin' } });
            await Worknote.create({
                applicationId,
                userId: admin?.id || (await User.findOne())?.id,
                noteText: text,
                noteType: 'system',
                status: 'active'
            });
        } catch (err) {
            console.error('[SLA Service] Failed to log work note:', err);
        }
    }

    private static async notifyStakeholder(track: any, template: string, content: { title: string, message: string }) {
        const { Application, User, SLAConfiguration } = db;
        
        // 1. Get the configuration for this stage to find the Owner Role(s)
        const config = await SLAConfiguration.findOne({ where: { activityName: track.stageName, isActive: true } });
        if (!config || !config.ownerRole) return;

        // 2. Resolve multiple roles (comma-separated)
        const roles = config.ownerRole.split(',').map((r: string) => r.trim());
        const application = await Application.findByPk(track.applicationId, {
            include: [{ model: db.District, as: 'district', include: [{ model: db.Region, as: 'region' }, { model: db.Zone, as: 'zone' }] }]
        });

        if (!application) return;

        const recipientIds = new Set<string>();
        const portalBase = process.env.FRONTEND_URL || 'http://localhost:5173';

        for (const role of roles) {
            let foundUserId = null;

            // Resolve geography-bound roles
            if (application.district) {
                const d = application.district;
                const roleMap: Record<string, string | null> = {
                    'ASM': d.asmId,
                    'DD-ZM': d.zmId,
                    'RBM': d.region?.rbmId || null,
                    'ZBH': d.zone?.zbhId || null
                };
                if (roleMap[role]) foundUserId = roleMap[role];
            }

            if (foundUserId) {
                recipientIds.add(foundUserId);
            } else {
                // Fallback: Resolve all active users with this role
                const users = await User.findAll({ where: { roleCode: role, status: 'active' } });
                users.forEach((u: any) => recipientIds.add(u.id));
            }
        }

        // 3. Send notifications to all resolved recipients
        for (const userId of recipientIds) {
            const user = await User.findByPk(userId);
            if (!user) continue;

            const phone = user.mobileNumber || user.phone || null;
            await NotificationService.notify(userId, user.email, {
                title: content.title,
                message: content.message,
                channels: phone ? ['email', 'system', 'whatsapp'] : ['email', 'system'],
                templateCode: template,
                placeholders: {
                    applicationId: application.applicationId || String(application.id),
                    stageName: track.stageName,
                    link: `${portalBase}/applications/${application.id}`,
                    phone: phone || ''
                },
                metadata: { applicationId: application.id }
            });
        }
    }
}
