import { Request, Response } from 'express';
import db from '../../database/models/index.js';
const { LoiRequest, LoiApproval, LoiDocumentGenerated, LoiAcknowledgement, AuditLog, StageApprovalPolicy, StageApprovalAction, User, SecurityDeposit } = db;
import { AuthRequest } from '../../types/express.types.js';
import { AUDIT_ACTIONS, APPLICATION_STATUS, ROLES } from '../../common/config/constants.js';
import { WorkflowService } from '../../services/WorkflowService.js';
import { NotificationService } from '../../services/NotificationService.js';
import { sendEmail } from '../../common/utils/email.service.js';
import { formatDueDateDaysFromNow, DEFAULT_STATUTORY_DOCUMENT_CHECKLIST } from '../../constants/onboarding-email-defaults.js';
import { getFrontendBaseUrl, getProspectPortalUrl } from '../../common/utils/frontendUrl.js';

const LOI_STAGE_CODE = 'LOI_APPROVAL';

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

    // If policy already exists but has Finance, update it
    if (policy && Array.isArray(policy.requiredRoles) && policy.requiredRoles.includes('Finance')) {
        await policy.update({
            requiredRoles: ['DD Head', 'NBH'],
            minApprovals: 2
        });
    }

    return policy;
};

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 LoiRequest.findOne({
            where: { applicationId: application.id },
            include: [
                { model: LoiApproval, as: 'approvals' },
                { model: LoiDocumentGenerated, as: 'generatedDocuments' },
                { model: LoiAcknowledgement, as: 'acknowledgement' }
            ]
        });
        res.json({ success: true, data: request });
    } catch (error) {
        console.error('Get LOI request error:', error);
        res.status(500).json({ success: false, message: 'Error fetching LOI request' });
    }
};

