/**
 * Notification Scheduler
 * Handles scheduled and recurring notifications
 */

import { getNotificationService } from './service';
import {
  ScheduledNotification,
  NotificationRecipient,
  NotificationPayload,
  NotificationChannel,
  NotificationPriority
} from './types';

export class NotificationScheduler {
  private static instance: NotificationScheduler;
  private scheduledNotifications: Map<string, ScheduledNotification> = new Map();
  private checkInterval: NodeJS.Timeout | null = null;
  private isRunning = false;

  private constructor() {}

  static getInstance(): NotificationScheduler {
    if (!NotificationScheduler.instance) {
      NotificationScheduler.instance = new NotificationScheduler();
    }
    return NotificationScheduler.instance;
  }

  /**
   * Start the scheduler
   */
  start(intervalMs: number = 60000): void {
    if (this.isRunning) return;

    this.isRunning = true;
    console.log('[NotificationScheduler] Started');

    // Check immediately
    this.processScheduledNotifications();

    // Set up interval
    this.checkInterval = setInterval(() => {
      this.processScheduledNotifications();
    }, intervalMs);
  }

  /**
   * Stop the scheduler
   */
  stop(): void {
    if (this.checkInterval) {
      clearInterval(this.checkInterval);
      this.checkInterval = null;
    }
    this.isRunning = false;
    console.log('[NotificationScheduler] Stopped');
  }

