import {
  Injectable,
  Logger,
  NotFoundException,
  ServiceUnavailableException,
} from "@nestjs/common";
import { EmailNotificationStrategy } from "./strategies/notification.email.strategy";
import { NotificationStrategy } from "./strategies/notification.strategy";
import { SmsNotificationStrategy } from "./strategies/notification.sms.strategy";



type StrategyMethod = "sms" | "email"
@Injectable()
export class NotificationsService {
  private readonly logger = new Logger(NotificationsService.name);
  private readonly strategies: Map<StrategyMethod, NotificationStrategy>
  constructor(private readonly email: EmailNotificationStrategy, private readonly sms: SmsNotificationStrategy) {
    this.strategies = new Map([
      ["sms", this.sms as NotificationStrategy],
      ["email", this.email as NotificationStrategy]
    ])
  }

  /**
   * Dispatch a notification to an operator or customer.
   * TODO: wire to email/SMS provider (SendGrid, SMS API, etc.) via a mailer service.
   */

  async directSend(method: StrategyMethod, recipient: string, message: string) {
    const strategy = this.strategies.get(method);
    if (!strategy) {
      throw new NotFoundException();
    }
    // A strategy returning false (or throwing) is a real delivery failure — do
    // not swallow it. Surface it so callers observe the failure (existing
    // callers wrap directSend in try/catch for best-effort notifications).
    const sent = await strategy.send(recipient, message);
    if (!sent) {
      this.logger.error(
        `Notification via ${method} to ${recipient} failed to send`,
      );
      throw new ServiceUnavailableException(
        `Failed to send ${method} notification`,
      );
    }
    this.logger.log(`Notification via ${method} to ${recipient} sent`);
  }

  async notifyDriverVehicleAssignment(params: {
    driverPhone: string;
    driverName: string;
    vehiclePlateNumber: string;
    bookingReference: string;
    pickupAddress?: string | null;
    destinationYard?: string | null;
  }): Promise<void> {
    const { driverPhone, driverName, vehiclePlateNumber, bookingReference, pickupAddress, destinationYard } = params;
    const message =
      `Dear ${driverName}, you have been assigned to a first-mile pickup. ` +
      `Booking: ${bookingReference}. Vehicle: ${vehiclePlateNumber}. ` +
      (pickupAddress ? `Pickup: ${pickupAddress}. ` : '') +
      (destinationYard ? `Destination: ${destinationYard}.` : '');

    try {
      await this.directSend('sms', driverPhone, message);
    } catch (err) {
      this.logger.error(`Failed to notify driver ${driverName} (${driverPhone}): ${String(err)}`);
    }
  }
}
