/**
 * Dealer Claim Approval Service
 * 
 * Dedicated approval service for dealer claim workflows (CLAIM_MANAGEMENT).
 * Handles dealer claim-specific logic including:
 * - Dynamic approver support (additional approvers added between steps)
 * - Activity Creation processing
 * - Dealer-specific notifications
 * 
 * This service is separate from ApprovalService to prevent conflicts with custom workflows.
 */

import { ApprovalLevel } from '@models/ApprovalLevel';
import { WorkflowRequest } from '@models/WorkflowRequest';
import { User } from '@models/User';
import { ApprovalAction } from '../types/approval.types';
import { ApprovalStatus, WorkflowStatus } from '../types/common.types';
import { calculateTATPercentage } from '@utils/helpers';
import { calculateElapsedWorkingHours } from '@utils/tatTimeUtils';
import logger from '@utils/logger';
import { Op } from 'sequelize';
import { notificationService } from './notification.service';
import { activityService } from './activity.service';
import { tatSchedulerService } from './tatScheduler.service';
import { DealerClaimService } from './dealerClaim.service';
import { emitToRequestRoom } from '../realtime/socket';

const appDomain = process.env.APP_DOMAIN || 'royalenfield.com';


let dealerClaimServiceInstance: any;

export class DealerClaimApprovalService {
  // Use lazy initialization to avoid circular dependency
  private getDealerClaimService(): DealerClaimService {
    if (!dealerClaimServiceInstance) {
      const { DealerClaimService } = require('./dealerClaim.service');
      dealerClaimServiceInstance = new DealerClaimService();
    }
    return dealerClaimServiceInstance;
  }
  /**
   * Approve a level in a dealer claim workflow
   * Handles dealer claim-specific logic including dynamic approvers and activity creation
   */
  async approveLevel(
    levelId: string,
    action: ApprovalAction,
    userId: string,
    requestMetadata?: { ipAddress?: string | null; userAgent?: string | null }
  ): Promise<ApprovalLevel | null> {
    try {
      const level = await ApprovalLevel.findByPk(levelId);
      if (!level) return null;

      // Get workflow to determine priority for working hours calculation
      const wf = await WorkflowRequest.findByPk(level.requestId);
      if (!wf) return null;

      // Verify this is a claim management workflow
      const workflowType = (wf as any)?.workflowType;
      if (workflowType !== 'CLAIM_MANAGEMENT') {
        logger.warn(`[DealerClaimApproval] Attempted to use DealerClaimApprovalService for non-claim-management workflow ${level.requestId}. Workflow type: ${workflowType}`);
        throw new Error('DealerClaimApprovalService can only be used for CLAIM_MANAGEMENT workflows');
      }

      const priority = ((wf as any)?.priority || 'standard').toString().toLowerCase();
      const isPaused = (wf as any).isPaused || (level as any).isPaused;

      // If paused, resume automatically when approving/rejecting
      if (isPaused) {
        const { pauseService } = await import('./pause.service');
        try {
          await pauseService.resumeWorkflow(level.requestId, userId);
          logger.info(`[DealerClaimApproval] Auto-resumed paused workflow ${level.requestId} when ${action.action === 'APPROVE' ? 'approving' : 'rejecting'}`);
        } catch (pauseError) {
          logger.warn(`[DealerClaimApproval] Failed to auto-resume paused workflow:`, pauseError);
          // Continue with approval/rejection even if resume fails
        }
      }

      const now = new Date();

      // Calculate elapsed hours using working hours logic (with pause handling)
      const isPausedLevel = (level as any).isPaused;
      const wasResumed = !isPausedLevel &&
        (level as any).pauseElapsedHours !== null &&
        (level as any).pauseElapsedHours !== undefined &&
        (level as any).pauseResumeDate !== null;

      const pauseInfo = isPausedLevel ? {
        // Level is currently paused - return frozen elapsed hours at pause time
        isPaused: true,
        pausedAt: (level as any).pausedAt,
        pauseElapsedHours: (level as any).pauseElapsedHours,
        pauseResumeDate: (level as any).pauseResumeDate
      } : wasResumed ? {
        // Level was paused but has been resumed - add pre-pause elapsed hours + time since resume
        isPaused: false,
        pausedAt: null,
        pauseElapsedHours: Number((level as any).pauseElapsedHours), // Pre-pause elapsed hours
        pauseResumeDate: (level as any).pauseResumeDate // Actual resume timestamp
      } : undefined;

      const elapsedHours = await calculateElapsedWorkingHours(
        (level as any).levelStartTime || (level as any).tatStartTime || now,
        now,
        priority,
        pauseInfo
      );
      const tatPercentage = calculateTATPercentage(elapsedHours, level.tatHours);

      // Handle rejection
      if (action.action === 'REJECT') {
        return await this.handleRejection(level, action, userId, requestMetadata, elapsedHours, tatPercentage, now);
      }

      logger.info(`[DealerClaimApproval] Approving level ${levelId} with action:`, JSON.stringify(action));

      // Robust comment extraction
      const approvalComment = action.comments || (action as any).comment || '';

      // Update level status and elapsed time for approval FIRST
      // Only save snapshot if the update succeeds
      await level.update({
        status: ApprovalStatus.APPROVED,
        actionDate: now,
        levelEndTime: now,
        elapsedHours: elapsedHours,
        tatPercentageUsed: tatPercentage,
        comments: approvalComment || undefined
      });

      // Check if this is a dealer submission (proposal or completion) - these have their own snapshot types
      const levelName = (level.levelName || '').toLowerCase();
      const isDealerSubmission = levelName.includes('dealer proposal') || levelName.includes('dealer completion');

      // Only save APPROVE snapshot for actual approver actions (not dealer submissions)
      // Dealer submissions use PROPOSAL/COMPLETION snapshot types instead
      if (!isDealerSubmission) {
        try {
          await this.getDealerClaimService().saveApprovalHistory(
            level.requestId,
            level.levelId,
            level.levelNumber,
            'APPROVE',
            approvalComment,
            undefined,
            userId
          );
        } catch (snapshotError) {
          // Log error but don't fail the approval - snapshot is for audit, not essential
          logger.error(`[DealerClaimApproval] Failed to save approval history snapshot (non-critical):`, snapshotError);
        }
      }

      // Note: We don't save workflow history for approval actions
      // The approval history (saveApprovalHistory) is sufficient and includes comments
      // Workflow movement information is included in the APPROVE snapshot's changeReason

      // Check if this is the final approver
      const allLevels = await ApprovalLevel.findAll({
        where: { requestId: level.requestId }
      });
      const approvedCount = allLevels.filter((l: any) => l.status === ApprovalStatus.APPROVED).length;
      const isFinalApprover = approvedCount === allLevels.length;

      if (isFinalApprover) {
        // Final approval - close workflow
        await WorkflowRequest.update(
          {
            status: WorkflowStatus.APPROVED,
            closureDate: now,
            currentLevel: level.levelNumber || 0
          },
          { where: { requestId: level.requestId } }
        );

        // Notify all participants
        const participants = await import('@models/Participant').then(m => m.Participant.findAll({
          where: { requestId: level.requestId, isActive: true }
        }));

        if (participants && participants.length > 0) {
          const participantIds = participants.map((p: any) => p.userId).filter(Boolean);
          await notificationService.sendToUsers(participantIds, {
            title: `Request Approved: ${(wf as any).requestNumber}`,
            body: `${(wf as any).title}`,
            requestNumber: (wf as any).requestNumber,
            requestId: level.requestId,
            url: `/request/${(wf as any).requestNumber}`,
            type: 'approval',
            priority: 'MEDIUM'
          });
          logger.info(`[DealerClaimApproval] Final approval complete. ${participants.length} participant(s) notified.`);
        }
      } else {
        // Not final - move to next level
        // Check if workflow is paused - if so, don't advance
        if ((wf as any).isPaused || (wf as any).status === 'PAUSED') {
          logger.warn(`[DealerClaimApproval] Cannot advance workflow ${level.requestId} - workflow is paused`);
          throw new Error('Cannot advance workflow - workflow is currently paused. Please resume the workflow first.');
        }

        // Find the next PENDING level (supports dynamically added approvers)
        // Strategy: First try sequential, then find next PENDING level if sequential doesn't exist
        const currentLevelNumber = level.levelNumber || 0;
        logger.info(`[DealerClaimApproval] Finding next level after level ${currentLevelNumber} for request ${level.requestId}`);

        // First, try sequential approach
        let nextLevel = await ApprovalLevel.findOne({
          where: {
            requestId: level.requestId,
            levelNumber: currentLevelNumber + 1
          }
        });

        // If sequential level doesn't exist, search for next PENDING level
        // This handles cases where additional approvers are added dynamically between steps
        if (!nextLevel) {
          logger.info(`[DealerClaimApproval] Sequential level ${currentLevelNumber + 1} not found, searching for next PENDING level (dynamic approvers)`);
          nextLevel = await ApprovalLevel.findOne({
            where: {
              requestId: level.requestId,
              levelNumber: { [Op.gt]: currentLevelNumber },
              status: ApprovalStatus.PENDING
            },
            order: [['levelNumber', 'ASC']]
          });

          if (nextLevel) {
            logger.info(`[DealerClaimApproval] Using fallback level ${nextLevel.levelNumber} (${(nextLevel as any).levelName || 'unnamed'})`);
          }
        } else if (nextLevel.status !== ApprovalStatus.PENDING) {
          // Sequential level exists but not PENDING - check if it's already approved/rejected
          if (nextLevel.status === ApprovalStatus.APPROVED || nextLevel.status === ApprovalStatus.REJECTED) {
            logger.warn(`[DealerClaimApproval] Sequential level ${currentLevelNumber + 1} already ${nextLevel.status}. Skipping activation.`);
            nextLevel = null; // Don't activate an already completed level
          } else {
            // Level exists but in unexpected status - log warning but proceed
            logger.warn(`[DealerClaimApproval] Sequential level ${currentLevelNumber + 1} exists but status is ${nextLevel.status}, expected PENDING. Proceeding with sequential level.`);
          }
        }

        const nextLevelNumber = nextLevel ? (nextLevel.levelNumber || 0) : null;

        if (nextLevel) {
          logger.info(`[DealerClaimApproval] Found next level: ${nextLevelNumber} (${(nextLevel as any).levelName || 'unnamed'}), approver: ${(nextLevel as any).approverName || (nextLevel as any).approverEmail || 'unknown'}, status: ${nextLevel.status}`);

          // SPECIAL LOGIC: If we are moving to Dealer Completion Documents,
          // check if this is a re-quotation flow. If so, skip this step and move to the next one.
          const currentNextLevelName = (nextLevel as any).levelName || '';
          if (currentNextLevelName === 'Dealer Completion Documents') {
            try {
              const { DealerClaimHistory, SnapshotType } = await import('@models/DealerClaimHistory');
              const reQuotationHistory = await DealerClaimHistory.findOne({
                where: {
                  requestId: level.requestId,
                  snapshotType: SnapshotType.APPROVE,
                  changeReason: { [Op.iLike]: '%Revised Quotation Requested%' }
                }
              });
 
              if (reQuotationHistory) {
                logger.info(`[DealerClaimApproval] Re-quotation detected in history for request ${level.requestId}. Skipping Dealer Completion Documents.`);
 
                // Skip this level
                await nextLevel.update({
                  status: ApprovalStatus.SKIPPED,
                  comments: 'Skipped - Re-quotation flow'
                });
 
                // Find the NEXT available level (regardless of its number)
                const nextTarget = await ApprovalLevel.findOne({
                  where: {
                    requestId: level.requestId,
                    levelNumber: { [Op.gt]: nextLevel.levelNumber }
                  },
                  order: [['levelNumber', 'ASC']]
                });
 
                if (nextTarget) {
                  logger.info(`[DealerClaimApproval] Redirecting to next target level ${nextTarget.levelNumber} (${(nextTarget as any).levelName || 'unnamed'}) for request ${level.requestId}`);
                  nextLevel = nextTarget;
                } else {
                  logger.info(`[DealerClaimApproval] No further steps after skipping Dealer Completion Documents for request ${level.requestId}`);
                  nextLevel = null;
                }
              }
            } catch (historyError) {
              logger.error(`[DealerClaimApproval] Error checking re-quotation history:`, historyError);
              // Fallback: proceed normally if history check fails
            }
          }

          // Important: Update nextLevelNumber in case nextLevel was shifted (e.g. Step 4 skip)
          // This ensures WorkflowRequest.currentLevel is updated to the correct active level
          const finalNextLevelNumber = nextLevel ? (nextLevel.levelNumber || 0) : null;

          if (nextLevel) {
            // Check if next level is paused - if so, don't activate it
            if ((nextLevel as any).isPaused || (nextLevel as any).status === 'PAUSED') {
              logger.warn(`[DealerClaimApproval] Cannot activate next level ${finalNextLevelNumber} - level is paused`);
              throw new Error('Cannot activate next level - the next approval level is currently paused. Please resume it first.');
            }

            // Activate next level
            await nextLevel.update({
              status: ApprovalStatus.IN_PROGRESS,
              levelStartTime: now,
              tatStartTime: now
            });

            // Schedule TAT jobs for the next level
            try {
              const workflowPriority = (wf as any)?.priority || 'STANDARD';

              await tatSchedulerService.scheduleTatJobs(
                level.requestId,
                (nextLevel as any).levelId,
                (nextLevel as any).approverId,
                Number((nextLevel as any).tatHours),
                now,
                workflowPriority
              );
              logger.info(`[DealerClaimApproval] TAT jobs scheduled for next level ${finalNextLevelNumber} (Priority: ${workflowPriority})`);
            } catch (tatError) {
              logger.error(`[DealerClaimApproval] Failed to schedule TAT jobs for next level:`, tatError);
              // Don't fail the approval if TAT scheduling fails
            }

            // Update workflow current level
            if (finalNextLevelNumber !== null) {
              await WorkflowRequest.update(
                { currentLevel: finalNextLevelNumber },
                { where: { requestId: level.requestId } }
              );

              // Update the APPROVE snapshot's changeReason to include movement information
              // This ensures the approval snapshot shows both the approval and the movement
              // We don't create a separate WORKFLOW snapshot for approvals - only APPROVE snapshot
              try {
                const { DealerClaimHistory } = await import('@models/DealerClaimHistory');
                const { SnapshotType } = await import('@models/DealerClaimHistory');

                const approvalHistory = await DealerClaimHistory.findOne({
                  where: {
                    requestId: level.requestId,
                    approvalLevelId: level.levelId,
                    snapshotType: SnapshotType.APPROVE
                  },
                  order: [['createdAt', 'DESC']]
                });

                if (approvalHistory) {
                  // Use the robust approvalComment from outer scope
                  const updatedChangeReason = approvalComment
                    ? `Approved by ${level.approverName || level.approverEmail}, moved to next level (${finalNextLevelNumber}). Comment: ${approvalComment}`
                    : `Approved by ${level.approverName || level.approverEmail}, moved to next level (${finalNextLevelNumber})`;

                  await approvalHistory.update({
                    changeReason: updatedChangeReason
                  });
                }
              } catch (updateError) {
                // Log error but don't fail - this is just updating the changeReason for better display
                logger.warn(`[DealerClaimApproval] Failed to update approval history changeReason (non-critical):`, updateError);
              }

              logger.info(`[DealerClaimApproval] Approved level ${level.levelNumber}. Activated next level ${finalNextLevelNumber} for workflow ${level.requestId}`);
            }
          }    // Handle dealer claim-specific step processing
          const currentLevelName = (level.levelName || '').toLowerCase();
          // Check by levelName first, use levelNumber only as fallback if levelName is missing
          // This handles cases where additional approvers shift step numbers
          const hasLevelNameForDeptLead = level.levelName && level.levelName.trim() !== '';
          const isDeptLeadApproval = hasLevelNameForDeptLead
            ? currentLevelName.includes('department lead')
            : (level.levelNumber === 3); // Only use levelNumber if levelName is missing

          const isRequestorClaimApproval = hasLevelNameForDeptLead
            ? (currentLevelName.includes('requestor') && (currentLevelName.includes('claim') || currentLevelName.includes('approval')))
            : (level.levelNumber === 5); // Only use levelNumber if levelName is missing

          if (isDeptLeadApproval) {
            // Activity Creation is now an activity log only - process it automatically
            logger.info(`[DealerClaimApproval] Department Lead approved. Processing Activity Creation as activity log.`);
            try {
              const dealerClaimService = this.getDealerClaimService();
              await dealerClaimService.processActivityCreation(level.requestId);
              logger.info(`[DealerClaimApproval] Activity Creation activity logged for request ${level.requestId}`);
            } catch (activityError) {
              logger.error(`[DealerClaimApproval] Error processing Activity Creation activity for request ${level.requestId}:`, activityError);
              // Don't fail the Department Lead approval if Activity Creation logging fails
            }
          } else if (isRequestorClaimApproval) {
            // E-Invoice Generation is now an activity log only - will be logged when invoice is generated via DMS webhook
            logger.info(`[DealerClaimApproval] Requestor Claim Approval approved. E-Invoice generation will be logged as activity when DMS webhook is received.`);
          }

          // Log approval activity
          activityService.log({
            requestId: level.requestId,
            type: 'approval',
            user: { userId: level.approverId, name: level.approverName },
            timestamp: new Date().toISOString(),
            action: 'Approved',
            details: `Request approved and forwarded to ${(nextLevel as any).approverName || (nextLevel as any).approverEmail} by ${level.approverName || level.approverEmail}`,
            ipAddress: requestMetadata?.ipAddress || undefined,
            userAgent: requestMetadata?.userAgent || undefined
          });

          // Notify initiator about the approval
          // BUT skip this if it's a dealer proposal or dealer completion step - those have special notifications below
          // Priority: levelName check first, then levelNumber only if levelName is missing
          const hasLevelNameForApproval = level.levelName && level.levelName.trim() !== '';
          const levelNameForApproval = hasLevelNameForApproval && level.levelName ? level.levelName.toLowerCase() : '';
          const isDealerProposalApproval = hasLevelNameForApproval
            ? (levelNameForApproval.includes('dealer') && levelNameForApproval.includes('proposal'))
            : (level.levelNumber === 1); // Only use levelNumber if levelName is missing
          const isDealerCompletionApproval = hasLevelNameForApproval
            ? (levelNameForApproval.includes('dealer') && (levelNameForApproval.includes('completion') || levelNameForApproval.includes('documents')))
            : (level.levelNumber === 5); // Only use levelNumber if levelName is missing

          // Skip sending approval notification to initiator if they are the approver
          // (they don't need to be notified that they approved their own request)
          const isApproverInitiator = level.approverId && (wf as any).initiatorId && level.approverId === (wf as any).initiatorId;

          if (wf && !isDealerProposalApproval && !isDealerCompletionApproval && !isApproverInitiator) {
            await notificationService.sendToUsers([(wf as any).initiatorId], {
              title: `Request Approved - Level ${level.levelNumber}`,
              body: `Your request "${(wf as any).title}" has been approved by ${level.approverName || level.approverEmail} and forwarded to the next approver.`,
              requestNumber: (wf as any).requestNumber,
              requestId: level.requestId,
              url: `/request/${(wf as any).requestNumber}`,
              type: 'approval',
              priority: 'MEDIUM'
            });
          } else if (isApproverInitiator) {
            logger.info(`[DealerClaimApproval] Skipping approval notification to initiator - they are the approver`);
          }

          // Notify next approver - ALWAYS send notification when there's a next level
          if (wf && nextLevel) {
            const nextApproverId = (nextLevel as any).approverId;
            const nextApproverEmail = (nextLevel as any).approverEmail || '';
            const nextApproverName = (nextLevel as any).approverName || nextApproverEmail || 'approver';

            // Check if it's an auto-step or system process
            const isAutoStep = nextApproverEmail === `system@${appDomain}`
              || (nextLevel as any).approverName === 'System Auto-Process'
              || nextApproverId === 'system';

            const isSystemEmail = nextApproverEmail.toLowerCase() === `system@${appDomain}`
              || nextApproverEmail.toLowerCase().includes('system');
            const isSystemName = nextApproverName.toLowerCase() === 'system auto-process'
              || nextApproverName.toLowerCase().includes('system');

            // Notify initiator when dealer submits documents (Dealer Proposal or Dealer Completion Documents)
            // Check this BEFORE sending assignment notification to avoid duplicates
            // Priority: levelName check first, then levelNumber only if levelName is missing
            const hasLevelNameForNotification = level.levelName && level.levelName.trim() !== '';
            const levelNameForNotification = hasLevelNameForNotification && level.levelName ? level.levelName.toLowerCase() : '';
            const isDealerProposalApproval = hasLevelNameForNotification
              ? (levelNameForNotification.includes('dealer') && levelNameForNotification.includes('proposal'))
              : (level.levelNumber === 1); // Only use levelNumber if levelName is missing
            const isDealerCompletionApproval = hasLevelNameForNotification
              ? (levelNameForNotification.includes('dealer') && (levelNameForNotification.includes('completion') || levelNameForNotification.includes('documents')))
              : (level.levelNumber === 5); // Only use levelNumber if levelName is missing

            // Check if next approver is the initiator (to avoid duplicate notifications)
            const isNextApproverInitiator = nextApproverId && (wf as any).initiatorId && nextApproverId === (wf as any).initiatorId;

            if (isDealerProposalApproval && (wf as any).initiatorId) {
              // Get dealer and proposal data for the email template
              const { DealerClaimDetails } = await import('@models/DealerClaimDetails');
              const { DealerProposalDetails } = await import('@models/DealerProposalDetails');
              const { DealerProposalCostItem } = await import('@models/DealerProposalCostItem');

              const claimDetails = await DealerClaimDetails.findOne({ where: { requestId: level.requestId } });
              const proposalDetails = await DealerProposalDetails.findOne({ where: { requestId: level.requestId } });

              // Get cost items if proposal exists
              let costBreakup: any[] = [];
              if (proposalDetails) {
                const proposalId = (proposalDetails as any).proposalId || (proposalDetails as any).proposal_id;
                if (proposalId) {
                  const costItems = await DealerProposalCostItem.findAll({
                    where: { proposalId },
                    order: [['itemOrder', 'ASC']]
                  });
                  costBreakup = costItems.map((item: any) => ({
                    description: item.itemDescription || item.description,
                    amount: Number(item.amount) || 0
                  }));
                }
              }

              // Get dealer user
              const dealerUser = level.approverId ? await User.findByPk(level.approverId) : null;
              const dealerData = dealerUser ? dealerUser.toJSON() : {
                userId: level.approverId,
                email: level.approverEmail || '',
                displayName: level.approverName || level.approverEmail || 'Dealer'
              };

              // Get next approver (could be Step 2 - Requestor Evaluation, or an additional approver if one was added between Step 1 and Step 2)
              // The nextLevel is already found above using dynamic logic that handles additional approvers correctly
              const nextApproverData = nextLevel ? await User.findByPk((nextLevel as any).approverId) : null;

              // Check if next approver is an additional approver (handles cases where additional approvers are added between Step 1 and Step 2)
              const nextLevelName = nextLevel ? ((nextLevel as any).levelName || '').toLowerCase() : '';
              const isNextAdditionalApprover = nextLevelName.includes('additional approver');

              // Send proposal submitted notification with proper type and metadata
              // This will use the dealerProposalSubmitted template, not the multi-level approval template
              await notificationService.sendToUsers([(wf as any).initiatorId], {
                title: 'Proposal Submitted',
                body: `Dealer ${dealerData.displayName || dealerData.email} has submitted a proposal for your claim request "${(wf as any).title}".`,
                requestNumber: (wf as any).requestNumber,
                requestId: (wf as any).requestId,
                url: `/request/${(wf as any).requestNumber}`,
                type: 'proposal_submitted',
                priority: 'MEDIUM',
                actionRequired: false,
                metadata: {
                  dealerData: dealerData,
                  proposalData: {
                    totalEstimatedBudget: proposalDetails ? (proposalDetails as any).totalEstimatedBudget : 0,
                    expectedCompletionDate: proposalDetails ? (proposalDetails as any).expectedCompletionDate : undefined,
                    dealerComments: proposalDetails ? (proposalDetails as any).dealerComments : undefined,
                    costBreakup: costBreakup,
                    submittedAt: proposalDetails ? (proposalDetails as any).submittedAt : new Date(),
                    nextApproverIsAdditional: isNextAdditionalApprover,
                    nextApproverIsInitiator: isNextApproverInitiator
                  },
                  nextApproverId: nextApproverData ? nextApproverData.userId : undefined,
                  // Add activity information from claimDetails
                  activityName: claimDetails ? (claimDetails as any).activityName : undefined,
                  activityType: claimDetails ? (claimDetails as any).activityType : undefined
                }
              });

              logger.info(`[DealerClaimApproval] Sent proposal_submitted notification to initiator for Dealer Proposal Submission. Next approver: ${isNextApproverInitiator ? 'Initiator (self)' : (isNextAdditionalApprover ? 'Additional Approver' : 'Step 2 (Requestor Evaluation)')}`);
            } else if (isDealerCompletionApproval && (wf as any).initiatorId) {
              // Get dealer and completion data for the email template
              const { DealerClaimDetails } = await import('@models/DealerClaimDetails');
              const { DealerCompletionDetails } = await import('@models/DealerCompletionDetails');
              const { DealerCompletionExpense } = await import('@models/DealerCompletionExpense');

              const claimDetails = await DealerClaimDetails.findOne({ where: { requestId: level.requestId } });
              const completionDetails = await DealerCompletionDetails.findOne({ where: { requestId: level.requestId } });

              // Get expense items if completion exists
              let closedExpenses: any[] = [];
              if (completionDetails) {
                const expenses = await DealerCompletionExpense.findAll({
                  where: { requestId: level.requestId },
                  order: [['createdAt', 'ASC']]
                });
                closedExpenses = expenses.map((item: any) => ({
                  description: item.description || '',
                  amount: Number(item.amount) || 0
                }));
              }

              // Get dealer user
              const dealerUser = level.approverId ? await User.findByPk(level.approverId) : null;
              const dealerData = dealerUser ? dealerUser.toJSON() : {
                userId: level.approverId,
                email: level.approverEmail || '',
                displayName: level.approverName || level.approverEmail || 'Dealer'
              };

              // Get next approver (could be Step 5 - Requestor Claim Approval, or an additional approver if one was added between Step 4 and Step 5)
              const nextApproverData = nextLevel ? await User.findByPk((nextLevel as any).approverId) : null;

              // Check if next approver is an additional approver (handles cases where additional approvers are added between Step 4 and Step 5)
              const nextLevelName = nextLevel ? ((nextLevel as any).levelName || '').toLowerCase() : '';
              const isNextAdditionalApprover = nextLevelName.includes('additional approver');

              // Check if next approver is the initiator (to show appropriate message in email)
              const isNextApproverInitiator = nextApproverData && (wf as any).initiatorId && nextApproverData.userId === (wf as any).initiatorId;

              // Send completion submitted notification with proper type and metadata
              // This will use the completionDocumentsSubmitted template, not the multi-level approval template
              await notificationService.sendToUsers([(wf as any).initiatorId], {
                title: 'Completion Documents Submitted',
                body: `Dealer ${dealerData.displayName || dealerData.email} has submitted completion documents for your claim request "${(wf as any).title}".`,
                requestNumber: (wf as any).requestNumber,
                requestId: (wf as any).requestId,
                url: `/request/${(wf as any).requestNumber}`,
                type: 'completion_submitted',
                priority: 'MEDIUM',
                actionRequired: false,
                metadata: {
                  dealerData: dealerData,
                  completionData: {
                    activityCompletionDate: completionDetails ? (completionDetails as any).activityCompletionDate : undefined,
                    numberOfParticipants: completionDetails ? (completionDetails as any).numberOfParticipants : undefined,
                    totalClosedExpenses: completionDetails ? (completionDetails as any).totalClosedExpenses : 0,
                    closedExpenses: closedExpenses,
                    documentsCount: undefined, // Documents count can be retrieved from documents table if needed
                    submittedAt: completionDetails ? (completionDetails as any).submittedAt : new Date(),
                    nextApproverIsAdditional: isNextAdditionalApprover,
                    nextApproverIsInitiator: isNextApproverInitiator
                  },
                  nextApproverId: nextApproverData ? nextApproverData.userId : undefined
                }
              });

              logger.info(`[DealerClaimApproval] Sent completion_submitted notification to initiator for Dealer Completion Documents. Next approver: ${isNextAdditionalApprover ? 'Additional Approver' : 'Step 5 (Requestor Claim Approval)'}`);
            }

            // Only send assignment notification to next approver if:
            // 1. It's NOT a dealer proposal/completion step (those have special notifications above)
            // 2. Next approver is NOT the initiator (to avoid duplicate notifications)
            // 3. It's not a system/auto step
            if (!isDealerProposalApproval && !isDealerCompletionApproval && !isNextApproverInitiator) {
              if (!isAutoStep && !isSystemEmail && !isSystemName && nextApproverId && nextApproverId !== 'system') {
                try {
                  logger.info(`[DealerClaimApproval] Sending assignment notification to next approver: ${nextApproverName} (${nextApproverId}) at level ${nextLevelNumber} for request ${(wf as any).requestNumber}`);

                  let assignmentTitle = `Action required: ${(wf as any).requestNumber}`;
                  let assignmentBody = `${(wf as any).title}`;
                  const nextLevelNameStr = (nextLevel as any).levelName || '';

                  if (nextLevelNameStr.toLowerCase().includes('proposal')) {
                    assignmentTitle = 'Proposal Required';
                    assignmentBody = `A proposal is required for request ${(wf as any).requestNumber}: ${(wf as any).title}`;
                  } else if (nextLevelNameStr.toLowerCase().includes('completion') || nextLevelNameStr.toLowerCase().includes('documents')) {
                    assignmentTitle = 'Completion Documents Required';
                    assignmentBody = `Completion documents are required for request ${(wf as any).requestNumber}: ${(wf as any).title}`;
                  }

                  await notificationService.sendToUsers([nextApproverId], {
                    title: assignmentTitle,
                    body: assignmentBody,
                    requestNumber: (wf as any).requestNumber,
                    requestId: (wf as any).requestId,
                    url: `/request/${(wf as any).requestNumber}`,
                    type: 'assignment',
                    priority: 'HIGH',
                    actionRequired: true
                  });

                  logger.info(`[DealerClaimApproval] ✅ Assignment notification sent successfully to ${nextApproverName} (${nextApproverId}) for level ${nextLevelNumber}`);

                  // Log assignment activity for the next approver
                  await activityService.log({
                    requestId: level.requestId,
                    type: 'assignment',
                    user: { userId: level.approverId, name: level.approverName },
                    timestamp: new Date().toISOString(),
                    action: 'Assigned to approver',
                    details: `Request assigned to ${nextApproverName} for ${(nextLevel as any).levelName || `level ${nextLevelNumber}`}`,
                    ipAddress: requestMetadata?.ipAddress || undefined,
                    userAgent: requestMetadata?.userAgent || undefined
                  });
                } catch (notifError) {
                  logger.error(`[DealerClaimApproval] ❌ Failed to send notification to next approver ${nextApproverId} at level ${nextLevelNumber}:`, notifError);
                  // Don't throw - continue with workflow even if notification fails
                }
              } else {
                logger.info(`[DealerClaimApproval] ⚠️ Skipping notification for system/auto-step: ${nextApproverEmail} (${nextApproverId}) at level ${nextLevelNumber}`);
              }
            } else {
              if (isDealerProposalApproval || isDealerCompletionApproval) {
                logger.info(`[DealerClaimApproval] ⚠️ Skipping assignment notification - dealer-specific notification already sent`);
              }
              if (isNextApproverInitiator) {
                logger.info(`[DealerClaimApproval] ⚠️ Skipping assignment notification - next approver is the initiator (already notified)`);
              }
            }
          }
        } else {
          // No next level found but not final approver - this shouldn't happen
          logger.warn(`[DealerClaimApproval] No next level found for workflow ${level.requestId} after approving level ${level.levelNumber}`);
          await WorkflowRequest.update(
            {
              status: WorkflowStatus.APPROVED,
              closureDate: now,
              currentLevel: level.levelNumber || 0
            },
            { where: { requestId: level.requestId } }
          );
          if (wf) {
            await notificationService.sendToUsers([(wf as any).initiatorId], {
              title: `Approved: ${(wf as any).requestNumber}`,
              body: `${(wf as any).title}`,
              requestNumber: (wf as any).requestNumber,
              requestId: level.requestId,
              url: `/request/${(wf as any).requestNumber}`,
              type: 'approval',
              priority: 'MEDIUM'
            });
          }
        }
      }

      // Emit real-time update to all users viewing this request
      emitToRequestRoom(level.requestId, 'request:updated', {
        requestId: level.requestId,
        requestNumber: (wf as any)?.requestNumber,
        action: action.action,
        levelNumber: level.levelNumber,
        timestamp: now.toISOString()
      });

      logger.info(`[DealerClaimApproval] Approval level ${levelId} ${action.action.toLowerCase()}ed and socket event emitted`);

      return level;
    } catch (error) {
      logger.error('[DealerClaimApproval] Error approving level:', error);
      throw error;
    }
  }