  /**
   * Schedule a notification
   */
  schedule(
    recipient: NotificationRecipient,
    payload: NotificationPayload,
    scheduledFor: Date | string,
    options?: {
      channels?: NotificationChannel[];
      priority?: NotificationPriority;
      recurring?: {
        pattern: 'daily' | 'weekly' | 'monthly';
        endDate?: string;
      };
    }
  ): ScheduledNotification {
    const id = `sched-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;

    const scheduled: ScheduledNotification = {
      id,
      notification: {
        recipientId: recipient.userId,
        payload,
        channels: options?.channels || ['inApp', 'email'],
        priority: options?.priority || 'medium',
        retryCount: 0
      },
      scheduledFor: typeof scheduledFor === 'string' ? scheduledFor : scheduledFor.toISOString(),
      recurring: options?.recurring,
      status: 'scheduled'
    };

    this.scheduledNotifications.set(id, scheduled);
    console.log(`[NotificationScheduler] Scheduled notification ${id} for ${scheduled.scheduledFor}`);

    return scheduled;
  }

  /**
   * Cancel a scheduled notification
   */
  cancel(id: string): boolean {
    const scheduled = this.scheduledNotifications.get(id);
    if (scheduled && scheduled.status === 'scheduled') {
      scheduled.status = 'cancelled';
      return true;
    }
    return false;
  }

  /**
   * Get scheduled notification
   */
  getScheduled(id: string): ScheduledNotification | undefined {
    return this.scheduledNotifications.get(id);
  }

  /**
   * Get all scheduled notifications for a user
   */
  getScheduledForUser(userId: string): ScheduledNotification[] {
    const result: ScheduledNotification[] = [];
    this.scheduledNotifications.forEach(scheduled => {
      if (scheduled.notification.recipientId === userId && scheduled.status === 'scheduled') {
        result.push(scheduled);
      }
    });
    return result.sort((a, b) =>
      new Date(a.scheduledFor).getTime() - new Date(b.scheduledFor).getTime()
    );
  }

  /**
   * Schedule a plant care reminder
   */
  schedulePlantReminder(
    userId: string,
    email: string,
    plantId: string,
    plantName: string,
    reminderType: 'water' | 'fertilize' | 'prune' | 'harvest',
    scheduledFor: Date
  ): ScheduledNotification {
    const reminderMessages = {
      water: `Time to water your ${plantName}!`,
      fertilize: `Your ${plantName} needs fertilizing.`,
      prune: `Consider pruning your ${plantName} for better growth.`,
      harvest: `Your ${plantName} may be ready for harvest!`
    };

    return this.schedule(
      { userId, email },
      {
        type: 'plant_reminder',
        title: `Plant Care Reminder: ${plantName}`,
        message: reminderMessages[reminderType],
        data: { plantId, plantName, reminderType },
        actionUrl: `/plants/${plantId}`
      },
      scheduledFor,
      { channels: ['inApp', 'email', 'push'] }
    );
  }

  /**
   * Schedule weekly digest
   */
  scheduleWeeklyDigest(userId: string, email: string): ScheduledNotification {
    // Schedule for next Monday at 9 AM
    const now = new Date();
    const nextMonday = new Date(now);
    nextMonday.setDate(now.getDate() + ((7 - now.getDay() + 1) % 7 || 7));
    nextMonday.setHours(9, 0, 0, 0);

    return this.schedule(
      { userId, email },
      {
        type: 'weekly_digest',
        title: 'Your Weekly LocalGreenChain Summary',
        message: 'Check out what happened this week!',
        actionUrl: '/dashboard'
      },
      nextMonday,
      {
        channels: ['email'],
        recurring: { pattern: 'weekly' }
      }
    );
  }

  /**
   * Schedule harvest alert
   */
  scheduleHarvestAlert(
    userId: string,
    email: string,
    batchId: string,
    cropType: string,
    estimatedHarvestDate: Date
  ): ScheduledNotification {
    // Schedule alert 1 day before harvest
    const alertDate = new Date(estimatedHarvestDate);
    alertDate.setDate(alertDate.getDate() - 1);

    return this.schedule(
      { userId, email },
      {
        type: 'harvest_ready',
        title: `Harvest Coming Soon: ${cropType}`,
        message: `Your ${cropType} batch will be ready for harvest tomorrow!`,
        data: { batchId, cropType, estimatedHarvestDate: estimatedHarvestDate.toISOString() },
        actionUrl: `/vertical-farm/batch/${batchId}`
      },
      alertDate,
      { channels: ['inApp', 'email', 'push'], priority: 'high' }
    );
  }

  /**
   * Process due notifications
   */
  private async processScheduledNotifications(): Promise<void> {
    const now = new Date();
    const notificationService = getNotificationService();

    for (const [id, scheduled] of this.scheduledNotifications.entries()) {
      if (scheduled.status !== 'scheduled') continue;

      const scheduledTime = new Date(scheduled.scheduledFor);
      if (scheduledTime <= now) {
        try {
          // Send the notification
          await notificationService.send(
            { userId: scheduled.notification.recipientId },
            scheduled.notification.payload,
            {
              channels: scheduled.notification.channels,
              priority: scheduled.notification.priority
            }
          );

          // Handle recurring
          if (scheduled.recurring) {
            const endDate = scheduled.recurring.endDate
              ? new Date(scheduled.recurring.endDate)
              : null;

            if (!endDate || scheduledTime < endDate) {
              // Schedule next occurrence
              const nextDate = this.getNextOccurrence(scheduledTime, scheduled.recurring.pattern);
              scheduled.scheduledFor = nextDate.toISOString();
              console.log(`[NotificationScheduler] Rescheduled ${id} for ${scheduled.scheduledFor}`);
            } else {
              scheduled.status = 'sent';
            }
          } else {
            scheduled.status = 'sent';
          }

          console.log(`[NotificationScheduler] Sent scheduled notification ${id}`);
        } catch (error: any) {
          console.error(`[NotificationScheduler] Failed to send ${id}:`, error.message);
        }
      }
    }
  }

  /**
   * Calculate next occurrence date
   */
  private getNextOccurrence(current: Date, pattern: 'daily' | 'weekly' | 'monthly'): Date {
    const next = new Date(current);

    switch (pattern) {
      case 'daily':
        next.setDate(next.getDate() + 1);
        break;
      case 'weekly':
        next.setDate(next.getDate() + 7);
        break;
      case 'monthly':
        next.setMonth(next.getMonth() + 1);
        break;
    }

    return next;
  }

  /**
   * Get scheduler stats
   */
  getStats(): {
    isRunning: boolean;
    total: number;
    scheduled: number;
    sent: number;
    cancelled: number;
  } {
    let scheduled = 0;
    let sent = 0;
    let cancelled = 0;

    this.scheduledNotifications.forEach(n => {
      switch (n.status) {
        case 'scheduled': scheduled++; break;
        case 'sent': sent++; break;
        case 'cancelled': cancelled++; break;
      }
    });

    return {
      isRunning: this.isRunning,
      total: this.scheduledNotifications.size,
      scheduled,
      sent,
      cancelled
    };
  }

  /**
   * Clean up old notifications
   */
  cleanup(olderThanDays: number = 30): number {
    const cutoff = new Date();
    cutoff.setDate(cutoff.getDate() - olderThanDays);

    let removed = 0;
    for (const [id, scheduled] of this.scheduledNotifications.entries()) {
      if (scheduled.status !== 'scheduled') {
        const scheduledDate = new Date(scheduled.scheduledFor);
        if (scheduledDate < cutoff) {
          this.scheduledNotifications.delete(id);
          removed++;
        }
      }
    }

    return removed;
  }
}

// Export singleton getter
export function getNotificationScheduler(): NotificationScheduler {
  return NotificationScheduler.getInstance();
}