export const acknowledgeRequest = async (req: AuthRequest, res: Response) => {
    try {
        const { requestId } = req.params;
        const { documentId } = req.body;

        const request = await LoiRequest.findByPk(requestId);
        if (!request) return res.status(404).json({ success: false, message: 'LOI Request not found' });

        const latestGen = await LoiDocumentGenerated.findOne({
            where: { requestId: request.id },
            order: [['generatedAt', 'DESC']]
        });
        const loiDocId = (documentId as string) || latestGen?.id;
        if (!loiDocId || !req.user?.id) {
            return res.status(400).json({
                success: false,
                message: 'LOI document reference and a signed-in applicant are required to acknowledge the LOI.'
            });
        }

        await LoiAcknowledgement.create({
            loiDocId,
            applicantId: req.user.id,
            remarks: null
        });

        const application = await db.Application.findByPk(request.applicationId);
        if (application) {
            await WorkflowService.transitionApplication(application, APPLICATION_STATUS.DEALER_CODE_GENERATION, req.user?.id || null, {
                reason: 'LOI Acknowledged by applicant',
                progressPercentage: 90
            });

            const portalBase = getFrontendBaseUrl();
            const deposit =
                (await SecurityDeposit.findOne({
                    where: { applicationId: application.id, depositType: 'SECURITY_DEPOSIT' }
                })) || (await SecurityDeposit.findOne({ where: { applicationId: application.id } }));
            const amountNum = deposit?.amount != null ? Number(deposit.amount) : 200000;
            const amountStr = Number.isFinite(amountNum) ? amountNum.toLocaleString('en-IN') : '200,000';

            const applicantUser = await User.findOne({
                where: { email: application.email },
                attributes: ['id', 'mobileNumber']
            });
            const phone = (applicantUser as any)?.mobileNumber || application.phone || '';

            await NotificationService.notify(applicantUser?.id ?? null, application.email, {
                title: `Security Deposit — ${application.applicationId}`,
                message: `Please remit the Security Deposit for ${application.applicationId} as per your LOI.`,
                channels: phone ? ['email', 'whatsapp'] : ['email'],
                templateCode: 'SECURITY_DEPOSIT_REQUEST',
                placeholders: {
                    applicantName: application.applicantName || 'Applicant',
                    applicationId: application.applicationId,
                    amount: amountStr,
                    dueDate: formatDueDateDaysFromNow(14),
                    bankDetails: 'Use the payment instructions shown on the Dealer Portal after you sign in.',
                    link: getProspectPortalUrl(application.id),
                    ctaLabel: 'Pay or upload proof',
                    phone: String(phone || '')
                }
            }).catch((e: any) => console.error('[LOI] Security Deposit email failed:', e));
        }

        res.json({ success: true, message: 'LOI Acknowledged by applicant' });
    } catch (error) {
        console.error('LOI Acknowledge error:', error);
        res.status(500).json({ success: false, message: 'Error acknowledging LOI' });
    }
};

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 LoiRequest.findOrCreate({
            where: { applicationId: application.id },
            defaults: {
                requestedBy: req.user?.id,
                status: 'In Progress'
            }
        });

        // Initialize first level approval (DD Head) if not already exists
        await LoiApproval.findOrCreate({
            where: { requestId: request.id, level: 1 },
            defaults: {
                approverRole: 'DD Head',
                action: 'Pending'
            }
        });

        await WorkflowService.transitionApplication(application, APPLICATION_STATUS.LOI_IN_PROGRESS, req.user?.id || null, {
            reason: 'LOI Request initiated for DD Head approval',
            progressPercentage: 75
        });

        // SRS §6.16.3.5 — Notify DD-Head that LOI needs their approval (System + Email + WhatsApp)
        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: LOI Approval for ${application.applicationId}`,
                message: `LOI request initiated for application ${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: 'LOI Approval',
                    link: `${getFrontendBaseUrl()}/applications/${application.id}`,
                    phone: phone || ''
                }
            }).catch((e: any) => console.error('[LOI] DD-Head notify failed:', e));
        }

        res.status(201).json({ success: true, message: 'LOI Request initiated for DD Head approval', data: request });
    } catch (error) {
        console.error('Create LOI request error:', error);
        res.status(500).json({ success: false, message: 'Error creating LOI 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; // action: Approved/Rejected

        const request = await LoiRequest.findByPk(requestId);
        if (!request) return res.status(404).json({ success: false, message: 'LOI Request not found' });
        const policy = await ensureLoiPolicy();
        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 ${LOI_STAGE_CODE}`
            });
        }

        // Find current pending approval
        const currentApproval = await LoiApproval.findOne({
            where: { requestId, action: 'Pending' },
            order: [['level', 'ASC']]
        });

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

        // MANDATORY DOCUMENT CHECK (SRS Requirement)
        // Level 2+ requires minimum set of documents uploaded by applicant
        if (currentApproval.level === 1 && action === 'Approved') {
            const docCount = await db.OnboardingDocument.count({
                where: { applicationId: request.applicationId }
            });
            if (docCount < 5) { // SRS requires 18, using 5 for functional demo
                return res.status(400).json({
                    success: false,
                    message: `Mandatory Document Check Failed: Applicant must upload at least 5 required documents (CIBIL, City Map, etc.) before DD Head approval. Current: ${docCount}`
                });
            }
        }

        // 1. Update current level
        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: LOI_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: LOI_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);

        // 2. Handle Logic based on Action
        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.LOI_REJECTED, req.user.id, {
                    reason: 'LOI Request rejected during approval',
                    stage: 'Rejected',
                    progressPercentage: 75
                });
            }
            return res.json({ success: true, message: 'LOI Request rejected' });
        }

        if (hasAllRequiredRoleApprovals && meetsMinApprovals) {
            // Final Approval reached
            await request.update({ status: 'Approved', approvedBy: req.user.id, approvedAt: new Date() });
            
            // Trigger Mock Document Generation
            const mockFile = `LOI_${request.id}.pdf`;
            const docRecord = await db.OnboardingDocument.create({
                applicationId: request.applicationId,
                documentType: 'LOI',
                fileName: mockFile,
                filePath: `/uploads/loi/${mockFile}`,
                status: 'active'
            });
            await LoiDocumentGenerated.create({
                requestId: request.id,
                documentId: docRecord.id,
                version: '1.0'
            });

            // Create Initial Security Deposit record (Advance Payment)
            await db.SecurityDeposit.findOrCreate({
                where: { applicationId: request.applicationId, depositType: 'SECURITY_DEPOSIT' },
                defaults: {
                    amount: 200000, // 2 Lakhs Advance
                    status: 'Pending'
                }
            });

            const application = await db.Application.findByPk(request.applicationId);
            if (application) {
                await WorkflowService.transitionApplication(application, APPLICATION_STATUS.SECURITY_DETAILS, req.user.id, {
                    reason: 'LOI Request fully approved and document generated',
                    progressPercentage: 80
                });
            }
            
            // SRS §6.16.3.5 — Notify Finance + DD-Admin that LOI is fully approved
            const notifyRoles = [ROLES.FINANCE, ROLES.DD_ADMIN];
            const application2 = await db.Application.findByPk(request.applicationId);
            for (const role of notifyRoles) {
                const roleUsers = await User.findAll({ where: { roleCode: role } });
                for (const u of roleUsers) {
                    NotificationService.notify(u.id, u.email, {
                        title: `LOI Fully Approved: ${application2?.applicationId || request.applicationId}`,
                        message: `LOI request has been approved by all required stakeholders and is ready to issue.`,
                        channels: ['system', 'email'],
                        templateCode: 'ONBOARDING_STATUS_UPDATE',
                        placeholders: {
                            dealerName: application2?.applicantName || '',
                            requestId: application2?.applicationId || '',
                            link: `${getFrontendBaseUrl()}/applications/${request.applicationId}`,
                            ctaLabel: 'View Application'
                        }
                    }).catch((e: any) => console.error('[LOI] finance/admin notify failed:', e));
                }
            }
            res.json({ success: true, message: 'LOI Request fully approved and document generated' });
        } else {
            // SRS §6.16.3.5 — Sequential: DD-Head approved → now notify NBH
            if (action === 'Approved' && 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: LOI Final Approval`,
                        message: `DD Head has approved the LOI for ${request.applicationId}. Your final sign-off is required.`,
                        channels: phone ? ['system', 'email', 'whatsapp'] : ['system', 'email'],
                        templateCode: 'WORKFLOW_ACTION_REQUIRED',
                        placeholders: {
                            dealerName: '',
                            requestId: String(request.applicationId),
                            targetStage: 'NBH LOI Approval',
                            link: `${getFrontendBaseUrl()}/applications/${request.applicationId}`,
                            phone: phone || ''
                        }
                    }).catch((e: any) => console.error('[LOI] NBH notify failed:', e));
                }
            }
            res.json({
                success: true,
                message: 'Approval recorded. Waiting for remaining required approvers.',
                data: {
                    stageCode: LOI_STAGE_CODE,
                    requiredRoles,
                    minApprovals: policy.minApprovals,
                    approvedRoles: Array.from(approvedRoles),
                    hasAllRequiredRoleApprovals,
                    meetsMinApprovals
                }
            });
        }

        await AuditLog.create({
            userId: req.user?.id,
            action: action === 'Approved' ? AUDIT_ACTIONS.LOI_APPROVED : AUDIT_ACTIONS.LOI_REJECTED,
            entityType: 'loi_request',
            entityId: requestId,
            newData: { level: currentApproval.level, action }
        });

    } catch (error) {
        console.error('Approve LOI 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 ensureLoiPolicy();
        const actions = await StageApprovalAction.findAll({
            where: { applicationId: application.id, stageCode: LOI_STAGE_CODE },
            include: [{ model: User, as: 'actor', attributes: ['id', 'fullName', 'email', 'roleCode'] }],
            order: [['updatedAt', 'DESC']]
        });
        res.json({
            success: true,
            data: {
                stageCode: LOI_STAGE_CODE,
                minApprovals: policy.minApprovals,
                approvalMode: policy.approvalMode,
                requiredRoles: policy.requiredRoles || [],
                actions
            }
        });
    } catch (error) {
        console.error('Get LOI approval status error:', error);
        res.status(500).json({ success: false, message: 'Error fetching LOI approval status' });
    }
};