  /**
   * Handle rejection (internal method called from approveLevel)
   */
  private async handleRejection(
    level: ApprovalLevel,
    action: ApprovalAction,
    userId: string,
    requestMetadata?: { ipAddress?: string | null; userAgent?: string | null },
    elapsedHours?: number,
    tatPercentage?: number,
    now?: Date
  ): Promise<ApprovalLevel | null> {
    const rejectionNow = now || new Date();
    const wf = await WorkflowRequest.findByPk(level.requestId);
    if (!wf) return null;

    // Check if this is the Department Lead approval step (Step 3)
    // Robust check: check level name for variations and level number as fallback
    // Default rejection logic: Return to immediately previous approval step
    logger.info(`[DealerClaimApproval] Rejection for request ${level.requestId} by level ${level.levelNumber}. Finding previous step to return to.`);

    // Save approval history (rejection) BEFORE updating level
    await this.getDealerClaimService().saveApprovalHistory(
      level.requestId,
      level.levelId,
      level.levelNumber,
      'REJECT',
      action.comments || '',
      action.rejectionReason || undefined,
      userId
    );

    // Find all levels to determine previous step
    const allLevels = await ApprovalLevel.findAll({
      where: { requestId: level.requestId },
      order: [['levelNumber', 'ASC']]
    });

    // Find the immediately previous approval level
    const currentLevelNumber = level.levelNumber || 0;
    const previousLevels = allLevels.filter(l => l.levelNumber < currentLevelNumber && l.levelNumber > 0);
    const previousLevel = previousLevels[previousLevels.length - 1];

    // Update level status - if returning to previous step, set this level to PENDING (reset)
    // If no previous step (terminal rejection), set to REJECTED
    const newStatus = previousLevel ? ApprovalStatus.PENDING : ApprovalStatus.REJECTED;

    await level.update({
      status: newStatus,
      // If resetting to PENDING, clear action details so it can be acted upon again later
      actionDate: previousLevel ? null : rejectionNow,
      levelEndTime: previousLevel ? null : rejectionNow,
      elapsedHours: previousLevel ? 0 : (elapsedHours || 0),
      tatPercentageUsed: previousLevel ? 0 : (tatPercentage || 0),
      comments: previousLevel ? null : (action.comments || action.rejectionReason || undefined)
    } as any);

    // If no previous level found (this is the first step), close the workflow
    if (!previousLevel) {
      logger.info(`[DealerClaimApproval] No previous level found. This is the first step. Closing workflow.`);

      // Capture workflow snapshot for terminal rejection
      await this.getDealerClaimService().saveWorkflowHistory(
        level.requestId,
        `Level ${level.levelNumber} rejected (terminal rejection - no previous step)`,
        userId,
        level.levelId,
        level.levelNumber,
        level.levelName || undefined
      );

      // Close workflow FIRST
      await WorkflowRequest.update(
        {
          status: WorkflowStatus.REJECTED,
          closureDate: rejectionNow
        },
        { where: { requestId: level.requestId } }
      );

      // Capture workflow snapshot AFTER workflow is closed successfully
      try {
        await this.getDealerClaimService().saveWorkflowHistory(
          level.requestId,
          `Level ${level.levelNumber} rejected (terminal rejection - no previous step)`,
          userId,
          level.levelId,
          level.levelNumber,
          level.levelName || undefined
        );
      } catch (snapshotError) {
        // Log error but don't fail the rejection - snapshot is for audit, not essential
        logger.error(`[DealerClaimApproval] Failed to save workflow history snapshot (non-critical):`, snapshotError);
      }

      // Log rejection activity (terminal rejection)
      activityService.log({
        requestId: level.requestId,
        type: 'rejection',
        user: { userId: level.approverId, name: level.approverName },
        timestamp: rejectionNow.toISOString(),
        action: 'Rejected',
        details: `Request rejected by ${level.approverName || level.approverEmail}. Reason: ${action.rejectionReason || action.comments || 'No reason provided'}`,
        ipAddress: requestMetadata?.ipAddress || undefined,
        userAgent: requestMetadata?.userAgent || undefined
      });

      // Notify initiator and participants (workflow is closed)
      const participants = await import('@models/Participant').then(m => m.Participant.findAll({
        where: { requestId: level.requestId, isActive: true }
      }));

      const userIdsToNotify = [(wf as any).initiatorId];
      if (participants && participants.length > 0) {
        participants.forEach((p: any) => {
          if (p.userId && p.userId !== (wf as any).initiatorId) {
            userIdsToNotify.push(p.userId);
          }
        });
      }

      await notificationService.sendToUsers(userIdsToNotify, {
        title: `Request Rejected: ${(wf as any).requestNumber}`,
        body: `${(wf as any).title} - Rejected by ${level.approverName || level.approverEmail}. Reason: ${action.rejectionReason || action.comments || 'No reason provided'}`,
        requestNumber: (wf as any).requestNumber,
        requestId: level.requestId,
        url: `/request/${(wf as any).requestNumber}`,
        type: 'rejection',
        priority: 'HIGH'
      });
    } else {
      // Return to previous step
      let targetLevel = previousLevel;
      const isReQuotation = (action.rejectionReason || action.comments || '').includes('Revised Quotation Requested');

      if (isReQuotation) {
        // Find Step 1 (Dealer Proposal Submission) by name (more robust than hardcoded level 1)
        const step1 = allLevels.find(l => (l as any).levelName === 'Dealer Proposal Submission');
        if (step1) {
          logger.info(`[DealerClaimApproval] Re-quotation requested. Jumping to Dealer Proposal Submission (${step1.levelNumber}) for request ${level.requestId}`);
          targetLevel = step1;

          // Reset all intermediate levels (between Step 1 and current level)
          const intermediateLevels = allLevels.filter(l => l.levelNumber > 1 && l.levelNumber <= currentLevelNumber);
          for (const iLevel of intermediateLevels) {
            await iLevel.update({
              status: ApprovalStatus.PENDING,
              actionDate: null,
              levelEndTime: null,
              comments: null,
              elapsedHours: 0,
              tatPercentageUsed: 0
            } as any);
          }

          // Trigger Re-Quotation Notification (Email + Push)
          if (step1.approverId) {
            await notificationService.sendToUsers([step1.approverId], {
              title: `Revised Quotation Requested: ${(wf as any).requestNumber}`,
              body: action.rejectionReason || action.comments || 'Please revise and resubmit your quotation.',
              requestNumber: (wf as any).requestNumber,
              requestId: level.requestId,
              url: `/request/${(wf as any).requestNumber}`,
              type: 're_quotation',
              priority: 'HIGH',
              actionRequired: true,
              metadata: {
                rejectionReason: action.rejectionReason || action.comments
              }
            });
          }
        }
      }

      logger.info(`[DealerClaimApproval] Returning to level ${targetLevel.levelNumber} (${targetLevel.levelName || 'unnamed'})`);

      // Reset target level to IN_PROGRESS so it can be acted upon again
      await targetLevel.update({
        status: ApprovalStatus.IN_PROGRESS,
        levelStartTime: rejectionNow,
        tatStartTime: rejectionNow,
        actionDate: undefined,
        levelEndTime: undefined,
        comments: undefined,
        elapsedHours: 0,
        tatPercentageUsed: 0
      });

      // Update workflow status to PENDING/IN_PROGRESS
      // Set currentLevel to target level
      await WorkflowRequest.update(
        {
          status: WorkflowStatus.PENDING,
          currentLevel: targetLevel.levelNumber
        },
        { where: { requestId: level.requestId } }
      );

      // Log rejection activity (returned to step)
      activityService.log({
        requestId: level.requestId,
        type: 'rejection',
        user: { userId: level.approverId, name: level.approverName },
        timestamp: rejectionNow.toISOString(),
        action: isReQuotation ? 'Re-quotation Requested' : 'Returned to Previous Step',
        details: `Request rejected by ${level.approverName || level.approverEmail} and returned to level ${targetLevel.levelNumber}. Reason: ${action.rejectionReason || action.comments || 'No reason provided'}`,
        ipAddress: requestMetadata?.ipAddress || undefined,
        userAgent: requestMetadata?.userAgent || undefined
      });

      // Notify the approver of the target level (skip if specific re-quotation notification was already sent)
      if (targetLevel.approverId && !isReQuotation) {
        await notificationService.sendToUsers([targetLevel.approverId], {
          title: `Request Returned for Revision: ${(wf as any).requestNumber}`,
          body: `Request "${(wf as any).title}" has been returned to your level for revision. Reason: ${action.rejectionReason || action.comments || 'No reason provided'}`,
          requestNumber: (wf as any).requestNumber,
          requestId: level.requestId,
          url: `/request/${(wf as any).requestNumber}`,
          type: 'assignment',
          priority: 'HIGH',
          actionRequired: true
        });
      }

      // Notify initiator when request is returned
      // Skip if they are the same as the dealer (who already got a more specific notification)
      const dealerId = targetLevel.approverId;
      if ((wf as any).initiatorId && (wf as any).initiatorId !== dealerId) {
        await notificationService.sendToUsers([(wf as any).initiatorId], {
          title: `Request Returned: ${(wf as any).requestNumber}`,
          body: `Request "${(wf as any).title}" has been returned to Step ${targetLevel.levelNumber} for revision. Reason: ${action.rejectionReason || action.comments || 'No reason provided'}`,
          requestNumber: (wf as any).requestNumber,
          requestId: level.requestId,
          url: `/request/${(wf as any).requestNumber}`,
          type: 'rejection',
          priority: 'MEDIUM'
        });
      }
    }

    // Emit real-time update to all users viewing this request
    emitToRequestRoom(level.requestId, 'request:updated', {
      requestId: level.requestId,
      requestNumber: (wf as any)?.requestNumber,
      action: 'REJECT',
      levelNumber: level.levelNumber,
      timestamp: rejectionNow.toISOString()
    });

    return level;
  }

