import { Request, Response } from 'express';
import db from '../../database/models/index.js';
const { AuditLog, User } = db;
import { AuthRequest } from '../../types/express.types.js';
import { resolveEntityUuidByType, normalizeRequestType } from '../../common/utils/requestResolver.js';

// Human-readable descriptions for audit actions
const ACTION_DESCRIPTIONS: Record<string, string> = {
    CREATED: 'Record created',
    UPDATED: 'Record updated',
    APPROVED: 'Approval',
    REJECTED: 'Rejected',
    DELETED: 'Record deleted',
    LOGIN: 'User logged in',
    LOGOUT: 'User logged out',
    REGISTERED: 'User registered',
    PASSWORD_CHANGED: 'Password changed',
    PROFILE_UPDATED: 'Profile updated',
    STAGE_CHANGED: 'Stage changed',
    SHORTLISTED: 'Application shortlisted',
    DISQUALIFIED: 'Application disqualified',
    QUESTIONNAIRE_SUBMITTED: 'Questionnaire response submitted',
    QUESTIONNAIRE_LINK_SENT: 'Questionnaire link sent to applicant',
    DOCUMENT_UPLOADED: 'Document uploaded',
    DOCUMENT_VERIFIED: 'Document verified',
    DOCUMENT_REJECTED: 'Document rejected',
    WORKNOTE_ADDED: 'Work note added',
    ATTACHMENT_UPLOADED: 'Attachment uploaded',
    PARTICIPANT_ADDED: 'Participant added',
    PARTICIPANT_REMOVED: 'Participant removed',
    INTERVIEW_SCHEDULED: 'Interview scheduled',
    INTERVIEW_UPDATED: 'Interview updated',
    INTERVIEW_COMPLETED: 'Interview completed',
    KT_MATRIX_SUBMITTED: 'KT Matrix score submitted',
    FEEDBACK_SUBMITTED: 'Interview feedback submitted',
    RECOMMENDATION_UPDATED: 'Recommendation updated',
    DECISION_MADE: 'Interview decision made',
    FDD_ASSIGNED: 'FDD agency assigned',
    FDD_REPORT_UPLOADED: 'FDD report uploaded',
    LOI_REQUESTED: 'LOI requested',
    LOI_APPROVED: 'LOI approved',
    LOI_REJECTED: 'LOI rejected',
    LOI_GENERATED: 'LOI document generated',
    LOA_REQUESTED: 'LOA requested',
    LOA_APPROVED: 'LOA approved',
    LOA_GENERATED: 'LOA document generated',
    EOR_CHECKLIST_CREATED: 'EOR checklist initiated',
    EOR_ITEM_UPDATED: 'EOR checklist item updated',
    EOR_AUDIT_SUBMITTED: 'EOR audit submitted',
    DEALER_CREATED: 'Dealer profile created',
    DEALER_UPDATED: 'Dealer profile updated',
    DEALER_CODE_GENERATED: 'Dealer code generated',
    PAYMENT_UPDATED: 'Payment record updated',
    SECURITY_DEPOSIT_UPDATED: 'Security Deposit updated',
    FNF_UPDATED: 'F&F settlement updated',
    CLEARANCE_UPDATED: 'Departmental clearance response recorded',
    STAKEHOLDER_CLEARANCE_UPDATED: 'F&F stakeholder clearance synced',
    USER_CREATED: 'User account created',
    USER_UPDATED: 'User account updated',
    USER_STATUS_CHANGED: 'User status changed',
    ROLE_CREATED: 'Role created',
    ROLE_UPDATED: 'Role updated',
    RESIGNATION_SUBMITTED: 'Resignation submitted',
    RESIGNATION_APPROVED: 'Resignation approved',
    RESIGNATION_REJECTED: 'Resignation rejected',
    RESIGNATION_REVOKED: 'Resignation request revoked',
    RESIGNATION_SENT_BACK: 'Resignation sent back for clarification',
    TERMINATION_REVOKED: 'Termination request revoked',
    TERMINATION_SENT_BACK: 'Termination sent back for clarification',
    RELOCATION_SENT_BACK: 'Relocation sent back',
    RELOCATION_REVOKED: 'Relocation revoked',
    CONSTITUTIONAL_SENT_BACK: 'Constitutional change sent back',
    CONSTITUTIONAL_REVOKED: 'Constitutional change revoked',
    EMAIL_SENT: 'Email notification sent',
    REMINDER_SENT: 'Reminder sent',
    FDD_FLAGGED_NON_RESPONSIVE: 'APPLICANT FLAGGED: Non-responsive to audit queries'
};

