import { Request, Response } from 'express';
import db from '../../database/models/index.js';
const { LoaRequest, LoaApproval, LoaDocumentGenerated, SecurityDeposit, AuditLog, StageApprovalPolicy, StageApprovalAction, User, DealerCode } = db;
import { formatDueDateDaysFromNow } from '../../constants/onboarding-email-defaults.js';
import { AuthRequest } from '../../types/express.types.js';
import { AUDIT_ACTIONS, APPLICATION_STATUS, APPLICATION_STAGES, ROLES } from '../../common/config/constants.js';
import { WorkflowService } from '../../services/WorkflowService.js';
import { NotificationService } from '../../services/NotificationService.js';
import { getFrontendBaseUrl, getProspectPortalUrl } from '../../common/utils/frontendUrl.js';

const LOA_STAGE_CODE = 'LOA_APPROVAL';

const ensureLoaPolicy = async () => {
    const [policy] = await StageApprovalPolicy.findOrCreate({
        where: { stageCode: LOA_STAGE_CODE },
        defaults: {
            stageCode: LOA_STAGE_CODE,
            minApprovals: 2,
            approvalMode: 'ROLE_MANDATORY',
            requiredRoles: ['DD Head', 'NBH'],
            isActive: true
        }
    });
    return policy;
};

// --- LOA ---

export const getRequest = async (req: Request, res: Response) => {
    try {
        const { applicationId } = req.params;
        const targetId = applicationId as string;
        const isUUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(targetId);

        // Resolve application first to get UUID
        const application = await db.Application.findOne({
            where: isUUID ? { [db.Sequelize.Op.or]: [{ id: targetId }, { applicationId: targetId }] } : { applicationId: targetId }
        });

        if (!application) {
            return res.status(404).json({ success: false, message: 'Application not found' });
        }

        const request = await LoaRequest.findOne({
            where: { applicationId: application.id },
            include: [
                { model: LoaApproval, as: 'approvals' },
                { model: LoaDocumentGenerated, as: 'generatedDocuments' }
            ]
        });
        res.json({ success: true, data: request });
    } catch (error) {
        console.error('Get LOA request error:', error);
        res.status(500).json({ success: false, message: 'Error fetching LOA request' });
    }
};

export const createRequest = async (req: AuthRequest, res: Response) => {
    try {
        const { applicationId } = req.body;
        const targetId = applicationId as string;
        const isUUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(targetId);

        const application = await db.Application.findOne({
            where: isUUID ? { [db.Sequelize.Op.or]: [{ id: targetId }, { applicationId: targetId }] } : { applicationId: targetId }
        });

        if (!application) return res.status(404).json({ success: false, message: 'Application not found' });

        const [request, created] = await LoaRequest.findOrCreate({
            where: { applicationId: application.id },
            defaults: {
                requestedBy: req.user?.id,
                status: 'In Progress'
            }
        });

        // Initialize first level approval (DD Head) for LOA
        await LoaApproval.findOrCreate({
            where: { requestId: request.id, level: 1 },
            defaults: {
                approverRole: 'DD Head',
                action: 'Pending'
            }
        });

        await WorkflowService.transitionApplication(application, APPLICATION_STATUS.LOA_PENDING, req.user?.id || null, {
            reason: 'LOA Request initiated with DD Head approval',
            progressPercentage: 92
        });

        // SRS §6.18.3.1 — Notify DD-Head that LOA needs their approval
        const ddHeadUsers = await User.findAll({ where: { roleCode: ROLES.DD_HEAD } });
        for (const ddHead of ddHeadUsers) {
            const phone = (ddHead as any).mobileNumber || null;
            NotificationService.notify(ddHead.id, ddHead.email, {
                title: `Action Required: LOA Approval for ${application.applicationId}`,
                message: `LOA request has been initiated for ${application.applicationId}. Your approval is required.`,
                channels: phone ? ['system', 'email', 'whatsapp'] : ['system', 'email'],
                templateCode: 'WORKFLOW_ACTION_REQUIRED',
                placeholders: {
                    dealerName: application.applicantName || application.applicationId,
                    requestId: application.applicationId,
                    targetStage: 'LOA Approval',
                    link: `${getFrontendBaseUrl()}/applications/${application.id}`,
                    phone: phone || ''
                }
            }).catch((e: any) => console.error('[LOA] DD-Head notify failed:', e));
        }

        res.status(201).json({ success: true, message: 'LOA Request initiated with DD Head approval', data: request });
    } catch (error) {
        console.error('Create LOA request error:', error);
        res.status(500).json({ success: false, message: 'Error creating LOA request' });
    }
};