  /**
   * Reject a level in a dealer claim workflow (legacy method - kept for backward compatibility)
   */
  async rejectLevel(
    levelId: string,
    reason: string,
    comments: string,
    userId: string,
    requestMetadata?: { ipAddress?: string | null; userAgent?: string | null }
  ): Promise<ApprovalLevel | null> {
    try {
      const level = await ApprovalLevel.findByPk(levelId);
      if (!level) return null;

      const wf = await WorkflowRequest.findByPk(level.requestId);
      if (!wf) return null;

      // Verify this is a claim management workflow
      const workflowType = (wf as any)?.workflowType;
      if (workflowType !== 'CLAIM_MANAGEMENT') {
        logger.warn(`[DealerClaimApproval] Attempted to use DealerClaimApprovalService for non-claim-management workflow ${level.requestId}. Workflow type: ${workflowType}`);
        throw new Error('DealerClaimApprovalService can only be used for CLAIM_MANAGEMENT workflows');
      }

      const now = new Date();

      // Calculate elapsed hours
      const priority = ((wf as any)?.priority || 'standard').toString().toLowerCase();
      const isPausedLevel = (level as any).isPaused;
      const wasResumed = !isPausedLevel &&
        (level as any).pauseElapsedHours !== null &&
        (level as any).pauseElapsedHours !== undefined &&
        (level as any).pauseResumeDate !== null;

      const pauseInfo = isPausedLevel ? {
        // Level is currently paused - return frozen elapsed hours at pause time
        isPaused: true,
        pausedAt: (level as any).pausedAt,
        pauseElapsedHours: (level as any).pauseElapsedHours,
        pauseResumeDate: (level as any).pauseResumeDate
      } : wasResumed ? {
        // Level was paused but has been resumed - add pre-pause elapsed hours + time since resume
        isPaused: false,
        pausedAt: null,
        pauseElapsedHours: Number((level as any).pauseElapsedHours), // Pre-pause elapsed hours
        pauseResumeDate: (level as any).pauseResumeDate // Actual resume timestamp
      } : undefined;

      // Use the internal handleRejection method
      const elapsedHours = await calculateElapsedWorkingHours(
        (level as any).levelStartTime || (level as any).tatStartTime || now,
        now,
        priority,
        pauseInfo
      );
      const tatPercentage = calculateTATPercentage(elapsedHours, level.tatHours);

      return await this.handleRejection(
        level,
        { action: 'REJECT', comments: comments || reason, rejectionReason: reason || comments },
        userId,
        requestMetadata,
        elapsedHours,
        tatPercentage,
        now
      );
    } catch (error) {
      logger.error('[DealerClaimApproval] Error rejecting level:', error);
      throw error;
    }
  }

  /**
   * Get current approval level for a request
   */
  async getCurrentApprovalLevel(requestId: string): Promise<ApprovalLevel | null> {
    const workflow = await WorkflowRequest.findByPk(requestId);
    if (!workflow) return null;

    const currentLevel = (workflow as any).currentLevel;
    if (!currentLevel) return null;

    return await ApprovalLevel.findOne({
      where: { requestId, levelNumber: currentLevel }
    });
  }

  /**
   * Get all approval levels for a request
   */
  async getApprovalLevels(requestId: string): Promise<ApprovalLevel[]> {
    return await ApprovalLevel.findAll({
      where: { requestId },
      order: [['levelNumber', 'ASC']]
    });
  }
}
