import { tatQueue } from '../queues/tatQueue';
import { calculateDelay, addWorkingHours, addWorkingHoursExpress } from '@utils/tatTimeUtils';
import { getTatThresholds } from './configReader.service';
import dayjs from 'dayjs';
import logger from '@utils/logger';
import { Priority } from '../types/common.types';

export class TatSchedulerService {
  /**
   * Schedule TAT notification jobs for an approval level
   * @param requestId - The workflow request ID
   * @param levelId - The approval level ID
   * @param approverId - The approver user ID
   * @param tatDurationHours - TAT duration in hours
   * @param startTime - Optional start time (defaults to now)
   * @param priority - Request priority (EXPRESS = 24/7, STANDARD = working hours only)
   */
  async scheduleTatJobs(
    requestId: string,
    levelId: string,
    approverId: string,
    tatDurationHours: number,
    startTime?: Date,
    priority: Priority = Priority.STANDARD
  ): Promise<void> {
    try {
      // Check if tatQueue is available
      if (!tatQueue) {
        logger.warn(`[TAT Scheduler] TAT queue not available (Redis not connected). Skipping TAT job scheduling.`);
        return;
      }

      const now = startTime || new Date();
      const isExpress = priority === Priority.EXPRESS;
      
      // Get current thresholds from database configuration
      const thresholds = await getTatThresholds();
      
      // Calculate milestone times using configured thresholds
      // EXPRESS mode: 24/7 calculation (includes holidays, weekends, non-working hours)
      // STANDARD mode: Working hours only (excludes holidays, weekends, non-working hours)
      let threshold1Time: Date;
      let threshold2Time: Date;
      let breachTime: Date;
      
      if (isExpress) {
        // EXPRESS: All calendar days (Mon-Sun, including weekends/holidays) but working hours only (9 AM - 6 PM)
        const t1 = await addWorkingHoursExpress(now, tatDurationHours * (thresholds.first / 100));
        const t2 = await addWorkingHoursExpress(now, tatDurationHours * (thresholds.second / 100));
        const tBreach = await addWorkingHoursExpress(now, tatDurationHours);
        threshold1Time = t1.toDate();
        threshold2Time = t2.toDate();
        breachTime = tBreach.toDate();
        logger.info(`[TAT Scheduler] Using EXPRESS mode - all days, working hours only (9 AM - 6 PM)`);
      } else {
        // STANDARD: Working days only (Mon-Fri), working hours (9 AM - 6 PM), excludes holidays
        const t1 = await addWorkingHours(now, tatDurationHours * (thresholds.first / 100));
        const t2 = await addWorkingHours(now, tatDurationHours * (thresholds.second / 100));
        const tBreach = await addWorkingHours(now, tatDurationHours);
        threshold1Time = t1.toDate();
        threshold2Time = t2.toDate();
        breachTime = tBreach.toDate();
        logger.info(`[TAT Scheduler] Using STANDARD mode - weekdays only, working hours (9 AM - 6 PM), excludes holidays`);
      }

      logger.info(`[TAT Scheduler] Calculating TAT milestones for request ${requestId}, level ${levelId}`);
      logger.info(`[TAT Scheduler] Priority: ${priority}, TAT Hours: ${tatDurationHours}`);
      logger.info(`[TAT Scheduler] Start: ${dayjs(now).format('YYYY-MM-DD HH:mm')}`);
      logger.info(`[TAT Scheduler] Threshold 1 (${thresholds.first}%): ${dayjs(threshold1Time).format('YYYY-MM-DD HH:mm')}`);
      logger.info(`[TAT Scheduler] Threshold 2 (${thresholds.second}%): ${dayjs(threshold2Time).format('YYYY-MM-DD HH:mm')}`);
      logger.info(`[TAT Scheduler] Breach (100%): ${dayjs(breachTime).format('YYYY-MM-DD HH:mm')}`);

      const jobs = [
        {
          type: 'threshold1' as const,
          threshold: thresholds.first,
          delay: calculateDelay(threshold1Time),
          targetTime: threshold1Time
        },
        {
          type: 'threshold2' as const,
          threshold: thresholds.second,
          delay: calculateDelay(threshold2Time),
          targetTime: threshold2Time
        },
        {
          type: 'breach' as const,
          threshold: 100,
          delay: calculateDelay(breachTime),
          targetTime: breachTime
        }
      ];


      // Check if test mode enabled (1 hour = 1 minute)
      const isTestMode = process.env.TAT_TEST_MODE === 'true';
      
      // Check if times collide (working hours calculation issue)
      const uniqueTimes = new Set(jobs.map(j => j.targetTime.getTime()));
      const hasCollision = uniqueTimes.size < jobs.length;
      
      let jobIndex = 0;
      for (const job of jobs) {
        if (job.delay < 0) {
          logger.error(`[TAT Scheduler] Skipping ${job.type} - time in past`);
          continue;
        }

        let spacedDelay: number;
        
        if (isTestMode) {
          // Test mode: times are already in minutes (tatTimeUtils converts hours to minutes)
          // Just ensure they have minimum spacing for BullMQ reliability
          spacedDelay = Math.max(job.delay, 5000) + (jobIndex * 5000);
        } else if (hasCollision) {
          // Production with collision: add 5-minute spacing
          spacedDelay = job.delay + (jobIndex * 300000);
        } else {
          // Production without collision: use calculated delays
          spacedDelay = job.delay;
        }
        
        const jobId = `tat-${job.type}-${requestId}-${levelId}`;
        
        await tatQueue.add(
          job.type,
          {
            type: job.type,
            threshold: job.threshold,
            requestId,
            levelId,
            approverId
          },
          {
            delay: spacedDelay,
            jobId: jobId,
            removeOnComplete: {
              age: 3600, // Keep for 1 hour for debugging
              count: 1000
            },
            removeOnFail: false
          }
        );

        logger.info(`[TAT Scheduler] Scheduled ${job.type} (${job.threshold}%)`);
        jobIndex++;
      }

      logger.info(`[TAT Scheduler] TAT jobs scheduled for request ${requestId}`);
    } catch (error) {
      logger.error(`[TAT Scheduler] Failed to schedule TAT jobs:`, error);
      throw error;
    }
  }