export const approveRequest = async (req: AuthRequest, res: Response) => {
    try {
        if (!req.user?.id || !req.user?.roleCode) {
            return res.status(401).json({ success: false, message: 'Unauthorized' });
        }
        const { requestId } = req.params;
        const { action, remarks } = req.body;

        const request = await LoaRequest.findByPk(requestId);
        if (!request) return res.status(404).json({ success: false, message: 'LOA Request not found' });
        const policy = await ensureLoaPolicy();
        const requiredRoles: string[] = Array.isArray(policy.requiredRoles) ? policy.requiredRoles : [];
        if (requiredRoles.length > 0 && !requiredRoles.includes(req.user.roleCode) && req.user.roleCode !== 'Super Admin') {
            return res.status(403).json({
                success: false,
                message: `Role ${req.user.roleCode} is not allowed to approve ${LOA_STAGE_CODE}`
            });
        }

        const currentApproval = await LoaApproval.findOne({
            where: { requestId, action: 'Pending' },
            order: [['level', 'ASC']]
        });

        if (!currentApproval) return res.status(400).json({ success: false, message: 'No pending approval found' });

        // MANDATORY FINANCIAL CHECK: LOA MUST HAVE FINAL SECURITY DEPOSIT VERIFIED
        if (action === 'Approved') {
            const finalDeposit = await SecurityDeposit.findOne({
                where: { 
                    applicationId: request.applicationId, 
                    depositType: 'FIRST_FILL', 
                    status: 'Verified' 
                }
            });

            if (!finalDeposit) {
                return res.status(400).json({
                    success: false,
                    message: `LOA Approval Blocked: The First Fill (₹15L) is either Pending or not found. Finance team must verify the payment before proceeding.`
                });
            }
        }

        await currentApproval.update({
            action,
            remarks,
            approverId: req.user.id,
            approvedAt: action === 'Approved' ? new Date() : null
        });

        const normalizedDecision = action === 'Rejected' ? 'Rejected' : 'Approved';
        await StageApprovalAction.upsert({
            applicationId: request.applicationId,
            interviewId: null,
            stageCode: LOA_STAGE_CODE,
            actorUserId: req.user.id,
            actorRole: req.user.roleCode,
            decision: normalizedDecision,
            remarks: remarks || null
        });

        const stageActions = await StageApprovalAction.findAll({
            where: { applicationId: request.applicationId, stageCode: LOA_STAGE_CODE }
        });
        const approvedRoles = new Set(
            stageActions.filter((a: any) => a.decision === 'Approved').map((a: any) => a.actorRole)
        );
        const hasRejection = stageActions.some((a: any) => a.decision === 'Rejected');
        const hasAllRequiredRoleApprovals = requiredRoles.length === 0
            ? true
            : requiredRoles.every((role: string) => approvedRoles.has(role));
        const meetsMinApprovals = approvedRoles.size >= (policy.minApprovals || 1);

        if (action === 'Rejected' || hasRejection) {
            await request.update({ status: 'Rejected' });
            const application = await db.Application.findByPk(request.applicationId);
            if (application) {
                await WorkflowService.transitionApplication(application, APPLICATION_STATUS.LOA_REJECTED, req.user.id, {
                    reason: 'LOA Request rejected during approval',
                    stage: 'Rejected',
                    progressPercentage: 92
                });
            }
            return res.json({ success: true, message: 'LOA Request rejected' });
        }

        if (hasAllRequiredRoleApprovals && meetsMinApprovals) {
            await request.update({ status: 'Approved', approvedBy: req.user.id, approvedAt: new Date() });
            
            const mockFile = `LOA_${request.id}.pdf`;
            const onboardingDoc = await db.OnboardingDocument.create({
                applicationId: request.applicationId,
                documentType: 'LOA',
                fileName: mockFile,
                filePath: `/uploads/loa/${mockFile}`,
                status: 'active',
                stage: 'LOA'
            });

            await LoaDocumentGenerated.create({
                requestId: request.id,
                documentId: onboardingDoc.id,
                version: '1.0',
                generatedAt: new Date()
            });

            const application = await db.Application.findByPk(request.applicationId);
            if (application) {
                await WorkflowService.transitionApplication(application, APPLICATION_STATUS.EOR_IN_PROGRESS, req.user.id, {
                    reason: 'LOA fully approved. Moving to EOR Work.',
                    progressPercentage: 97
                });
            }

            // SRS §6.18.3.1 — LOA issued: notify all relevant teams (System + Email)
            const app = await db.Application.findByPk(request.applicationId);
            const loaTeamRoles = [ROLES.DD_ADMIN, ROLES.FINANCE, ROLES.LEGAL_ADMIN, ROLES.DD_HEAD, ROLES.NBH];
            for (const role of loaTeamRoles) {
                const roleUsers = await User.findAll({ where: { roleCode: role } });
                for (const u of roleUsers) {
                    NotificationService.notify(u.id, u.email, {
                        title: `LOA Issued: ${app?.applicationId || request.applicationId}`,
                        message: `Letter of Appointment fully approved and issued for ${app?.applicantName || 'the applicant'}. EOR process may now begin.`,
                        channels: ['system', 'email'],
                        templateCode: 'LOA_ISSUED',
                        placeholders: {
                            applicantName: app?.applicantName || '',
                            applicationId: app?.applicationId || '',
                            link: `${getFrontendBaseUrl()}/applications/${request.applicationId}`,
                            ctaLabel: 'View Application'
                        }
                    }).catch((e: any) => console.error('[LOA] team notify failed:', e));
                }
            }

            const appFull = await db.Application.findByPk(request.applicationId, {
                include: [{ model: DealerCode, as: 'dealerCode', required: false }]
            });
            if (appFull?.email) {
                const portalBaseLoa = getFrontendBaseUrl();
                const dc = (appFull as any).dealerCode;
                const dealerCodeStr = dc?.salesCode || dc?.serviceCode || 'Available on portal after SAP sync';
                const applicantAcct = await User.findOne({
                    where: { email: appFull.email },
                    attributes: ['id', 'mobileNumber']
                });
                await NotificationService.notify(applicantAcct?.id ?? null, appFull.email, {
                    title: `LOA issued — ${appFull.applicationId}`,
                    message: 'Your Letter of Appointment is ready. View it on the Dealer Portal.',
                    channels: ['email'],
                    templateCode: 'LOA_ISSUED',
                    placeholders: {
                        applicantName: appFull.applicantName || '',
                        applicationId: appFull.applicationId,
                        dealerCode: dealerCodeStr,
                        link: getProspectPortalUrl(appFull.id),
                        ctaLabel: 'View LOA'
                    }
                }).catch((e: any) => console.error('[LOA] Applicant LOA email failed:', e));

                await NotificationService.notify(applicantAcct?.id ?? null, appFull.email, {
                    title: `Sign dealership agreement — ${appFull.applicationId}`,
                    message: 'Please review and e-sign your dealership agreement on the portal.',
                    channels: ['email'],
                    templateCode: 'DEALERSHIP_AGREEMENT_SIGNATURE_REQUEST',
                    placeholders: {
                        applicantName: appFull.applicantName || '',
                        applicationId: appFull.applicationId,
                        dealerCode: dealerCodeStr,
                        dueDate: formatDueDateDaysFromNow(14),
                        link: getProspectPortalUrl(appFull.id),
                        ctaLabel: 'Review & sign agreement'
                    }
                }).catch((e: any) => console.error('[LOA] Agreement signature email failed:', e));
            }

            res.json({ success: true, message: 'LOA fully approved and issued' });
        } else {
            // SEQUENTIAL APPROVAL BRIDGE:
            // If Level 1 is approved but more role approvals are needed based on policy, 
            // create the Level 2 pending record automatically.
            if (action === 'Approved') {
                const nextLevel = currentApproval.level + 1;
                // Determine next role from policy requiredRoles
                const nextRole = requiredRoles[currentApproval.level] || (req.user.roleCode === 'DD Head' ? 'NBH' : 'DD Head');
                
                await LoaApproval.findOrCreate({
                    where: { requestId, level: nextLevel },
                    defaults: {
                        approverRole: nextRole,
                        action: 'Pending'
                    }
                });
                console.log(`[LOA] Generated sequential approval record for Level ${nextLevel}: ${nextRole}`);

                // SRS §6.18.3.1 — Sequential: DD-Head approved → notify NBH
                if (req.user.roleCode === ROLES.DD_HEAD) {
                    const nbhUsers = await User.findAll({ where: { roleCode: ROLES.NBH } });
                    for (const nbh of nbhUsers) {
                        const phone = (nbh as any).mobileNumber || null;
                        NotificationService.notify(nbh.id, nbh.email, {
                            title: `Action Required: LOA Final Approval`,
                            message: `DD Head has approved LOA request ${requestId}. Your final sign-off is required.`,
                            channels: phone ? ['system', 'email', 'whatsapp'] : ['system', 'email'],
                            templateCode: 'WORKFLOW_ACTION_REQUIRED',
                            placeholders: {
                                dealerName: '',
                                requestId,
                                targetStage: 'NBH LOA Approval',
                                link: `${getFrontendBaseUrl()}/applications/${request.applicationId}`,
                                phone: phone || ''
                            }
                        }).catch((e: any) => console.error('[LOA] NBH notify failed:', e));
                    }
                }
            }

            res.json({
                success: true,
                message: 'Approval recorded. Waiting for remaining required approvers.',
                data: {
                    stageCode: LOA_STAGE_CODE,
                    requiredRoles,
                    minApprovals: policy.minApprovals,
                    approvedRoles: Array.from(approvedRoles),
                    hasAllRequiredRoleApprovals,
                    meetsMinApprovals
                }
            });
        }
    } catch (error) {
        console.error('Approve LOA request error:', error);
        res.status(500).json({ success: false, message: 'Error processing approval' });
    }
};

