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 from '@utils/logger';
import { Op } from 'sequelize';
import { notificationService } from './notification.service';
import { activityService } from './activity.service';
import { tatSchedulerService } from './tatScheduler.service';

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);
      const priority = ((wf as any)?.priority || 'standard').toString().toLowerCase();

      const now = new Date();
      // Calculate elapsed hours using working hours logic (matches frontend)
      const elapsedHours = await calculateElapsedWorkingHours(level.levelStartTime || level.createdAt, now, priority);
      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') {
        if (level.isFinalApprover) {
          // Final approver - close workflow as APPROVED
          await WorkflowRequest.update(
            { 
              status: WorkflowStatus.APPROVED, 
              closureDate: now,
              currentLevel: (level.levelNumber || 0) + 1
            },
            { where: { requestId: level.requestId } }
          );
          logger.info(`Final approver approved. Workflow ${level.requestId} closed as APPROVED`);
          
          // 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()) {
                logger.info(`[Approval] 🔄 Starting background AI conclusion generation for ${level.requestId}...`);
                
                // 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);
                
                // Save to database
                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);
                
                logger.info(`[Approval] ✅ Background AI conclusion completed for ${level.requestId}`);
                
                // Log activity
                activityService.log({
                  requestId: level.requestId,
                  type: 'ai_conclusion_generated',
                  user: { userId: 'system', name: 'System' },
                  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`);
                }
              }
            } catch (aiError) {
              logger.error(`[Approval] Background AI generation failed for ${level.requestId}:`, aiError);
              // Silent failure - initiator can write manually
            }
          })().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 (with action required)
            const initiatorId = (wf as any).initiatorId;
            await notificationService.sendToUsers([initiatorId], {
              title: `Request Approved - Closure Pending`,
              body: `Your request "${(wf as any).title}" has been fully approved. 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_pending_closure',
              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
          const nextLevelNumber = (level.levelNumber || 0) + 1;
          const nextLevel = await ApprovalLevel.findOne({
            where: {
              requestId: level.requestId,
              levelNumber: nextLevelNumber
            }
          });

          if (nextLevel) {
            // 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
            await WorkflowRequest.update(
              { currentLevel: nextLevelNumber },
              { where: { requestId: level.requestId } }
            );
            
            logger.info(`Approved level ${level.levelNumber}. Activated next level ${nextLevelNumber} for workflow ${level.requestId}`);
            // Notify next approver
            if (wf && nextLevel) {
              await notificationService.sendToUsers([ (nextLevel as any).approverId ], {
                title: `Action required: ${(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 forwarded to ${(nextLevel as any).approverName || (nextLevel as any).approverEmail} by ${level.approverName || level.approverEmail}`,
                ipAddress: requestMetadata?.ipAddress || undefined,
                userAgent: requestMetadata?.userAgent || undefined
              });
            }
          } 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}`);
            await WorkflowRequest.update(
              { 
                status: WorkflowStatus.APPROVED, 
                closureDate: now,
                currentLevel: nextLevelNumber
              },
              { 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 } 
            } 
          }
        );

        logger.info(`Level ${level.levelNumber} rejected. Workflow ${level.requestId} marked as REJECTED. 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);
          }
          await notificationService.sendToUsers(Array.from(targetUserIds), {
            title: `Rejected: ${(wf as any).requestNumber}`,
            body: `${(wf as any).title}`,
            requestNumber: (wf as any).requestNumber,
            url: `/request/${(wf as any).requestNumber}`
          });
        }
        
        // 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`);
      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');
    }
  }
}