  /**
   * Cancel TAT jobs for a specific approval level
   * Useful when an approver acts before TAT expires
   * @param requestId - The workflow request ID
   * @param levelId - The approval level ID
   */
  async cancelTatJobs(requestId: string, levelId: string): Promise<void> {
    try {
      // Check if tatQueue is available
      if (!tatQueue) {
        logger.warn(`[TAT Scheduler] TAT queue not available. Skipping job cancellation.`);
        return;
      }

      // Use generic job names that don't depend on threshold percentages
      const jobIds = [
        `tat-threshold1-${requestId}-${levelId}`,
        `tat-threshold2-${requestId}-${levelId}`,
        `tat-breach-${requestId}-${levelId}`
      ];

      for (const jobId of jobIds) {
        try {
          const job = await tatQueue.getJob(jobId);
          if (job) {
            await job.remove();
            logger.info(`[TAT Scheduler] Cancelled job ${jobId}`);
          }
        } catch (error) {
          // Job might not exist, which is fine
          logger.debug(`[TAT Scheduler] Job ${jobId} not found (may have already been processed)`);
        }
      }

      logger.info(`[TAT Scheduler] ✅ TAT jobs cancelled for level ${levelId}`);
    } catch (error) {
      logger.error(`[TAT Scheduler] Failed to cancel TAT jobs:`, error);
      // Don't throw - cancellation failure shouldn't break the workflow
    }
  }

  /**
   * Cancel all TAT jobs for a workflow request
   * @param requestId - The workflow request ID
   */
  async cancelAllTatJobsForRequest(requestId: string): Promise<void> {
    try {
      // Check if tatQueue is available
      if (!tatQueue) {
        logger.warn(`[TAT Scheduler] TAT queue not available. Skipping job cancellation.`);
        return;
      }

      const jobs = await tatQueue.getJobs(['delayed', 'waiting']);
      const requestJobs = jobs.filter(job => job.data.requestId === requestId);

      for (const job of requestJobs) {
        await job.remove();
        logger.info(`[TAT Scheduler] Cancelled job ${job.id}`);
      }

      logger.info(`[TAT Scheduler] ✅ All TAT jobs cancelled for request ${requestId}`);
    } catch (error) {
      logger.error(`[TAT Scheduler] Failed to cancel all TAT jobs:`, error);
      // Don't throw - cancellation failure shouldn't break the workflow
    }
  }
}

export const tatSchedulerService = new TatSchedulerService();

