/**
 * Report Scheduling System
 * Automatically generates and sends reports on a schedule
 */

import { EventEmitter } from 'events';

export type ReportFrequency = 'daily' | 'weekly' | 'monthly';
export type ReportType = 'withdrawal_audit' | 'payment_analytics' | 'fraud_summary' | 'admin_activity';

export interface ScheduledReport {
  id: string;
  type: ReportType;
  frequency: ReportFrequency;
  recipients: string[];
  enabled: boolean;
  lastRun?: Date;
  nextRun: Date;
  createdAt: Date;
  createdBy: number;
  filters?: Record<string, any>;
  format: 'csv' | 'json' | 'html' | 'pdf';
}

export interface ReportSchedule {
  reportId: string;
  type: ReportType;
  frequency: ReportFrequency;
  nextRun: Date;
  lastRun?: Date;
  status: 'pending' | 'running' | 'completed' | 'failed';
  error?: string;
}

/**
 * Report Scheduling Service
 */
export class ReportSchedulingService extends EventEmitter {
  private schedules: Map<string, ScheduledReport> = new Map();
  private runningReports: Set<string> = new Set();
  private reportHistory: Map<string, ReportSchedule[]> = new Map();

  constructor() {
    super();
    this.startScheduler();
  }

  /**
   * Create a scheduled report
   */
  createScheduledReport(
    type: ReportType,
    frequency: ReportFrequency,
    recipients: string[],
    adminId: number,
    format: 'csv' | 'json' | 'html' | 'pdf' = 'html',
    filters?: Record<string, any>
  ): ScheduledReport {
    const report: ScheduledReport = {
      id: `report_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
      type,
      frequency,
      recipients,
      enabled: true,
      nextRun: this.calculateNextRun(frequency),
      createdAt: new Date(),
      createdBy: adminId,
      filters,
      format,
    };

    this.schedules.set(report.id, report);
    this.emit('report_scheduled', report);

    console.log(`[ReportScheduler] Scheduled ${type} report (${frequency})`);

    return report;
  }

  /**
   * Update scheduled report
   */
  updateScheduledReport(reportId: string, updates: Partial<ScheduledReport>): boolean {
    const report = this.schedules.get(reportId);
    if (!report) return false;

    Object.assign(report, updates);
    this.emit('report_updated', report);

    return true;
  }

  /**
   * Delete scheduled report
   */
  deleteScheduledReport(reportId: string): boolean {
    const deleted = this.schedules.delete(reportId);
    if (deleted) {
      this.emit('report_deleted', reportId);
    }
    return deleted;
  }

  /**
   * Get scheduled report
   */
  getScheduledReport(reportId: string): ScheduledReport | undefined {
    return this.schedules.get(reportId);
  }

  /**
   * Get all scheduled reports
   */
  getAllScheduledReports(): ScheduledReport[] {
    return Array.from(this.schedules.values());
  }

  /**
   * Get scheduled reports by type
   */
  getScheduledReportsByType(type: ReportType): ScheduledReport[] {
    return Array.from(this.schedules.values()).filter((r) => r.type === type);
  }

  /**
   * Enable/disable scheduled report
   */
  setReportEnabled(reportId: string, enabled: boolean): boolean {
    const report = this.schedules.get(reportId);
    if (!report) return false;

    report.enabled = enabled;
    this.emit('report_enabled_changed', { reportId, enabled });

    return true;
  }

  /**
   * Calculate next run time
   */
  private calculateNextRun(frequency: ReportFrequency): Date {
    const now = new Date();

    switch (frequency) {
      case 'daily':
        // Next day at 2 AM
        const tomorrow = new Date(now);
        tomorrow.setDate(tomorrow.getDate() + 1);
        tomorrow.setHours(2, 0, 0, 0);
        return tomorrow;

      case 'weekly':
        // Next Monday at 2 AM
        const nextMonday = new Date(now);
        nextMonday.setDate(nextMonday.getDate() + ((1 + 7 - nextMonday.getDay()) % 7));
        nextMonday.setHours(2, 0, 0, 0);
        return nextMonday;

      case 'monthly':
        // First day of next month at 2 AM
        const nextMonth = new Date(now);
        nextMonth.setMonth(nextMonth.getMonth() + 1);
        nextMonth.setDate(1);
        nextMonth.setHours(2, 0, 0, 0);
        return nextMonth;

      default:
        return now;
    }
  }

  /**
   * Start the scheduler
   */
  private startScheduler(): void {
    setInterval(() => {
      this.checkAndRunScheduledReports();
    }, 60000); // Check every minute
  }

  /**
   * Check and run scheduled reports
   */
  private checkAndRunScheduledReports(): void {
    const now = new Date();

    for (const [reportId, report] of this.schedules.entries()) {
      if (!report.enabled) continue;
      if (this.runningReports.has(reportId)) continue;
      if (now < report.nextRun) continue;

      this.runReport(reportId);
    }
  }

  /**
   * Run a scheduled report
   */
  private async runReport(reportId: string): Promise<void> {
    const report = this.schedules.get(reportId);
    if (!report) return;

    this.runningReports.add(reportId);

    const schedule: ReportSchedule = {
      reportId,
      type: report.type,
      frequency: report.frequency,
      nextRun: report.nextRun,
      lastRun: report.lastRun,
      status: 'running',
    };

    try {
      console.log(`[ReportScheduler] Running ${report.type} report`);

      // Emit event for report generation
      this.emit('report_running', schedule);

      // Simulate report generation (in real implementation, would call report generators)
      await new Promise((resolve) => setTimeout(resolve, 2000));

      // Update report
      report.lastRun = new Date();
      report.nextRun = this.calculateNextRun(report.frequency);

      schedule.status = 'completed';
      schedule.lastRun = report.lastRun;
      schedule.nextRun = report.nextRun;

      // Record in history
      if (!this.reportHistory.has(reportId)) {
        this.reportHistory.set(reportId, []);
      }
      this.reportHistory.get(reportId)!.push(schedule);

      // Emit completion event
      this.emit('report_completed', schedule);

      console.log(`[ReportScheduler] ${report.type} report completed`);
    } catch (error) {
      schedule.status = 'failed';
      schedule.error = (error as Error).message;

      // Record in history
      if (!this.reportHistory.has(reportId)) {
        this.reportHistory.set(reportId, []);
      }
      this.reportHistory.get(reportId)!.push(schedule);

      // Emit error event
      this.emit('report_failed', schedule);

      console.error(`[ReportScheduler] ${report.type} report failed:`, error);
    } finally {
      this.runningReports.delete(reportId);
    }
  }

  /**
   * Get report history
   */
  getReportHistory(reportId: string, limit: number = 10): ReportSchedule[] {
    const history = this.reportHistory.get(reportId) || [];
    return history.slice(-limit).reverse();
  }

  /**
   * Get statistics
   */
  getStatistics(): {
    totalSchedules: number;
    enabledSchedules: number;
    runningReports: number;
    reportsByType: Record<ReportType, number>;
    reportsByFrequency: Record<ReportFrequency, number>;
  } {
    const allReports = Array.from(this.schedules.values());
    const enabledReports = allReports.filter((r) => r.enabled);

    const reportsByType: Record<ReportType, number> = {
      withdrawal_audit: 0,
      payment_analytics: 0,
      fraud_summary: 0,
      admin_activity: 0,
    };

    const reportsByFrequency: Record<ReportFrequency, number> = {
      daily: 0,
      weekly: 0,
      monthly: 0,
    };

    allReports.forEach((r) => {
      reportsByType[r.type]++;
      reportsByFrequency[r.frequency]++;
    });

    return {
      totalSchedules: allReports.length,
      enabledSchedules: enabledReports.length,
      runningReports: this.runningReports.size,
      reportsByType,
      reportsByFrequency,
    };
  }
}

/**
 * Global report scheduling service instance
 */
let reportSchedulingService: ReportSchedulingService | null = null;

/**
 * Get or create report scheduling service
 */
export function getReportSchedulingService(): ReportSchedulingService {
  if (!reportSchedulingService) {
    reportSchedulingService = new ReportSchedulingService();
  }
  return reportSchedulingService;
}
