import { Injectable, Logger, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { SignalService } from '../signal/signal.service';
import { EventStatus } from '@prisma/client';

/**
 * Automatic event status management service
 * - Transitions SCHEDULED → IN_PROGRESS when startTime arrives
 * - Sends Signal confirmation requests to drivers
 * - Handles driver responses (1=Confirmed, 2=Delayed, 3=Issue)
 * - Transitions IN_PROGRESS → COMPLETED when endTime passes (with grace period)
 */
@Injectable()
export class EventStatusService implements OnModuleInit, OnModuleDestroy {
  private readonly logger = new Logger(EventStatusService.name);
  private intervalId: NodeJS.Timeout | null = null;
  private readonly CHECK_INTERVAL = 60 * 1000; // Check every minute
  private readonly COMPLETION_GRACE_PERIOD = 15 * 60 * 1000; // 15 min after endTime before auto-complete

  constructor(
    private prisma: PrismaService,
    private signalService: SignalService,
  ) {}

  onModuleInit() {
    this.logger.log('Starting event status monitoring...');
    this.startMonitoring();
  }

  onModuleDestroy() {
    this.stopMonitoring();
  }

  private startMonitoring() {
    // Run immediately on start
    this.checkAndUpdateStatuses();

    // Then run every minute
    this.intervalId = setInterval(() => {
      this.checkAndUpdateStatuses();
    }, this.CHECK_INTERVAL);
  }

  private stopMonitoring() {
    if (this.intervalId) {
      clearInterval(this.intervalId);
      this.intervalId = null;
      this.logger.log('Stopped event status monitoring');
    }
  }

  /**
   * Main check loop - finds events that need status updates
   */
  private async checkAndUpdateStatuses() {
    try {
      const now = new Date();

      // 1. Send reminders for upcoming events (20 min and 5 min before)
      await this.sendUpcomingReminders(now);

      // 2. Find SCHEDULED events that should now be IN_PROGRESS
      await this.transitionToInProgress(now);

      // 3. Find IN_PROGRESS events that are past their end time (with grace period)
      await this.transitionToCompleted(now);

    } catch (error) {
      this.logger.error('Error checking event statuses:', error);
    }
  }

  /**
   * Send 20-minute and 5-minute reminders to drivers
   */
  private async sendUpcomingReminders(now: Date) {
    const twentyMinutesFromNow = new Date(now.getTime() + 20 * 60 * 1000);
    const fiveMinutesFromNow = new Date(now.getTime() + 5 * 60 * 1000);

    // Find events needing 20-minute reminder
    // Events starting within 20 minutes that haven't had reminder sent
    const eventsFor20MinReminder = await this.prisma.scheduleEvent.findMany({
      where: {
        status: EventStatus.SCHEDULED,
        type: 'TRANSPORT',
        startTime: { lte: twentyMinutesFromNow, gt: now },
        reminder20MinSent: false,
        driverId: { not: null },
        deletedAt: null,
      },
      include: {
        driver: true,
        vehicle: true,
      },
    });

    for (const event of eventsFor20MinReminder) {
      // Only send if actually ~20 min away (between 15-25 min)
      const minutesUntil = Math.floor((new Date(event.startTime).getTime() - now.getTime()) / 60000);
      if (minutesUntil <= 25 && minutesUntil >= 15) {
        await this.send20MinReminder(event, minutesUntil);
      }
    }

    // Find events needing 5-minute reminder
    const eventsFor5MinReminder = await this.prisma.scheduleEvent.findMany({
      where: {
        status: EventStatus.SCHEDULED,
        type: 'TRANSPORT',
        startTime: { lte: fiveMinutesFromNow, gt: now },
        reminder5MinSent: false,
        driverId: { not: null },
        deletedAt: null,
      },
      include: {
        driver: true,
        vehicle: true,
      },
    });

    for (const event of eventsFor5MinReminder) {
      // Only send if actually ~5 min away (between 3-10 min)
      const minutesUntil = Math.floor((new Date(event.startTime).getTime() - now.getTime()) / 60000);
      if (minutesUntil <= 10 && minutesUntil >= 3) {
        await this.send5MinReminder(event, minutesUntil);
      }
    }
  }

  /**
   * Send 20-minute reminder to driver
   */
  private async send20MinReminder(event: any, minutesUntil: number) {
    try {
      const linkedNumber = await this.signalService.getLinkedNumber();
      if (!linkedNumber || !event.driver?.phone) return;

      // Get VIP names
      const vips = await this.prisma.vIP.findMany({
        where: { id: { in: event.vipIds || [] } },
        select: { name: true },
      });
      const vipNames = vips.map(v => v.name).join(', ') || 'VIP';

      const message = `📢 UPCOMING TRIP in ~${minutesUntil} minutes

📍 Pickup: ${event.pickupLocation || 'See schedule'}
📍 Dropoff: ${event.dropoffLocation || 'See schedule'}
👤 VIP: ${vipNames}
🚐 Vehicle: ${event.vehicle?.name || 'Check assignment'}
⏰ Start Time: ${new Date(event.startTime).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })}

Please head to the pickup location.`;

      const formattedPhone = this.signalService.formatPhoneNumber(event.driver.phone);
      await this.signalService.sendMessage(linkedNumber, formattedPhone, message);

      // Mark reminder as sent
      await this.prisma.scheduleEvent.update({
        where: { id: event.id },
        data: { reminder20MinSent: true },
      });

      this.logger.log(`Sent 20-min reminder to ${event.driver.name} for event ${event.id}`);
    } catch (error) {
      this.logger.error(`Failed to send 20-min reminder for event ${event.id}:`, error);
    }
  }

  /**
   * Send 5-minute reminder to driver (more urgent)
   */
  private async send5MinReminder(event: any, minutesUntil: number) {
    try {
      const linkedNumber = await this.signalService.getLinkedNumber();
      if (!linkedNumber || !event.driver?.phone) return;

      // Get VIP names
      const vips = await this.prisma.vIP.findMany({
        where: { id: { in: event.vipIds || [] } },
        select: { name: true },
      });
      const vipNames = vips.map(v => v.name).join(', ') || 'VIP';

      const message = `⚠️ TRIP STARTING in ${minutesUntil} MINUTES!

📍 Pickup: ${event.pickupLocation || 'See schedule'}
👤 VIP: ${vipNames}
🚐 Vehicle: ${event.vehicle?.name || 'Check assignment'}

You should be at the pickup location NOW.

Reply:
1️⃣ = Ready and waiting
2️⃣ = Running late
3️⃣ = Issue / Need help`;

      const formattedPhone = this.signalService.formatPhoneNumber(event.driver.phone);
      await this.signalService.sendMessage(linkedNumber, formattedPhone, message);

      // Mark reminder as sent
      await this.prisma.scheduleEvent.update({
        where: { id: event.id },
        data: { reminder5MinSent: true },
      });

      this.logger.log(`Sent 5-min reminder to ${event.driver.name} for event ${event.id}`);
    } catch (error) {
      this.logger.error(`Failed to send 5-min reminder for event ${event.id}:`, error);
    }
  }

  /**
   * Transition SCHEDULED → IN_PROGRESS for events whose startTime has passed
   */
  private async transitionToInProgress(now: Date) {
    const eventsToStart = await this.prisma.scheduleEvent.findMany({
      where: {
        status: EventStatus.SCHEDULED,
        startTime: { lte: now },
        deletedAt: null,
      },
      include: {
        driver: true,
        vehicle: true,
      },
    });

    for (const event of eventsToStart) {
      try {
        // Update status to IN_PROGRESS
        await this.prisma.scheduleEvent.update({
          where: { id: event.id },
          data: {
            status: EventStatus.IN_PROGRESS,
            actualStartTime: now,
          },
        });

        this.logger.log(`Event ${event.id} (${event.title}) auto-started`);

        // Send Signal confirmation request to driver if assigned
        if (event.driver?.phone) {
          await this.sendDriverConfirmationRequest(event);
        }
      } catch (error) {
        this.logger.error(`Failed to transition event ${event.id}:`, error);
      }
    }

    if (eventsToStart.length > 0) {
      this.logger.log(`Auto-started ${eventsToStart.length} events`);
    }
  }

  /**
   * Transition IN_PROGRESS → COMPLETED for events past their endTime + grace period
   * Only auto-complete if no driver confirmation is pending
   */
  private async transitionToCompleted(now: Date) {
    const gracePeriodAgo = new Date(now.getTime() - this.COMPLETION_GRACE_PERIOD);

    const eventsToComplete = await this.prisma.scheduleEvent.findMany({
      where: {
        status: EventStatus.IN_PROGRESS,
        endTime: { lte: gracePeriodAgo },
        deletedAt: null,
      },
      include: {
        driver: true,
      },
    });

    for (const event of eventsToComplete) {
      try {
        await this.prisma.scheduleEvent.update({
          where: { id: event.id },
          data: {
            status: EventStatus.COMPLETED,
            actualEndTime: now,
          },
        });

        this.logger.log(`Event ${event.id} (${event.title}) auto-completed`);
      } catch (error) {
        this.logger.error(`Failed to complete event ${event.id}:`, error);
      }
    }

    if (eventsToComplete.length > 0) {
      this.logger.log(`Auto-completed ${eventsToComplete.length} events`);
    }
  }

  /**
   * Send a Signal message to the driver asking for confirmation
   */
  private async sendDriverConfirmationRequest(event: any) {
    try {
      const linkedNumber = await this.signalService.getLinkedNumber();
      if (!linkedNumber) {
        this.logger.warn('No Signal account linked, skipping driver notification');
        return;
      }

      // Get VIP names for the message
      const vips = await this.prisma.vIP.findMany({
        where: { id: { in: event.vipIds || [] } },
        select: { name: true },
      });
      const vipNames = vips.map(v => v.name).join(', ') || 'VIP';

      const message = `🚗 TRIP STARTED: ${event.title}

📍 Pickup: ${event.pickupLocation || 'See schedule'}
📍 Dropoff: ${event.dropoffLocation || 'See schedule'}
👤 VIP: ${vipNames}
🚐 Vehicle: ${event.vehicle?.name || 'Not assigned'}

Please confirm status:
1️⃣ = En route / Confirmed
2️⃣ = Delayed (explain in next message)
3️⃣ = Issue / Need help

Reply with 1, 2, or 3`;

      const formattedPhone = this.signalService.formatPhoneNumber(event.driver.phone);
      await this.signalService.sendMessage(linkedNumber, formattedPhone, message);

      this.logger.log(`Sent confirmation request to driver ${event.driver.name} for event ${event.id}`);
    } catch (error) {
      this.logger.error(`Failed to send Signal confirmation for event ${event.id}:`, error);
    }
  }

  /**
   * Process a driver's response to a confirmation request
   * Called by the Signal message handler when a driver replies with 1, 2, or 3
   */
  async processDriverResponse(driverPhone: string, response: string): Promise<string | null> {
    const responseNum = parseInt(response.trim(), 10);
    if (![1, 2, 3].includes(responseNum)) {
      return null; // Not a status response
    }

    // Find the driver
    const driver = await this.prisma.driver.findFirst({
      where: {
        phone: { contains: driverPhone.replace(/\D/g, '').slice(-10) },
        deletedAt: null,
      },
    });

    if (!driver) {
      return null;
    }

    // Find their current IN_PROGRESS event
    const activeEvent = await this.prisma.scheduleEvent.findFirst({
      where: {
        driverId: driver.id,
        status: EventStatus.IN_PROGRESS,
        deletedAt: null,
      },
      include: { vehicle: true },
    });

    if (!activeEvent) {
      return 'No active trip found. Reply ignored.';
    }

    let replyMessage: string;

    switch (responseNum) {
      case 1: // Confirmed
        // Event is already IN_PROGRESS, this just confirms it
        await this.prisma.scheduleEvent.update({
          where: { id: activeEvent.id },
          data: {
            notes: `${activeEvent.notes || ''}\n[${new Date().toLocaleTimeString()}] Driver confirmed en route`.trim(),
          },
        });
        replyMessage = `✅ Confirmed! Safe travels. Reply when completed or if you need assistance.`;
        break;

      case 2: // Delayed
        await this.prisma.scheduleEvent.update({
          where: { id: activeEvent.id },
          data: {
            notes: `${activeEvent.notes || ''}\n[${new Date().toLocaleTimeString()}] Driver reported DELAY`.trim(),
          },
        });
        replyMessage = `⏰ Delay noted. Please reply with details about the delay. Coordinator has been alerted.`;
        break;

      case 3: // Issue
        await this.prisma.scheduleEvent.update({
          where: { id: activeEvent.id },
          data: {
            notes: `${activeEvent.notes || ''}\n[${new Date().toLocaleTimeString()}] Driver reported ISSUE - needs help`.trim(),
          },
        });
        replyMessage = `🚨 Issue reported! A coordinator will contact you shortly. Please describe the issue in your next message.`;
        break;

      default:
        return null;
    }

    // Send the reply
    try {
      const linkedNumber = await this.signalService.getLinkedNumber();
      if (linkedNumber && driver.phone) {
        const formattedPhone = this.signalService.formatPhoneNumber(driver.phone);
        await this.signalService.sendMessage(linkedNumber, formattedPhone, replyMessage);
      }
    } catch (error) {
      this.logger.error('Failed to send reply to driver:', error);
    }

    return replyMessage;
  }
}
