/**
 * OneTimeEvent model - represents scheduled one-time events
 * Ported from Python oneTimeEvent class
 */

import { v4 as uuidv4 } from 'uuid';

export type DateType = 'date' | 'daysFromNow';

export interface OneTimeEventData {
  id?: string;
  message: string;
  title?: string;
  dateType?: DateType;
  dateModifier?: number;
  targetDate?: string;  // ISO date string
  hour?: number;
  location?: string;
  completionFunc?: string;
  completionArgs?: unknown[];
}

/**
 * OneTimeEvent - scheduled events that trigger once at a specific time
 */
export class OneTimeEvent {
  id: string;
  message: string;
  title: string;
  dateType: DateType;
  dateModifier: number;
  targetDate: string;
  hour: number;
  location: string;
  type: string = 'oneTimeEventObject';
  completionFunc: string;
  completionArgs: unknown[];

  constructor(data: OneTimeEventData) {
    this.id = data.id ?? uuidv4();
    this.message = data.message;
    this.title = data.title ?? '';
    this.dateType = data.dateType ?? 'daysFromNow';
    this.dateModifier = data.dateModifier ?? 1;
    this.targetDate = data.targetDate ?? '';
    this.hour = data.hour ?? 12;
    this.location = data.location ?? '';
    this.completionFunc = data.completionFunc ?? '';
    this.completionArgs = data.completionArgs ?? [];

    // Calculate target date if not provided
    if (!this.targetDate) {
      this.targetDate = this.findDate();
    }
  }

  /**
   * Calculate the target date based on dateType
   */
  findDate(currentDate?: string): string {
    const baseDate = currentDate ? new Date(currentDate) : new Date();

    if (this.dateType === 'date' && this.targetDate) {
      return this.targetDate;
    }

    if (this.dateType === 'daysFromNow') {
      const targetDate = new Date(baseDate);
      targetDate.setDate(targetDate.getDate() + this.dateModifier);
      return targetDate.toISOString().split('T')[0];
    }

    return baseDate.toISOString().split('T')[0];
  }

  /**
   * Check if the event should trigger at the given date/hour
   */
  shouldTrigger(currentDate: string, currentHour: number): boolean {
    return this.targetDate === currentDate && this.hour === currentHour;
  }

  /**
   * Convert to plain object for JSON serialization
   */
  toJSON(): OneTimeEventData {
    return {
      id: this.id,
      message: this.message,
      title: this.title,
      dateType: this.dateType,
      dateModifier: this.dateModifier,
      targetDate: this.targetDate,
      hour: this.hour,
      location: this.location,
      completionFunc: this.completionFunc,
      completionArgs: this.completionArgs,
    };
  }

  /**
   * Create from plain object
   */
  static fromJSON(data: OneTimeEventData): OneTimeEvent {
    return new OneTimeEvent(data);
  }
}

/**
 * Parse one-time events for a player
 * Called every hour to check for triggered events
 */
export function parseOneTimeEvents(
  player: { c: { oneTimeEvents?: OneTimeEventData[] }; date: string; hourOfDay: number },
  sendEventMessage?: (event: { type: string; message: string }) => Promise<void>
): void {
  const events = player.c.oneTimeEvents ?? [];
  const triggeredIndices: number[] = [];

  for (let i = 0; i < events.length; i++) {
    const eventData = events[i];
    const event = new OneTimeEvent(eventData);

    if (event.shouldTrigger(player.date, player.hourOfDay)) {
      // Trigger the event
      if (sendEventMessage) {
        sendEventMessage({
          type: 'messageEvent',
          message: event.message,
        });
      }

      triggeredIndices.push(i);
    }
  }

  // Remove triggered events (in reverse order to preserve indices)
  for (let i = triggeredIndices.length - 1; i >= 0; i--) {
    events.splice(triggeredIndices[i], 1);
  }
}
