import { ApprovalLevel } from '@models/ApprovalLevel';
import { WorkflowRequest } from '@models/WorkflowRequest';
import { Participant } from '@models/Participant';
import { TatAlert } from '@models/TatAlert';
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, { logWorkflowEvent, logAIEvent } from '@utils/logger';
import { Op } from 'sequelize';
import { notificationService } from './notification.service';
import { activityService } from './activity.service';
import { tatSchedulerService } from './tatScheduler.service';
import { emitToRequestRoom } from '../realtime/socket';

const appDomain = process.env.APP_DOMAIN || 'royalenfield.com';
// Note: DealerClaimService import removed - dealer claim approvals are handled by DealerClaimApprovalService

export class ApprovalService {
  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 NOT a claim management workflow (should use DealerClaimApprovalService)
      const workflowType = (wf as any)?.workflowType;
      if (workflowType === 'CLAIM_MANAGEMENT') {
        logger.error(`[Approval] Attempted to use ApprovalService for CLAIM_MANAGEMENT workflow ${level.requestId}. Use DealerClaimApprovalService instead.`);
        throw new Error('ApprovalService cannot be used for CLAIM_MANAGEMENT workflows. Use DealerClaimApprovalService instead.');
      }

      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 (requirement 3.6)
      if (isPaused) {
        const { pauseService } = await import('./pause.service');
        try {
          await pauseService.resumeWorkflow(level.requestId, _userId);
          logger.info(`[Approval] Auto-resumed paused workflow ${level.requestId} when ${action.action === 'APPROVE' ? 'approving' : 'rejecting'}`);
        } catch (pauseError) {
          logger.warn(`[Approval] 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)
      // Case 1: Level is currently paused (isPaused = true)
      // Case 2: Level was paused and resumed (isPaused = false but pauseElapsedHours and pauseResumeDate exist)
      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.levelStartTime || level.createdAt,
        now,
        priority,
        pauseInfo
      );
      const tatPercentage = calculateTATPercentage(elapsedHours, level.tatHours);

      const updateData = {
        status: action.action === 'APPROVE' ? ApprovalStatus.APPROVED : ApprovalStatus.REJECTED,
        actionDate: now,
        levelEndTime: now,
        elapsedHours,
        tatPercentageUsed: tatPercentage,
        comments: action.comments,
        rejectionReason: action.rejectionReason
      };

      const updatedLevel = await level.update(updateData);

      // Cancel TAT jobs for the current level since it's been actioned
      try {
        await tatSchedulerService.cancelTatJobs(level.requestId, level.levelId);
        logger.info(`[Approval] TAT jobs cancelled for level ${level.levelId}`);
      } catch (tatError) {
        logger.error(`[Approval] Failed to cancel TAT jobs:`, tatError);
        // Don't fail the approval if TAT cancellation fails
      }

      // Update TAT alerts for this level to mark completion status
      try {
        const wasOnTime = elapsedHours <= level.tatHours;
        await TatAlert.update(
          {
            wasCompletedOnTime: wasOnTime,
            completionTime: now
          },
          {
            where: { levelId: level.levelId }
          }
        );
        logger.info(`[Approval] TAT alerts updated for level ${level.levelId} - Completed ${wasOnTime ? 'on time' : 'late'}`);
      } catch (tatAlertError) {
        logger.error(`[Approval] Failed to update TAT alerts:`, tatAlertError);
        // Don't fail the approval if TAT alert update fails
      }

      // Handle approval - move to next level or close workflow (wf already loaded above)
      if (action.action === 'APPROVE') {
        // Check if this is final approval: either isFinalApprover flag is set OR all levels are approved
        // This handles cases where additional approvers are added after initial approval
        const allLevels = await ApprovalLevel.findAll({
          where: { requestId: level.requestId },
          order: [['levelNumber', 'ASC']]
        });
        const approvedLevelsCount = allLevels.filter((l: any) => l.status === 'APPROVED').length;
        const totalLevels = allLevels.length;
        const isAllLevelsApproved = approvedLevelsCount === totalLevels;
        const isFinalApproval = level.isFinalApprover || isAllLevelsApproved;

        if (isFinalApproval) {
          // Final approver - close workflow as APPROVED
          await WorkflowRequest.update(
            {
              status: WorkflowStatus.APPROVED,
              closureDate: now,
              currentLevel: (level.levelNumber || 0) + 1
            },
            { where: { requestId: level.requestId } }
          );
          logWorkflowEvent('approved', level.requestId, {
            level: level.levelNumber,
            isFinalApproval: true,
            status: 'APPROVED',
            detectedBy: level.isFinalApprover ? 'isFinalApprover flag' : 'all levels approved check'
          });

          // Log final approval activity first (so it's included in AI context)
          activityService.log({
            requestId: level.requestId,
            type: 'approval',
            user: { userId: level.approverId, name: level.approverName },
            timestamp: new Date().toISOString(),
            action: 'Approved',
            details: `Request approved and finalized by ${level.approverName || level.approverEmail}. Awaiting conclusion remark from initiator.`,
            ipAddress: requestMetadata?.ipAddress || undefined,
            userAgent: requestMetadata?.userAgent || undefined
          });

          // Generate AI conclusion remark ASYNCHRONOUSLY (don't wait)
          // This runs in the background without blocking the approval response
          (async () => {
            try {
              const { aiService } = await import('./ai.service');
              const { ConclusionRemark } = await import('@models/index');
              const { ApprovalLevel } = await import('@models/ApprovalLevel');
              const { WorkNote } = await import('@models/WorkNote');
              const { Document } = await import('@models/Document');
              const { Activity } = await import('@models/Activity');
              const { getConfigValue } = await import('./configReader.service');

              // Check if AI features and remark generation are enabled in admin config
              const aiEnabled = (await getConfigValue('AI_ENABLED', 'true'))?.toLowerCase() === 'true';
              const remarkGenerationEnabled = (await getConfigValue('AI_REMARK_GENERATION_ENABLED', 'true'))?.toLowerCase() === 'true';

              if (aiEnabled && remarkGenerationEnabled && aiService.isAvailable()) {
                logAIEvent('request', {
                  requestId: level.requestId,
                  action: 'conclusion_generation_started',
                });

                // Gather context for AI generation
                const approvalLevels = await ApprovalLevel.findAll({
                  where: { requestId: level.requestId },
                  order: [['levelNumber', 'ASC']]
                });

                const workNotes = await WorkNote.findAll({
                  where: { requestId: level.requestId },
                  order: [['createdAt', 'ASC']],
                  limit: 20
                });

                const documents = await Document.findAll({
                  where: { requestId: level.requestId },
                  order: [['uploadedAt', 'DESC']]
                });

                const activities = await Activity.findAll({
                  where: { requestId: level.requestId },
                  order: [['createdAt', 'ASC']],
                  limit: 50
                });

                // Build context object
                const context = {
                  requestTitle: (wf as any).title,
                  requestDescription: (wf as any).description,
                  requestNumber: (wf as any).requestNumber,
                  priority: (wf as any).priority,
                  approvalFlow: approvalLevels.map((l: any) => {
                    const tatPercentage = l.tatPercentageUsed !== undefined && l.tatPercentageUsed !== null
                      ? Number(l.tatPercentageUsed)
                      : (l.elapsedHours && l.tatHours ? (Number(l.elapsedHours) / Number(l.tatHours)) * 100 : 0);
                    return {
                      levelNumber: l.levelNumber,
                      approverName: l.approverName,
                      status: l.status,
                      comments: l.comments,
                      actionDate: l.actionDate,
                      tatHours: Number(l.tatHours || 0),
                      elapsedHours: Number(l.elapsedHours || 0),
                      tatPercentageUsed: tatPercentage
                    };
                  }),
                  workNotes: workNotes.map((note: any) => ({
                    userName: note.userName,
                    message: note.message,
                    createdAt: note.createdAt
                  })),
                  documents: documents.map((doc: any) => ({
                    fileName: doc.originalFileName || doc.fileName,
                    uploadedBy: doc.uploadedBy,
                    uploadedAt: doc.uploadedAt
                  })),
                  activities: activities.map((activity: any) => ({
                    type: activity.activityType,
                    action: activity.activityDescription,
                    details: activity.activityDescription,
                    timestamp: activity.createdAt
                  }))
                };

                const aiResult = await aiService.generateConclusionRemark(context);

                // Check if conclusion already exists (e.g., from previous final approval before additional approver was added)
                const existingConclusion = await ConclusionRemark.findOne({
                  where: { requestId: level.requestId }
                });

                if (existingConclusion) {
                  // Update existing conclusion with new AI-generated remark (regenerated with updated context)
                  await existingConclusion.update({
                    aiGeneratedRemark: aiResult.remark,
                    aiModelUsed: aiResult.provider,
                    aiConfidenceScore: aiResult.confidence,
                    // Preserve finalRemark if it was already finalized
                    // Only reset if it wasn't finalized yet
                    finalRemark: (existingConclusion as any).finalizedAt ? (existingConclusion as any).finalRemark : null,
                    editedBy: null,
                    isEdited: false,
                    editCount: 0,
                    approvalSummary: {
                      totalLevels: approvalLevels.length,
                      approvedLevels: approvalLevels.filter((l: any) => l.status === 'APPROVED').length,
                      averageTatUsage: approvalLevels.reduce((sum: number, l: any) =>
                        sum + Number(l.tatPercentageUsed || 0), 0) / (approvalLevels.length || 1)
                    },
                    documentSummary: {
                      totalDocuments: documents.length,
                      documentNames: documents.map((d: any) => d.originalFileName || d.fileName)
                    },
                    keyDiscussionPoints: aiResult.keyPoints,
                    generatedAt: new Date(),
                    // Preserve finalizedAt if it was already finalized
                    finalizedAt: (existingConclusion as any).finalizedAt || null
                  } as any);
                  logger.info(`[Approval] Updated existing AI conclusion for request ${level.requestId} with regenerated content (includes new approver)`);
                } else {
                  // Create new conclusion
                  await ConclusionRemark.create({
                    requestId: level.requestId,
                    aiGeneratedRemark: aiResult.remark,
                    aiModelUsed: aiResult.provider,
                    aiConfidenceScore: aiResult.confidence,
                    finalRemark: null,
                    editedBy: null,
                    isEdited: false,
                    editCount: 0,
                    approvalSummary: {
                      totalLevels: approvalLevels.length,
                      approvedLevels: approvalLevels.filter((l: any) => l.status === 'APPROVED').length,
                      averageTatUsage: approvalLevels.reduce((sum: number, l: any) =>
                        sum + Number(l.tatPercentageUsed || 0), 0) / (approvalLevels.length || 1)
                    },
                    documentSummary: {
                      totalDocuments: documents.length,
                      documentNames: documents.map((d: any) => d.originalFileName || d.fileName)
                    },
                    keyDiscussionPoints: aiResult.keyPoints,
                    generatedAt: new Date(),
                    finalizedAt: null
                  } as any);
                }

                logAIEvent('response', {
                  requestId: level.requestId,
                  action: 'conclusion_generation_completed',
                });

                // Log activity
                activityService.log({
                  requestId: level.requestId,
                  type: 'ai_conclusion_generated',
                  user: { userId: null as any, name: 'System' }, // Use null instead of 'system' for UUID field
                  timestamp: new Date().toISOString(),
                  action: 'AI Conclusion Generated',
                  details: 'AI-powered conclusion remark generated for review by initiator',
                  ipAddress: undefined, // System-generated, no IP
                  userAgent: undefined  // System-generated, no user agent
                });
              } else {
                // Log why AI generation was skipped
                if (!aiEnabled) {
                  logger.info(`[Approval] AI features disabled in admin config, skipping conclusion generation for ${level.requestId}`);
                } else if (!remarkGenerationEnabled) {
                  logger.info(`[Approval] AI remark generation disabled in admin config, skipping for ${level.requestId}`);
                } else if (!aiService.isAvailable()) {
                  logger.warn(`[Approval] AI service unavailable for ${level.requestId}, skipping conclusion generation`);
                }
              }

              // Auto-generate RequestSummary after final approval (system-level generation)
              // This makes the summary immediately available when user views the approved request
              try {
                const { summaryService } = await import('./summary.service');
                const summary = await summaryService.createSummary(level.requestId, 'system', {
                  isSystemGeneration: true
                });
                logger.info(`[Approval] ✅ Auto-generated summary ${(summary as any).summaryId} for approved request ${level.requestId}`);

                // Log summary generation activity
                activityService.log({
                  requestId: level.requestId,
                  type: 'summary_generated',
                  user: { userId: null as any, name: 'System' }, // Use null instead of 'system' for UUID field
                  timestamp: new Date().toISOString(),
                  action: 'Summary Auto-Generated',
                  details: 'Request summary auto-generated after final approval',
                  ipAddress: undefined,
                  userAgent: undefined
                });
              } catch (summaryError: any) {
                // Log but don't fail - initiator can regenerate later
                logger.error(`[Approval] Failed to auto-generate summary for ${level.requestId}:`, summaryError.message);
              }

            } catch (aiError) {
              logAIEvent('error', {
                requestId: level.requestId,
                action: 'conclusion_generation_failed',
                error: aiError,
              });
              // Silent failure - initiator can write manually

              // Still try to generate summary even if AI conclusion failed
              try {
                const { summaryService } = await import('./summary.service');
                const summary = await summaryService.createSummary(level.requestId, 'system', {
                  isSystemGeneration: true
                });
                logger.info(`[Approval] ✅ Auto-generated summary ${(summary as any).summaryId} for approved request ${level.requestId} (without AI conclusion)`);
              } catch (summaryError: any) {
                logger.error(`[Approval] Failed to auto-generate summary for ${level.requestId}:`, summaryError.message);
              }
            }
          })().catch(err => {
            // Catch any unhandled promise rejections
            logger.error(`[Approval] Unhandled error in background AI generation:`, err);
          });

          // Notify initiator and all participants (including spectators) about approval
          // Spectators are CC'd for transparency, similar to email CC
          if (wf) {
            const participants = await Participant.findAll({
              where: { requestId: level.requestId }
            });
            const targetUserIds = new Set<string>();
            targetUserIds.add((wf as any).initiatorId);
            for (const p of participants as any[]) {
              targetUserIds.add(p.userId); // Includes spectators
            }

            // Send notification to initiator about final approval (triggers email)
            const initiatorId = (wf as any).initiatorId;
            await notificationService.sendToUsers([initiatorId], {
              title: `Request Approved - All Approvals Complete`,
              body: `Your request "${(wf as any).title}" has been fully approved by all approvers. Please review and finalize the conclusion remark to close the request.`,
              requestNumber: (wf as any).requestNumber,
              requestId: level.requestId,
              url: `/request/${(wf as any).requestNumber}`,
              type: 'approval',
              priority: 'HIGH',
              actionRequired: true
            });

            // Send notification to all participants/spectators (for transparency, no action required)
            const participantUserIds = Array.from(targetUserIds).filter(id => id !== initiatorId);
            if (participantUserIds.length > 0) {
              await notificationService.sendToUsers(participantUserIds, {
                title: `Request Approved`,
                body: `Request "${(wf as any).title}" has been fully approved. The initiator will finalize the conclusion remark to close the request.`,
                requestNumber: (wf as any).requestNumber,
                requestId: level.requestId,
                url: `/request/${(wf as any).requestNumber}`,
                type: 'approval_pending_closure',
                priority: 'MEDIUM',
                actionRequired: false
              });
            }

            logger.info(`[Approval] ✅ Final approval complete for ${level.requestId}. Initiator and ${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(`[Approval] 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
          // Custom workflows use strict sequential ordering (levelNumber + 1) to maintain intended order
          // This ensures custom workflows work predictably and don't skip levels
          const currentLevelNumber = level.levelNumber || 0;
          logger.info(`[Approval] Finding next level after level ${currentLevelNumber} for request ${level.requestId} (Custom workflow)`);

          // Use strict sequential approach for custom workflows
          const nextLevel = await ApprovalLevel.findOne({
            where: {
              requestId: level.requestId,
              levelNumber: currentLevelNumber + 1
            }
          });

          if (!nextLevel) {
            logger.info(`[Approval] Sequential level ${currentLevelNumber + 1} not found for custom workflow - this may be the final approval`);
          } else if (nextLevel.status !== ApprovalStatus.PENDING) {
            // Sequential level exists but not PENDING - log warning but proceed
            logger.warn(`[Approval] Sequential level ${currentLevelNumber + 1} exists but status is ${nextLevel.status}, expected PENDING. Proceeding with sequential level to maintain workflow order.`);
          }

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

          if (nextLevel) {
            logger.info(`[Approval] Found next level: ${nextLevelNumber} (${(nextLevel as any).levelName || 'unnamed'}), approver: ${(nextLevel as any).approverName || (nextLevel as any).approverEmail || 'unknown'}, status: ${nextLevel.status}`);
          } else {
            logger.info(`[Approval] No next level found after level ${currentLevelNumber} - this may be the final approval`);
          }

          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(`[Approval] Cannot activate next level ${nextLevelNumber} - 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 {
              // Get workflow priority for TAT calculation
              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  // Pass workflow priority (EXPRESS = 24/7, STANDARD = working hours)
              );
              logger.info(`[Approval] TAT jobs scheduled for next level ${nextLevelNumber} (Priority: ${workflowPriority})`);
            } catch (tatError) {
              logger.error(`[Approval] Failed to schedule TAT jobs for next level:`, tatError);
              // Don't fail the approval if TAT scheduling fails
            }

            // Update workflow current level (only if nextLevelNumber is not null)
            if (nextLevelNumber !== null) {
              await WorkflowRequest.update(
                { currentLevel: nextLevelNumber },
                { where: { requestId: level.requestId } }
              );
              logger.info(`Approved level ${level.levelNumber}. Activated next level ${nextLevelNumber} for workflow ${level.requestId}`);
            } else {
              logger.warn(`Approved level ${level.levelNumber} but no next level found - workflow may be complete`);
            }

            // Note: Dealer claim-specific logic (Activity Creation, E-Invoice) is handled by DealerClaimApprovalService
            // This service is for custom workflows only

            // 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 (triggers email for regular workflows)
            if (wf) {
              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'
              });
            }

            // Notify next approver
            if (wf && nextLevel) {
              // Check if it's an auto-step by checking approverEmail or levelName
              // Note: Activity Creation, E-Invoice Generation, and Credit Note Confirmation are now activity logs only, not approval steps
              // These steps are processed automatically and should NOT trigger notifications
              const isAutoStep = (nextLevel as any).approverEmail === `system@${appDomain}`
                || (nextLevel as any).approverName === 'System Auto-Process'
                || (nextLevel as any).approverId === 'system';

              // IMPORTANT: Skip notifications and assignment logging for system/auto-steps
              // System steps are any step with system@${appDomain}
              // Only send notifications to real users, NOT system processes
              if (!isAutoStep && (nextLevel as any).approverId && (nextLevel as any).approverId !== 'system') {
                // Additional checks: ensure approverEmail and approverName are not system-related
                // This prevents notifications to system accounts even if they pass other checks
                const approverEmail = (nextLevel as any).approverEmail || '';
                const approverName = (nextLevel as any).approverName || '';
                const isSystemEmail = approverEmail.toLowerCase() === `system@${appDomain}`
                  || approverEmail.toLowerCase().includes('system');
                const isSystemName = approverName.toLowerCase() === 'system auto-process'
                  || approverName.toLowerCase().includes('system');

                // EXCLUDE all system-related steps from notifications
                // Only send notifications to real users, NOT system processes
                if (!isSystemEmail && !isSystemName) {
                  // Send notification to next approver (only for real users, not system processes)
                  // This will send both in-app and email notifications
                  const nextApproverId = (nextLevel as any).approverId;
                  const nextApproverName = (nextLevel as any).approverName || (nextLevel as any).approverEmail || 'approver';

                  logger.info(`[Approval] 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(`[Approval] Assignment notification sent successfully to ${nextApproverName} for level ${nextLevelNumber}`);

                  // Log assignment activity for the next approver
                  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
                  });
                } else {
                  logger.info(`[Approval] Skipping notification for system process: ${approverEmail} at level ${nextLevelNumber}`);
                }
              } else {
                logger.info(`[Approval] Skipping notification for auto-step at level ${nextLevelNumber}`);
              }

              // Note: Dealer-specific notifications (proposal/completion submissions) are handled by DealerClaimApprovalService
            }
          } else {
            // No next level found but not final approver - this shouldn't happen
            logger.warn(`No next level found for workflow ${level.requestId} after approving level ${level.levelNumber}`);
            // Use current level number since there's no next level (workflow is complete)
            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,
                url: `/request/${(wf as any).requestNumber}`
              });
              activityService.log({
                requestId: level.requestId,
                type: 'approval',
                user: { userId: level.approverId, name: level.approverName },
                timestamp: new Date().toISOString(),
                action: 'Approved',
                details: `Request approved and finalized by ${level.approverName || level.approverEmail}`,
                ipAddress: requestMetadata?.ipAddress || undefined,
                userAgent: requestMetadata?.userAgent || undefined
              });
            }
          }
        }
      } else if (action.action === 'REJECT') {
        // Rejection - mark workflow as REJECTED (closure will happen when initiator finalizes conclusion)
        await WorkflowRequest.update(
          {
            status: WorkflowStatus.REJECTED
            // Note: closureDate will be set when initiator finalizes the conclusion
          },
          { where: { requestId: level.requestId } }
        );

        // Mark all pending levels as skipped
        await ApprovalLevel.update(
          {
            status: ApprovalStatus.SKIPPED,
            levelEndTime: now
          },
          {
            where: {
              requestId: level.requestId,
              status: ApprovalStatus.PENDING,
              levelNumber: { [Op.gt]: level.levelNumber }
            }
          }
        );

        logWorkflowEvent('rejected', level.requestId, {
          level: level.levelNumber,
          status: 'REJECTED',
          message: 'Awaiting closure from initiator',
        });

        // Log rejection activity first (so it's included in AI context)
        if (wf) {
          activityService.log({
            requestId: level.requestId,
            type: 'rejection',
            user: { userId: level.approverId, name: level.approverName },
            timestamp: new Date().toISOString(),
            action: 'Rejected',
            details: `Request rejected by ${level.approverName || level.approverEmail}. Reason: ${action.rejectionReason || action.comments || 'No reason provided'}. Awaiting closure from initiator.`,
            ipAddress: requestMetadata?.ipAddress || undefined,
            userAgent: requestMetadata?.userAgent || undefined
          });
        }

        // Notify initiator and all participants
        if (wf) {
          const participants = await Participant.findAll({ where: { requestId: level.requestId } });
          const targetUserIds = new Set<string>();
          targetUserIds.add((wf as any).initiatorId);
          for (const p of participants as any[]) {
            targetUserIds.add(p.userId);
          }

          // Send notification to initiator with type 'rejection' to trigger email
          await notificationService.sendToUsers([(wf as any).initiatorId], {
            title: `Rejected: ${(wf as any).requestNumber}`,
            body: `${(wf as any).title}`,
            requestNumber: (wf as any).requestNumber,
            requestId: level.requestId,
            url: `/request/${(wf as any).requestNumber}`,
            type: 'rejection',
            priority: 'HIGH',
            metadata: {
              rejectionReason: action.rejectionReason || action.comments || 'No reason provided'
            }
          });

          // Send notification to other participants (spectators) for transparency (no email, just in-app)
          const participantUserIds = Array.from(targetUserIds).filter(id => id !== (wf as any).initiatorId);
          if (participantUserIds.length > 0) {
            await notificationService.sendToUsers(participantUserIds, {
              title: `Rejected: ${(wf as any).requestNumber}`,
              body: `Request "${(wf as any).title}" has been rejected.`,
              requestNumber: (wf as any).requestNumber,
              requestId: level.requestId,
              url: `/request/${(wf as any).requestNumber}`,
              type: 'status_change', // Use status_change to avoid triggering emails for participants
              priority: 'MEDIUM'
            });
          }
        }

        // Generate AI conclusion remark ASYNCHRONOUSLY for rejected requests (similar to approved)
        // This runs in the background without blocking the rejection response
        (async () => {
          try {
            const { aiService } = await import('./ai.service');
            const { ConclusionRemark } = await import('@models/index');
            const { ApprovalLevel } = await import('@models/ApprovalLevel');
            const { WorkNote } = await import('@models/WorkNote');
            const { Document } = await import('@models/Document');
            const { Activity } = await import('@models/Activity');
            const { getConfigValue } = await import('./configReader.service');

            // Check if AI features and remark generation are enabled in admin config
            const aiEnabled = (await getConfigValue('AI_ENABLED', 'true'))?.toLowerCase() === 'true';
            const remarkGenerationEnabled = (await getConfigValue('AI_REMARK_GENERATION_ENABLED', 'true'))?.toLowerCase() === 'true';

            if (!aiEnabled || !remarkGenerationEnabled) {
              logger.info(`[Approval] AI conclusion generation skipped for rejected request ${level.requestId} (AI disabled)`);
              return;
            }

            // Check if AI service is available
            const { aiService: aiSvc } = await import('./ai.service');
            if (!aiSvc.isAvailable()) {
              logger.warn(`[Approval] AI service unavailable for rejected request ${level.requestId}`);
              return;
            }

            // Gather context for AI generation (similar to approved flow)
            const approvalLevels = await ApprovalLevel.findAll({
              where: { requestId: level.requestId },
              order: [['levelNumber', 'ASC']]
            });

            const workNotes = await WorkNote.findAll({
              where: { requestId: level.requestId },
              order: [['createdAt', 'ASC']],
              limit: 20
            });

            const documents = await Document.findAll({
              where: { requestId: level.requestId },
              order: [['uploadedAt', 'DESC']]
            });

            const activities = await Activity.findAll({
              where: { requestId: level.requestId },
              order: [['createdAt', 'ASC']],
              limit: 50
            });

            // Build context object (include rejection reason)
            const context = {
              requestTitle: (wf as any).title,
              requestDescription: (wf as any).description,
              requestNumber: (wf as any).requestNumber,
              priority: (wf as any).priority,
              rejectionReason: action.rejectionReason || action.comments || 'No reason provided',
              rejectedBy: level.approverName || level.approverEmail,
              approvalFlow: approvalLevels.map((l: any) => {
                const tatPercentage = l.tatPercentageUsed !== undefined && l.tatPercentageUsed !== null
                  ? Number(l.tatPercentageUsed)
                  : (l.elapsedHours && l.tatHours ? (Number(l.elapsedHours) / Number(l.tatHours)) * 100 : 0);
                return {
                  levelNumber: l.levelNumber,
                  approverName: l.approverName,
                  status: l.status,
                  comments: l.comments,
                  actionDate: l.actionDate,
                  tatHours: Number(l.tatHours || 0),
                  elapsedHours: Number(l.elapsedHours || 0),
                  tatPercentageUsed: tatPercentage
                };
              }),
              workNotes: workNotes.map((note: any) => ({
                userName: note.userName,
                message: note.message,
                createdAt: note.createdAt
              })),
              documents: documents.map((doc: any) => ({
                fileName: doc.originalFileName || doc.fileName,
                uploadedBy: doc.uploadedBy,
                uploadedAt: doc.uploadedAt
              })),
              activities: activities.map((activity: any) => ({
                type: activity.activityType,
                action: activity.activityDescription,
                details: activity.activityDescription,
                timestamp: activity.createdAt
              }))
            };

            logger.info(`[Approval] Generating AI conclusion for rejected request ${level.requestId}...`);

            // Generate AI conclusion (will adapt to rejection context)
            const aiResult = await aiSvc.generateConclusionRemark(context);

            // Create or update conclusion remark
            let conclusionInstance = await ConclusionRemark.findOne({ where: { requestId: level.requestId } });

            const conclusionData = {
              aiGeneratedRemark: aiResult.remark,
              aiModelUsed: aiResult.provider,
              aiConfidenceScore: aiResult.confidence,
              approvalSummary: {
                totalLevels: approvalLevels.length,
                rejectedLevel: level.levelNumber,
                rejectedBy: level.approverName || level.approverEmail,
                rejectionReason: action.rejectionReason || action.comments
              },
              documentSummary: {
                totalDocuments: documents.length,
                documentNames: documents.map((d: any) => d.originalFileName || d.fileName)
              },
              keyDiscussionPoints: aiResult.keyPoints,
              generatedAt: new Date()
            };

            if (conclusionInstance) {
              await conclusionInstance.update(conclusionData as any);
              logger.info(`[Approval] ✅ AI conclusion updated for rejected request ${level.requestId}`);
            } else {
              await ConclusionRemark.create({
                requestId: level.requestId,
                ...conclusionData,
                finalRemark: null,
                editedBy: null,
                isEdited: false,
                editCount: 0,
                finalizedAt: null
              } as any);
              logger.info(`[Approval] ✅ AI conclusion generated for rejected request ${level.requestId}`);
            }
          } catch (error: any) {
            logger.error(`[Approval] Failed to generate AI conclusion for rejected request ${level.requestId}:`, error);
            // Don't fail the rejection if AI generation fails
          }
        })();
      }

      logger.info(`Approval level ${levelId} ${action.action.toLowerCase()}ed`);

      // 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()
      });

      return updatedLevel;
    } catch (error) {
      logger.error(`Failed to ${action.action.toLowerCase()} level ${levelId}:`, error);
      throw new Error(`Failed to ${action.action.toLowerCase()} level`);
    }
  }

  async getCurrentApprovalLevel(requestId: string): Promise<ApprovalLevel | null> {
    try {
      return await ApprovalLevel.findOne({
        where: { requestId, status: ApprovalStatus.PENDING },
        order: [['levelNumber', 'ASC']]
      });
    } catch (error) {
      logger.error(`Failed to get current approval level for ${requestId}:`, error);
      throw new Error('Failed to get current approval level');
    }
  }

  async getApprovalLevels(requestId: string): Promise<ApprovalLevel[]> {
    try {
      return await ApprovalLevel.findAll({
        where: { requestId },
        order: [['levelNumber', 'ASC']]
      });
    } catch (error) {
      logger.error(`Failed to get approval levels for ${requestId}:`, error);
      throw new Error('Failed to get approval levels');
    }
  }
}
