import { Injectable, Logger } from '@nestjs/common';

import { Booking } from '../bookings/entities/booking.entity';
import { NotificationsService } from '../notifications/notifications.service';
import { PAYMENT_WINDOW_MS } from './booking-batch.constants';

@Injectable()
export class BookingNotifierService {
  private readonly logger = new Logger(BookingNotifierService.name);

  constructor(private readonly notifications: NotificationsService) {}

  private ref(b: Booking): string {
    return `${b.reference}${b.isGovernment ? ' (gov)' : ''}`;
  }

  private async notifyContact(
    b: Booking,
    message: string,
    logLabel: string,
  ): Promise<void> {
    this.logger.log(`${logLabel} — ${this.ref(b)}`);
    const phone = b.company?.contactPersonPhone ?? b.company?.phone ?? null;
    const email = b.company?.email ?? b.company?.generalManagerEmail ?? null;

    if (phone) {
      try {
        await this.notifications.directSend('sms', phone, message);
      } catch (err) {
        this.logger.warn(`SMS failed for ${this.ref(b)}: ${(err as Error).message}`);
      }
    }
    if (email) {
      try {
        await this.notifications.directSend('email', email, message);
      } catch (err) {
        this.logger.warn(`Email failed for ${this.ref(b)}: ${(err as Error).message}`);
      }
    }
    if (!phone && !email) {
      this.logger.warn(`No contact on file for ${this.ref(b)} — notification not sent`);
    }
  }

  async payNow(b: Booking, deadline: Date): Promise<void> {
    const payMinutes = Math.round(PAYMENT_WINDOW_MS / 60_000);
    const eat = deadline.toLocaleString('en-GB', { timeZone: 'Africa/Addis_Ababa' });
    const msg = `Pay within ${payMinutes} minute${payMinutes === 1 ? '' : 's'} to secure train slot ${b.reference ?? b.id}. Deadline: ${eat} EAT.`;
    await this.notifyContact(b, msg, 'PAY NOW');
  }

  secured(b: Booking, reason: 'paid' | 'gov'): void {
    const msg = `Booking ${b.reference ?? b.id} allocated on train schedule ${b.trainScheduleId ?? ''}${
      reason === 'gov' ? ' (government)' : ''
    }.`;
    void this.notifyContact(b, msg, 'ALLOCATED');
  }

  expired(b: Booking): void {
    const msg = `Payment window expired for booking ${b.reference ?? b.id}. Reschedule or cancel — no re-approval needed.`;
    void this.notifyContact(b, msg, 'EXPIRED');
  }

  scheduleFull(b: Booking): void {
    this.logger.warn(
      `SCHEDULE FULL — ${this.ref(b)} could not be placed; change schedule, pick another day, or cancel.`,
    );
  }

  /**
   * Staff-facing warning when a pooled booking fits no train on its chosen day.
   * It stays pending and is retried next batch; staff can add capacity or pin it
   * to a train manually. Mirrors {@link scheduleFull} — no customer notification.
   */
  unplaced(b: Booking, day: string): void {
    this.logger.warn(
      `UNPLACED — ${this.ref(b)} could not be placed on any train for ${day}; add capacity or assign it manually.`,
    );
  }

  displaced(b: Booking): void {
    const msg = `Booking ${b.reference ?? b.id} was displaced by a government booking. Move to another schedule or cancel.`;
    void this.notifyContact(b, msg, 'DISPLACED');
  }
}
