import db from '../database/models/index.js';
const { User } = db;
import { RESIGNATION_STAGES, ROLES, REQUEST_TYPES, FNF_DEPARTMENTS } from '../common/config/constants.js';
import { getResignationStatusForStage } from '../common/utils/offboardingStatus.js';
import { NotificationService } from './NotificationService.js';
import { Op, Transaction } from 'sequelize';
import logger from '../common/utils/logger.js';
import { writeWorkflowActivityWorknote } from '../common/utils/workflowWorknote.js';
import { getOffboardingAuditAction, formatOffboardingAction } from '../common/utils/offboardingWorkflow.utils.js';
import { NomenclatureService } from '../common/utils/nomenclature.js';
import { syncSlaOnStageTransition } from '../common/utils/slaWorkflowSync.js';
import { getFrontendBaseUrl } from '../common/utils/frontendUrl.js';


export class ResignationWorkflowService {
    /**
     * Standardized method to transition a resignation request status
     */
    static async transitionResignation(resignation: any, targetStage: string, userId: string | null = null, metadata: any = {}) {
        const { action, remarks, status, transaction } = metadata;
        const sourceStage = resignation.currentStage;

        const updateData: any = {
            currentStage: targetStage,
            status: status || getResignationStatusForStage(targetStage),
            progressPercentage: this.calculateProgress(targetStage),
            updatedAt: new Date()
        };

        // 1. Resolve Actor
        const actor = userId ? await User.findByPk(userId) : null;

        // 2. Update Timeline (JSON array) & Resignation Record
        const timelineEntry = {
            stage: sourceStage, // Correctly Associate remark with the stage where action happened
            targetStage: targetStage, // Store target for reference
            timestamp: new Date(),
            user: actor ? actor.fullName : 'System',
            action: action || `Approved to ${targetStage}`,
            remarks: remarks || ''
        };

        const updatedTimeline = [...(resignation.timeline || []), timelineEntry];
        
        await resignation.update({
            ...updateData,
            timeline: updatedTimeline
        }, transaction ? { transaction } : undefined);

        await syncSlaOnStageTransition({
            entityType: 'resignation',
            entityId: resignation.id,
            fromStage: sourceStage,
            toStage: targetStage
        });

        // 3. Create Audit Log using standardized mapper
        const { actionType } = metadata;
        const auditAction = getOffboardingAuditAction(actionType || action || 'Approved', REQUEST_TYPES.RESIGNATION);

        await db.ResignationAudit.create({
            userId: userId,
            resignationId: resignation.id,
            action: formatOffboardingAction(auditAction),
            remarks: remarks || '',
            details: { status: updateData.status, stage: sourceStage, targetStage: formatOffboardingAction(targetStage) }
        }, transaction ? { transaction } : undefined);

        // 4. Create Worknote for standardized communication trail
        if (remarks && userId) {
            try {
                // Prepend action to remarks for better context in Work Notes
                const actionPrefix = action ? `${formatOffboardingAction(action)}: ` : '';
                await writeWorkflowActivityWorknote({
                    requestId: resignation.id,
                    requestType: 'resignation',
                    userId: userId,
                    noteText: `${actionPrefix}${remarks}`,
                    noteType: (action && action.toLowerCase().includes('send back')) ? 'workflow' : 'internal'
                });
            } catch (wnErr) {
                logger.error('[ResignationWorkflowService] failed to write worknote:', wnErr);
            }
        }


        console.log(`[ResignationWorkflowService] Transitioned Resignation ${resignation.resignationId} to ${targetStage}`);

        // 5. Send Notifications
        const user = await User.findOne({ 
            where: { 
                [Op.or]: [
                    { id: resignation.dealerId },
                    { dealerId: resignation.dealerId }
                ]
            } 
        });

        if (user) {
            const portalBase = getFrontendBaseUrl();

            const { notifyStakeholdersOnTransition } = await import('../common/utils/workflow-email-notifications.js');

            await notifyStakeholdersOnTransition(
                resignation.id,
                REQUEST_TYPES.RESIGNATION,
                targetStage,
                {
                    code: resignation.resignationId || resignation.id,
                    dealerName: user.fullName || 'Dealer',
                    dealerId: user.id,
                    actionUserFullName: actor ? actor.fullName : 'System',
                    action: action || `Approved to ${targetStage}`,
                    remarks: remarks || 'N/A',
                    link: `${portalBase}/dealer-resignation/${resignation.id}`
                }
            );


            // 6. Deactivate User Account on final completion (SRS 1.1.5 / 2.3.5)
            if (targetStage === RESIGNATION_STAGES.COMPLETED || targetStage === RESIGNATION_STAGES.LEGAL) {
                logger.info(`[ResignationWorkflowService] Revoking portal access for user ${user.email} (Stage: ${targetStage})`);
                await user.update({
                    status: 'inactive',
                    isActive: false
                }, transaction ? { transaction } : undefined);
            }
        } else {
            logger.warn(`[ResignationWorkflowService] No user account found with dealerId ${resignation.dealerId}`);
        }
        
        return resignation;
    }