export const getApprovalStatus = async (req: AuthRequest, res: Response) => {
    try {
        const { applicationId } = req.params;
        const targetId = applicationId as string;
        const isUUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(targetId);

        const application = await db.Application.findOne({
            where: isUUID ? { [db.Sequelize.Op.or]: [{ id: targetId }, { applicationId: targetId }] } : { applicationId: targetId }
        });

        if (!application) {
            return res.status(404).json({ success: false, message: 'Application not found' });
        }

        const policy = await ensureLoaPolicy();
        const actions = await StageApprovalAction.findAll({
            where: { applicationId: application.id, stageCode: LOA_STAGE_CODE },
            include: [{ model: User, as: 'actor', attributes: ['id', 'fullName', 'email', 'roleCode'] }],
            order: [['updatedAt', 'DESC']]
        });
        res.json({
            success: true,
            data: {
                stageCode: LOA_STAGE_CODE,
                minApprovals: policy.minApprovals,
                approvalMode: policy.approvalMode,
                requiredRoles: policy.requiredRoles || [],
                actions
            }
        });
    } catch (error) {
        console.error('Get LOA approval status error:', error);
        res.status(500).json({ success: false, message: 'Error fetching LOA approval status' });
    }
};

export const generateDocument = async (req: AuthRequest, res: Response) => {
    try {
        const { requestId } = req.body;
        const mockFile = `LOA_MANUAL_${Date.now()}.pdf`;
        const doc = await LoaDocumentGenerated.create({
            requestId,
            documentType: 'LOA',
            fileName: mockFile,
            filePath: `/uploads/loa/${mockFile}`
        });
        res.json({ success: true, message: 'LOA Document generated (Mock)', data: doc });
    } catch (error) {
        res.status(500).json({ success: false, message: 'Error generating document' });
    }
};

