/**
 * Booking Automation Service
 *
 * Handles post-booking automation (emails, PDFs, storage)
 */

import { Injectable, Logger, Inject } from '@nestjs/common';
import { Booking } from '@domain/entities/booking.entity';
import { EmailPort, EMAIL_PORT } from '@domain/ports/out/email.port';
import { PdfPort, PDF_PORT, BookingPdfData } from '@domain/ports/out/pdf.port';
import { StoragePort, STORAGE_PORT } from '@domain/ports/out/storage.port';
import { UserRepository, USER_REPOSITORY } from '@domain/ports/out/user.repository';
import {
  RateQuoteRepository,
  RATE_QUOTE_REPOSITORY,
} from '@domain/ports/out/rate-quote.repository';

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

  constructor(
    @Inject(EMAIL_PORT) private readonly emailPort: EmailPort,
    @Inject(PDF_PORT) private readonly pdfPort: PdfPort,
    @Inject(STORAGE_PORT) private readonly storagePort: StoragePort,
    @Inject(USER_REPOSITORY) private readonly userRepository: UserRepository,
    @Inject(RATE_QUOTE_REPOSITORY) private readonly rateQuoteRepository: RateQuoteRepository
  ) {}

  /**
   * Execute all post-booking automation tasks
   */
  async executePostBookingTasks(booking: Booking): Promise<void> {
    this.logger.log(`Starting post-booking automation for booking: ${booking.bookingNumber.value}`);

    try {
      // Get user and rate quote details
      const user = await this.userRepository.findById(booking.userId);
      if (!user) {
        throw new Error(`User not found: ${booking.userId}`);
      }

      const rateQuote = await this.rateQuoteRepository.findById(booking.rateQuoteId);
      if (!rateQuote) {
        throw new Error(`Rate quote not found: ${booking.rateQuoteId}`);
      }

      // Generate booking confirmation PDF
      const pdfData: BookingPdfData = {
        bookingNumber: booking.bookingNumber.value,
        bookingDate: booking.createdAt,
        origin: {
          code: rateQuote.origin.code,
          name: rateQuote.origin.name,
        },
        destination: {
          code: rateQuote.destination.code,
          name: rateQuote.destination.name,
        },
        carrier: {
          name: rateQuote.carrierName,
          logo: undefined, // TODO: Add carrierLogoUrl to RateQuote entity
        },
        shipper: {
          name: booking.shipper.name,
          address: this.formatAddress(booking.shipper.address),
          contact: booking.shipper.contactName,
          email: booking.shipper.contactEmail,
          phone: booking.shipper.contactPhone,
        },
        consignee: {
          name: booking.consignee.name,
          address: this.formatAddress(booking.consignee.address),
          contact: booking.consignee.contactName,
          email: booking.consignee.contactEmail,
          phone: booking.consignee.contactPhone,
        },
        containers: booking.containers.map(c => ({
          type: c.type,
          quantity: 1,
          containerNumber: c.containerNumber,
          sealNumber: c.sealNumber,
        })),
        cargoDescription: booking.cargoDescription,
        specialInstructions: booking.specialInstructions,
        etd: rateQuote.etd,
        eta: rateQuote.eta,
        transitDays: rateQuote.transitDays,
        price: {
          amount: rateQuote.pricing.totalAmount,
          currency: rateQuote.pricing.currency,
        },
      };

      const pdfBuffer = await this.pdfPort.generateBookingConfirmation(pdfData);

      // Store PDF in S3
      const storageKey = `bookings/${booking.id}/${booking.bookingNumber.value}.pdf`;
      await this.storagePort.upload({
        bucket: 'xpeditis-bookings',
        key: storageKey,
        body: pdfBuffer,
        contentType: 'application/pdf',
        metadata: {
          bookingId: booking.id,
          bookingNumber: booking.bookingNumber.value,
          userId: user.id,
        },
      });

      this.logger.log(
        `Stored booking PDF: ${storageKey} for booking ${booking.bookingNumber.value}`
      );

      // Send confirmation email with PDF attachment
      await this.emailPort.sendBookingConfirmation(
        user.email,
        booking.bookingNumber.value,
        {
          origin: rateQuote.origin.name,
          destination: rateQuote.destination.name,
          carrier: rateQuote.carrierName,
          etd: rateQuote.etd,
          eta: rateQuote.eta,
        },
        pdfBuffer
      );

      this.logger.log(
        `Post-booking automation completed successfully for booking: ${booking.bookingNumber.value}`
      );
    } catch (error) {
      this.logger.error(
        `Post-booking automation failed for booking: ${booking.bookingNumber.value}`,
        error
      );
      // Don't throw - we don't want to fail the booking creation if email/PDF fails
      // TODO: Implement retry mechanism with queue (Bull/BullMQ)
    }
  }

  /**
   * Format address for PDF
   */
  private formatAddress(address: {
    street: string;
    city: string;
    postalCode: string;
    country: string;
  }): string {
    return `${address.street}, ${address.city}, ${address.postalCode}, ${address.country}`;
  }

  /**
   * Send booking update notification
   */
  async sendBookingUpdateNotification(
    booking: Booking,
    updateType: 'confirmed' | 'delayed' | 'arrived'
  ): Promise<void> {
    try {
      const user = await this.userRepository.findById(booking.userId);
      if (!user) {
        throw new Error(`User not found: ${booking.userId}`);
      }

      // TODO: Send update email based on updateType
      this.logger.log(
        `Sent ${updateType} notification for booking: ${booking.bookingNumber.value}`
      );
    } catch (error) {
      this.logger.error(`Failed to send booking update notification`, error);
    }
  }
}