    /**
     * Maps resignation stages to progress percentage
     */
    static calculateProgress(stage: string): number {
        const progress: Record<string, number> = {
            [RESIGNATION_STAGES.ASM]: 12,
            [RESIGNATION_STAGES.RBM]: 25,
            [RESIGNATION_STAGES.ZBH]: 38,
            [RESIGNATION_STAGES.DD_LEAD]: 50,
            [RESIGNATION_STAGES.DD_HEAD]: 60,
            [RESIGNATION_STAGES.NBH]: 70,
            [RESIGNATION_STAGES.LEGAL]: 82,
            [RESIGNATION_STAGES.DD_ADMIN]: 90,
            [RESIGNATION_STAGES.AWAITING_FNF]: 92,
            [RESIGNATION_STAGES.FNF_INITIATED]: 95,
            [RESIGNATION_STAGES.COMPLETED]: 100,
            [RESIGNATION_STAGES.REJECTED]: 100
        };
        return progress[stage] || 0;
    }

    /**
     * Checks if a user is authorized to perform an action based on their role and current stage
     */
    static async canUserAction(resignation: any, user: any) {
        if (!user) return false;
        if (user.roleCode === ROLES.SUPER_ADMIN) return true;

        const stageToRole: Record<string, string | string[]> = {
            [RESIGNATION_STAGES.ASM]: ROLES.ASM,
            [RESIGNATION_STAGES.RBM]: [ROLES.RBM, ROLES.DD_ZM],
            [RESIGNATION_STAGES.ZBH]: ROLES.ZBH,
            [RESIGNATION_STAGES.DD_LEAD]: ROLES.DD_LEAD,
            [RESIGNATION_STAGES.DD_HEAD]: ROLES.DD_HEAD,
            [RESIGNATION_STAGES.NBH]: ROLES.NBH,
            [RESIGNATION_STAGES.LEGAL]: ROLES.LEGAL_ADMIN,
            [RESIGNATION_STAGES.DD_ADMIN]: ROLES.DD_ADMIN,
            [RESIGNATION_STAGES.AWAITING_FNF]: [ROLES.DD_LEAD, ROLES.DD_HEAD, ROLES.DD_ADMIN],
            [RESIGNATION_STAGES.FNF_INITIATED]: ROLES.DD_ADMIN
        };

        const requiredRole = stageToRole[resignation.currentStage];
        if (Array.isArray(requiredRole)) {
            return requiredRole.includes(user.roleCode);
        }
        return user.roleCode === requiredRole;
    }

    /**
     * Initiates the F&F settlement process for a resignation
     * SRS §4.2.2.9 — Standardized trigger mechanism
     */
    static async initiateFnF(resignation: any, userId: string, transaction: Transaction) {
        try {
            // 1. Resolve Dealer Entity ID (from User profile)
            let dealerEntityId = resignation.dealerId; // Fallback to User ID if not linked, though DB FK prefers dealers.id
            if (resignation.dealer && resignation.dealer.dealerId) {
                dealerEntityId = resignation.dealer.dealerId;
            } else {
                // If not eager loaded, fetch the user to get dealerId
                const user = await db.User.findByPk(resignation.dealerId);
                if (user && user.dealerId) {
                    dealerEntityId = user.dealerId;
                }
            }

            const fnf = await db.FnF.create({
                settlementId: await NomenclatureService.generateFnFId(),
                resignationId: resignation.id,
                dealerId: dealerEntityId,
                outletId: resignation.outletId,
                status: 'Initiated',
                initiatedAt: new Date(),
                initiatedBy: userId,
                totalPayables: 0,
                totalReceivables: 0,
                totalDeductions: 0,
                netAmount: 0,
                departmentalClearances: {}
            }, { transaction });

            // 2. Initialize Departmental Clearances
            const clearancePromises = FNF_DEPARTMENTS.map(dept => 
                db.FffClearance.create({
                    fnfId: fnf.id,
                    department: dept,
                    status: 'Pending',
                    amount: 0,
                    remarks: 'Awaiting departmental input'
                }, { transaction })
            );
            await Promise.all(clearancePromises);

            const { startAllPendingFnfClearanceSlas } = await import('../common/utils/slaFnfSync.js');
            await startAllPendingFnfClearanceSlas(fnf.id);

            // 3. Create Audit Trail
            await db.FnFAudit.create({
                userId,
                fnfId: fnf.id,
                action: 'INITIATED',
                remarks: 'F&F Settlement workflow triggered from Resignation',
                details: { source: 'Resignation Workflow', resignationId: resignation.resignationId }
            }, { transaction });

            logger.info(`[ResignationWorkflowService] F&F ${fnf.settlementId} initiated for Resignation ${resignation.resignationId}`);
            return fnf;
        } catch (error) {
            logger.error('[ResignationWorkflowService] Failed to initiate F&F:', error);
            throw error;
        }
    }
}