// --- Security Deposit ---

export const updateSecurityDeposit = async (req: AuthRequest, res: Response) => {
    try {
        const { applicationId, amount, paymentReference, proofDocumentId, status, depositType } = req.body;
        
        const isUUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(applicationId);
        const application = await db.Application.findOne({
            where: isUUID ? { [db.Sequelize.Op.or]: [{ id: applicationId }, { applicationId }] } : { applicationId }
        });

        if (!application) return res.status(404).json({ success: false, message: 'Application not found' });

        let deposit = await SecurityDeposit.findOne({ 
            where: { applicationId: application.id, depositType: depositType || 'SECURITY_DEPOSIT' } 
        });

        if (deposit) {
            await deposit.update({
                amount, paymentReference, proofDocumentId, status,
                verifiedBy: status === 'Verified' ? req.user?.id : deposit.verifiedBy,
                verifiedAt: status === 'Verified' && !deposit.verifiedAt ? new Date() : deposit.verifiedAt
            });
        } else {
            deposit = await SecurityDeposit.create({
                applicationId: application.id,
                amount,
                paymentReference,
                proofDocumentId,
                status: status || 'Pending',
                depositType: depositType || 'SECURITY_DEPOSIT',
                verifiedBy: status === 'Verified' ? req.user?.id : null,
                verifiedAt: status === 'Verified' ? new Date() : null
            });
        }

        // --- FETCH WITH JOIN FOR FRONTEND ---
        const updatedDeposit = await SecurityDeposit.findByPk(deposit.id, {
            include: [{ model: User, as: 'verifier', attributes: ['fullName'] }]
        });

        // --- AUTOMATION: After verification transitions ---

        // 1. SECURITY_DEPOSIT verified: the deposit row is always updated above. Only touch application workflow when
        //    the app is already in the LOI payment/security corridor — never jump from earlier stages (e.g. interviews).
        if ((depositType === 'SECURITY_DEPOSIT' || !depositType) && status === 'Verified') {
            const os = application.overallStatus;
            const inInitialSdCorridor =
                os === APPLICATION_STATUS.PAYMENT_PENDING ||
                os === APPLICATION_STATUS.SECURITY_DETAILS ||
                os === 'Security Details';
            if (inInitialSdCorridor) {
                console.log(
                    `[DEBUG] SECURITY_DEPOSIT verified (overallStatus=${os}). Aligning to Security Deposit for admin before LOI Issued.`,
                );
                await WorkflowService.transitionApplication(application, APPLICATION_STATUS.SECURITY_DETAILS, req.user?.id || null, {
                    reason: 'Security Deposit verified by Finance. Awaiting admin approval to proceed to LOI issuance.',
                    stage: APPLICATION_STAGES.LOI,
                    progressPercentage: 78
                });
            } else {
                console.log(
                    `[DEBUG] SECURITY_DEPOSIT verified but overallStatus=${os}; no status transition (payment recorded only).`,
                );
            }
        }

        // 2. FIRST_FILL verified: always persist the row above. Only touch workflow when already at LOA Pending
        //    (same idea as SECURITY_DEPOSIT — never jump from earlier stages into LOA).
        if (depositType === 'FIRST_FILL' && status === 'Verified') {
            const os = application.overallStatus;
            if (os === APPLICATION_STATUS.LOA_PENDING) {
                await db.LoaRequest.findOrCreate({
                    where: { applicationId: application.id },
                    defaults: { status: 'pending', requestedBy: req.user?.id }
                });

                await WorkflowService.transitionApplication(application, APPLICATION_STATUS.LOA_PENDING, req.user?.id || null, {
                    reason: 'First Fill verified by Finance while at LOA Pending. LOA approval may proceed.',
                    progressPercentage: 90
                });
            } else {
                console.log(
                    `[DEBUG] FIRST_FILL verified but overallStatus=${os}; no status transition (payment recorded only).`,
                );
            }
        }

        res.json({ success: true, message: 'Security Deposit updated', data: updatedDeposit });
    } catch (error) {
        console.error('Update Security Deposit error:', error);
        res.status(500).json({ success: false, message: 'Error updating Security Deposit' });
    }
};

export const getSecurityDeposit = async (req: Request, res: Response) => {
    try {
        const applicationId = req.params.applicationId as string;
        
        const isUUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(applicationId);
        const application = await db.Application.findOne({
            where: isUUID ? { [db.Sequelize.Op.or]: [{ id: applicationId }, { applicationId }] } : { applicationId }
        });

        if (!application) {
            return res.json({ success: true, data: [] });
        }

        const deposits = await SecurityDeposit.findAll({ 
            where: { applicationId: application.id },
            include: [{ model: User, as: 'verifier', attributes: ['fullName'] }],
            order: [['createdAt', 'ASC']]
        });
        res.json({ success: true, data: deposits });
    } catch (error) {
        console.error('Fetch Security Deposit error:', error);
        res.status(500).json({ success: false, message: 'Error fetching Security Deposit' });
    }
};