export const generateDocument = async (req: AuthRequest, res: Response) => {
    try {
        const { requestId } = req.body;
        // Mocking document generation
        const mockFile = `LOI_MANUAL_${Date.now()}.pdf`;
        const reqRecord = await LoiRequest.findByPk(requestId);
        
        let docId = null;
        if (reqRecord) {
            const docRecord = await db.OnboardingDocument.create({
                applicationId: reqRecord.applicationId,
                documentType: 'LOI',
                fileName: mockFile,
                filePath: `/uploads/loi/${mockFile}`,
                status: 'active'
            });
            docId = docRecord.id;
        }

        const doc = docId
            ? await LoiDocumentGenerated.create({
                  requestId: reqRecord ? reqRecord.id : requestId,
                  documentId: docId,
                  version: '1.0'
              })
            : null;

        // Bridge: Transition from LOI Issued -> Dealer Code Generation
        const request = await LoiRequest.findByPk(requestId);
        if (request) {
            const application = await db.Application.findByPk(request.applicationId);
            if (application) {
                await WorkflowService.transitionApplication(application, APPLICATION_STATUS.DEALER_CODE_GENERATION, req.user?.id || null, {
                    reason: 'LOI Document issued. Proceeding to Dealer Code Generation.',
                    progressPercentage: 85
                });

                // SRS §6.16.3.6 — LOI issued: email ONLY to applicant (explicitly NO WhatsApp per SRS)
                if (application.email) {
                    sendEmail(
                        application.email,
                        `Letter of Intent Issued: ${application.applicationId}`,
                        'LOI_ISSUED',
                        {
                            applicantName: application.applicantName || 'Applicant',
                            applicationId: application.applicationId,
                            link: getProspectPortalUrl(application.id),
                            ctaLabel: 'View Your Application'
                        }
                    ).catch((e: any) => console.error('[LOI] Applicant email failed:', e));
                }

                // SRS §6.16.3.6 — Alert Finance, DD-Head, NBH confirming LOI issuance (System + Email)
                const stakeholderRoles = [ROLES.FINANCE, ROLES.DD_HEAD, ROLES.NBH];
                for (const role of stakeholderRoles) {
                    const roleUsers = await User.findAll({ where: { roleCode: role } });
                    for (const u of roleUsers) {
                        NotificationService.notify(u.id, u.email, {
                            title: `LOI Issued: ${application.applicationId}`,
                            message: `Letter of Intent has been issued for ${application.applicantName || application.applicationId}. Dealer code generation is now pending.`,
                            channels: ['system', 'email'],
                            templateCode: 'LOI_ISSUED',
                            placeholders: {
                                applicantName: application.applicantName || '',
                                applicationId: application.applicationId,
                                link: `${getFrontendBaseUrl()}/applications/${application.id}`,
                                ctaLabel: 'View Application'
                            }
                        }).catch((e: any) => console.error('[LOI] stakeholder notify failed:', e));
                    }
                }

                if (application.email) {
                    const portalBase = getFrontendBaseUrl();
                    await NotificationService.notify(null, application.email, {
                        title: `Statutory documents — ${application.applicationId}`,
                        message: 'Please upload statutory and compliance documents on the Dealer Portal.',
                        channels: ['email'],
                        templateCode: 'STATUTORY_DOCUMENT_REQUEST',
                        placeholders: {
                            applicantName: application.applicantName || 'Applicant',
                            applicationId: application.applicationId,
                            dueDate: formatDueDateDaysFromNow(21),
                            statutoryList: DEFAULT_STATUTORY_DOCUMENT_CHECKLIST,
                            link: getProspectPortalUrl(application.id),
                            ctaLabel: 'Upload statutory documents'
                        }
                    }).catch((e: any) => console.error('[LOI] Statutory document email failed:', e));
                }
            }
        }

        res.json({ success: true, message: 'LOI Document generated (Mock)', data: doc });
    } catch (error) {
        res.status(500).json({ success: false, message: 'Error generating document' });
    }
};