const isIdleParallelStatus = (v: unknown) => {
    const s = String(v ?? '')
        .trim()
        .toUpperCase();
    return !s || s === 'PENDING' || s === 'NULL' || s === 'NOT_STARTED';
};

/** Readable copy for onboarding status transitions (avoids repeating unchanged parallel tracks). */
function buildFriendlyApplicationUpdatedDescription(logData: any, payload: any): string {
    const oldD = logData.oldData || {};
    const oldCtx = (oldD.context || {}) as Record<string, unknown>;
    const newCtx = (payload.context || {}) as Record<string, unknown>;
    const pipeline = payload.pipelineStage as string | undefined;
    const status = payload.status as string | undefined;
    const oldStatus = oldD.status as string | undefined;
    const reason = payload.reason != null ? String(payload.reason).trim() : '';

    const parts: string[] = [];

    if (pipeline) {
        parts.push(`Onboarding progressed to ${pipeline}`);
        if (oldStatus && status && oldStatus !== status) {
            parts.push(`Overall status: ${oldStatus} → ${status}`);
        }
    } else if (status && oldStatus && oldStatus !== status) {
        parts.push(`Application status: ${oldStatus} → ${status}`);
    } else if (status) {
        parts.push(`Application status: ${status}`);
    } else {
        parts.push('Application updated');
    }

    const statChanged = oldCtx.statutoryStatus !== newCtx.statutoryStatus;
    const archChanged = oldCtx.architectureStatus !== newCtx.architectureStatus;

    if (statChanged) {
        parts.push(
            `Statutory: ${oldCtx.statutoryStatus ?? '—'} → ${newCtx.statutoryStatus ?? '—'}`
        );
    } else if (!isIdleParallelStatus(newCtx.statutoryStatus) && String(newCtx.statutoryStatus)) {
        parts.push(`Statutory: ${newCtx.statutoryStatus}`);
    }

    if (archChanged) {
        parts.push(
            `Architecture: ${oldCtx.architectureStatus ?? '—'} → ${newCtx.architectureStatus ?? '—'}`
        );
    } else if (!isIdleParallelStatus(newCtx.architectureStatus) && String(newCtx.architectureStatus)) {
        parts.push(`Architecture: ${newCtx.architectureStatus}`);
    }

    const src = payload.transitionSource as string | undefined;
    if (src && !/WorkflowService\.transitionApplication/i.test(src) && !/^WorkflowService$/i.test(src.trim())) {
        parts.push(`Note: ${src}`);
    }

    if (reason && !/^Transitioned to\b/i.test(reason) && reason.length < 200) {
        parts.push(reason);
    }

    return parts.join(' · ');
}

function buildFriendlyInterviewUpdatedDescription(logData: any, payload: any): string {
    const eventType = String(payload?.eventType || '').toLowerCase();
    const oldData = logData?.oldData || {};
    const oldSchedule = oldData?.scheduleDate ? new Date(oldData.scheduleDate).toLocaleString() : null;
    const newSchedule = payload?.scheduleDate ? new Date(payload.scheduleDate).toLocaleString() : null;
    const level = payload?.interviewLevel ? `Level ${payload.interviewLevel}` : 'Interview';

    if (eventType === 'interview_cancelled') {
        return `${level} interview cancelled`;
    }

    if (eventType === 'interview_rescheduled') {
        if (oldSchedule && newSchedule && oldSchedule !== newSchedule) {
            return `${level} interview rescheduled from ${oldSchedule} to ${newSchedule}`;
        }
        if (newSchedule) {
            return `${level} interview rescheduled to ${newSchedule}`;
        }
        return `${level} interview rescheduled`;
    }

    return `${level} interview updated`;
}

const getNormalizedAuditPayload = (logData: any, entityType: string, entityId: string) => {
    const payload = logData.details || logData.newData || {};
    const actorName = logData.user?.fullName || logData.userName || 'System';
    const action = logData.action || 'UPDATED';
    const et = String(entityType || '').toLowerCase();

    let description = ACTION_DESCRIPTIONS[action] ||
        String(action).split('_').map((w: any) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(' ');

    if (et === 'application' && action === 'UPDATED') {
        description = buildFriendlyApplicationUpdatedDescription(logData, payload);
    } else if (et === 'application' && action === 'INTERVIEW_UPDATED') {
        description = buildFriendlyInterviewUpdatedDescription(logData, payload);
    } else {
        if (payload?.stage) description += ` - Stage: ${payload.stage}`;
        else if (payload?.department) description += ` - ${payload.department}`;
        else if (payload?.status && action === 'UPDATED') description += ` to ${payload.status}`;

        if (payload?.transitionSource && et !== 'application') {
            description += ` · Source: ${payload.transitionSource}`;
        }
        if (payload?.pipelineStage && et === 'application' && action !== 'UPDATED') {
            description += ` · Pipeline: ${payload.pipelineStage}`;
        }
    }
    if (payload?.documentType && action === 'DOCUMENT_UPLOADED') {
        description += ` · ${payload.documentType}`;
    }
    if (payload?.paymentType && action === 'PAYMENT_UPDATED') {
        description += ` · ${payload.paymentType}`;
    }

    return {
        id: logData.id,
        action,
        description,
        entityType,
        entityId,
        stage:
            payload?.pipelineStage ||
            payload?.stage ||
            payload?.targetStage ||
            (payload?.context as any)?.currentStage ||
            null,
        actor: {
            id: logData.userId || logData.actorId || logData.user?.id || null,
            name: actorName,
            email: logData.user?.email || logData.userEmail || null
        },
        actorId: logData.userId || logData.actorId || logData.user?.id || null,
        userName: actorName,
        userEmail: logData.user?.email || logData.userEmail || null,
        remarks: logData.remarks || payload?.remarks || '',
        newData: logData.newData ?? payload,
        details: payload,
        oldData: logData.oldData ?? null,
        timestamp: logData.createdAt || logData.timestamp
    };
};

/**
 * Get audit logs for a specific entity (e.g., application)
 * Query params: entityType, entityId, page, limit
 */
export const getAuditLogs = async (req: AuthRequest, res: Response) => {
    try {
        const { entityType, entityId, page = '1', limit = '50' } = req.query;
        console.log(`[AuditController] Fetching logs for ${entityType} ID: ${entityId}`);

        if (!entityType || !entityId) {
            return res.status(400).json({
                success: false,
                message: 'entityType and entityId are required'
            });
        }

        const pageNum = Math.max(1, parseInt(page as string));
        const limitNum = Math.min(100, Math.max(1, parseInt(limit as string)));
        const offset = (pageNum - 1) * limitNum;

        let count = 0;
        let logs: any[] = [];

        // Dynamic Table Switching based on Module
        // Case-insensitive entity type routing
        const type = normalizeRequestType(entityType as string);
        const { resolvedId } = await resolveEntityUuidByType(db as any, entityId as string, type);
        
        if (type === 'resignation') {
            const result = await db.ResignationAudit.findAndCountAll({
                where: { resignationId: resolvedId },
                include: [{ model: db.User, as: 'user', attributes: ['id', 'fullName', 'email'] }],
                order: [['createdAt', 'DESC']],
                limit: limitNum, offset
            });
            count = result.count;
            logs = result.rows;
        } else if (type === 'termination') {
            const result = await db.TerminationAudit.findAndCountAll({
                where: { terminationRequestId: resolvedId },
                include: [{ model: db.User, as: 'user', attributes: ['id', 'fullName', 'email'] }],
                order: [['createdAt', 'DESC']],
                limit: limitNum, offset
            });
            count = result.count;
            logs = result.rows;
        } else if (type === 'fnf') {
            const result = await db.FnFAudit.findAndCountAll({
                where: { fnfId: resolvedId },
                include: [{ model: db.User, as: 'user', attributes: ['id', 'fullName', 'email'] }],
                order: [['createdAt', 'DESC']],
                limit: limitNum, offset
            });
            count = result.count;
            logs = result.rows;
        } else if (type === 'constitutional') {
            const result = await db.ConstitutionalAudit.findAndCountAll({
                where: { constitutionalChangeId: resolvedId },
                include: [{ model: db.User, as: 'user', attributes: ['id', 'fullName', 'email'] }],
                order: [['createdAt', 'DESC']],
                limit: limitNum, offset
            });
            count = result.count;
            logs = result.rows;
        } else if (type === 'relocation') {
            const result = await db.RelocationAudit.findAndCountAll({
                where: { relocationRequestId: resolvedId },
                include: [{ model: db.User, as: 'user', attributes: ['id', 'fullName', 'email'] }],
                order: [['createdAt', 'DESC']],
                limit: limitNum, offset
            });
            count = result.count;
            logs = result.rows;
        } else {
            console.log(`[AuditController] Falling back to global AuditLog for type: ${type}`);
            const result = await db.AuditLog.findAndCountAll({
                where: { entityType: entityType as string, entityId: resolvedId },
                include: [{ model: db.User, as: 'user', attributes: ['id', 'fullName', 'email'] }],
                order: [['createdAt', 'DESC']],
                limit: limitNum, offset
            });
            count = result.count;
            logs = result.rows;
        }

        console.log(`[AuditController] Found ${count} logs for ${entityType}`);

        // Format the response with human-readable descriptions and consistent mapping
        const formattedLogs = logs.map((log: any) => {
            const logData = log.get ? log.get({ plain: true }) : log;
            if (logData.details?.statutoryStatus === 'Flagged') {
                logData.action = 'FDD_FLAGGED_NON_RESPONSIVE';
            }
            return getNormalizedAuditPayload(logData, entityType as string, entityId as string);
        });

        res.json({
            success: true,
            data: formattedLogs,
            pagination: {
                total: count,
                page: pageNum,
                limit: limitNum,
                totalPages: Math.ceil(count / limitNum)
            }
        });
    } catch (error) {
        console.error('Get audit logs error:', error);
        res.status(500).json({ success: false, message: 'Error fetching audit logs' });
    }
};

/**
 * Get audit log summary/stats for an entity
 */
export const getAuditSummary = async (req: AuthRequest, res: Response) => {
    try {
        const { entityType, entityId } = req.query;

        if (!entityType || !entityId) {
            return res.status(400).json({
                success: false,
                message: 'entityType and entityId are required'
            });
        }

        let totalLogs = 0;
        let latestLog: any = null;
        const type = normalizeRequestType(entityType as string);
        const { resolvedId } = await resolveEntityUuidByType(db as any, entityId as string, type);

        // Dynamic Table Switching
        if (type === 'resignation') {
            totalLogs = await db.ResignationAudit.count({ where: { resignationId: resolvedId } });
            latestLog = await db.ResignationAudit.findOne({
                where: { resignationId: resolvedId },
                include: [{ model: db.User, as: 'user', attributes: ['id', 'fullName'] }],
                order: [['createdAt', 'DESC']]
            });
        } else if (type === 'termination') {
            totalLogs = await db.TerminationAudit.count({ where: { terminationRequestId: resolvedId } });
            latestLog = await db.TerminationAudit.findOne({
                where: { terminationRequestId: resolvedId },
                include: [{ model: db.User, as: 'user', attributes: ['id', 'fullName'] }],
                order: [['createdAt', 'DESC']]
            });
        } else if (type === 'fnf') {
            totalLogs = await db.FnFAudit.count({ where: { fnfId: resolvedId } });
            latestLog = await db.FnFAudit.findOne({
                where: { fnfId: resolvedId },
                include: [{ model: db.User, as: 'user', attributes: ['id', 'fullName'] }],
                order: [['createdAt', 'DESC']]
            });
        } else if (type === 'constitutional') {
            totalLogs = await db.ConstitutionalAudit.count({ where: { constitutionalChangeId: resolvedId } });
            latestLog = await db.ConstitutionalAudit.findOne({
                where: { constitutionalChangeId: resolvedId },
                include: [{ model: db.User, as: 'user', attributes: ['id', 'fullName'] }],
                order: [['createdAt', 'DESC']]
            });
        } else if (type === 'relocation') {
            totalLogs = await db.RelocationAudit.count({ where: { relocationRequestId: resolvedId } });
            latestLog = await db.RelocationAudit.findOne({
                where: { relocationRequestId: resolvedId },
                include: [{ model: db.User, as: 'user', attributes: ['id', 'fullName'] }],
                order: [['createdAt', 'DESC']]
            });
        } else {
            totalLogs = await db.AuditLog.count({ where: { entityType: entityType as string, entityId: resolvedId } });
            latestLog = await db.AuditLog.findOne({
                where: { entityType: entityType as string, entityId: resolvedId },
                include: [{ model: db.User, as: 'user', attributes: ['id', 'fullName'] }],
                order: [['createdAt', 'DESC']]
            });
        }

        res.json({
            success: true,
            data: {
                totalEntries: totalLogs,
                lastActivity: latestLog ? {
                    action: (latestLog as any).action,
                    description: ACTION_DESCRIPTIONS[(latestLog as any).action] || (latestLog as any).action.split('_').map((w: any) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(' '),
                    user: (latestLog as any).user?.fullName || 'System',
                    timestamp: (latestLog as any).createdAt
                } : null
            }
        });
    } catch (error) {
        console.error('Get audit summary error:', error);
        res.status(500).json({ success: false, message: 'Error fetching audit summary' });
    }
};

/**
 * Format changes between old and new data into a readable list
 */
function formatChanges(oldData: any, newData: any): string[] {
    const changes: string[] = [];

    if (!oldData && newData) {
        // New record created
        if (typeof newData === 'object') {
            Object.entries(newData).forEach(([key, value]) => {
                if (value !== null && value !== undefined) {
                    changes.push(`${formatFieldName(key)}: ${value}`);
                }
            });
        }
        return changes;
    }

    if (oldData && newData && typeof oldData === 'object' && typeof newData === 'object') {
        // Compare old vs new
        const allKeys = new Set([...Object.keys(oldData), ...Object.keys(newData)]);
        allKeys.forEach(key => {
            const oldVal = oldData[key];
            const newVal = newData[key];
            if (JSON.stringify(oldVal) !== JSON.stringify(newVal)) {
                if (oldVal !== undefined && newVal !== undefined) {
                    changes.push(`${formatFieldName(key)}: "${oldVal}" → "${newVal}"`);
                } else if (newVal !== undefined) {
                    changes.push(`${formatFieldName(key)} set to "${newVal}"`);
                }
            }
        });
    }

    return changes;
}

/**
 * Convert camelCase field names to human-readable format
 */
function formatFieldName(field: string): string {
    return field
        .replace(/([A-Z])/g, ' $1')
        .replace(/^./, str => str.toUpperCase())
        .trim();
}
