/**
 * Export Service
 *
 * Handles booking data export to various formats (CSV, Excel, JSON)
 */

import { Injectable, Logger } from '@nestjs/common';
import { Booking } from '@domain/entities/booking.entity';
import { RateQuote } from '@domain/entities/rate-quote.entity';
import { ExportFormat, ExportField } from '../dto/booking-export.dto';
import * as ExcelJS from 'exceljs';

interface BookingExportData {
  booking: Booking;
  rateQuote: RateQuote;
}

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

  /**
   * Export bookings to specified format
   */
  async exportBookings(
    data: BookingExportData[],
    format: ExportFormat,
    fields?: ExportField[]
  ): Promise<{ buffer: Buffer; contentType: string; filename: string }> {
    this.logger.log(
      `Exporting ${data.length} bookings to ${format} format with ${fields?.length || 'all'} fields`
    );

    switch (format) {
      case ExportFormat.CSV:
        return this.exportToCSV(data, fields);
      case ExportFormat.EXCEL:
        return this.exportToExcel(data, fields);
      case ExportFormat.JSON:
        return this.exportToJSON(data, fields);
      default:
        throw new Error(`Unsupported export format: ${format}`);
    }
  }

  /**
   * Export to CSV format
   */
  private async exportToCSV(
    data: BookingExportData[],
    fields?: ExportField[]
  ): Promise<{ buffer: Buffer; contentType: string; filename: string }> {
    const selectedFields = fields || Object.values(ExportField);
    const rows = data.map(item => this.extractFields(item, selectedFields));

    // Build CSV header
    const header = selectedFields.map(field => this.getFieldLabel(field)).join(',');

    // Build CSV rows
    const csvRows = rows.map(row =>
      selectedFields.map(field => this.escapeCSVValue(row[field] || '')).join(',')
    );

    const csv = [header, ...csvRows].join('\n');
    const buffer = Buffer.from(csv, 'utf-8');

    const timestamp = new Date().toISOString().split('T')[0];
    const filename = `bookings_export_${timestamp}.csv`;

    return {
      buffer,
      contentType: 'text/csv',
      filename,
    };
  }

  /**
   * Export to Excel format
   */
  private async exportToExcel(
    data: BookingExportData[],
    fields?: ExportField[]
  ): Promise<{ buffer: Buffer; contentType: string; filename: string }> {
    const selectedFields = fields || Object.values(ExportField);
    const rows = data.map(item => this.extractFields(item, selectedFields));

    const workbook = new ExcelJS.Workbook();
    workbook.creator = 'Xpeditis';
    workbook.created = new Date();

    const worksheet = workbook.addWorksheet('Bookings');

    // Add header row with styling
    const headerRow = worksheet.addRow(selectedFields.map(field => this.getFieldLabel(field)));
    headerRow.font = { bold: true };
    headerRow.fill = {
      type: 'pattern',
      pattern: 'solid',
      fgColor: { argb: 'FFE0E0E0' },
    };

    // Add data rows
    rows.forEach(row => {
      const values = selectedFields.map(field => row[field] || '');
      worksheet.addRow(values);
    });

    // Auto-fit columns
    worksheet.columns.forEach(column => {
      let maxLength = 10;
      column.eachCell?.({ includeEmpty: false }, cell => {
        const columnLength = cell.value ? String(cell.value).length : 10;
        if (columnLength > maxLength) {
          maxLength = columnLength;
        }
      });
      column.width = Math.min(maxLength + 2, 50);
    });

    const buffer = await workbook.xlsx.writeBuffer();

    const timestamp = new Date().toISOString().split('T')[0];
    const filename = `bookings_export_${timestamp}.xlsx`;

    return {
      buffer: Buffer.from(buffer),
      contentType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      filename,
    };
  }

  /**
   * Export to JSON format
   */
  private async exportToJSON(
    data: BookingExportData[],
    fields?: ExportField[]
  ): Promise<{ buffer: Buffer; contentType: string; filename: string }> {
    const selectedFields = fields || Object.values(ExportField);
    const rows = data.map(item => this.extractFields(item, selectedFields));

    const json = JSON.stringify(
      {
        exportedAt: new Date().toISOString(),
        totalBookings: rows.length,
        bookings: rows,
      },
      null,
      2
    );

    const buffer = Buffer.from(json, 'utf-8');

    const timestamp = new Date().toISOString().split('T')[0];
    const filename = `bookings_export_${timestamp}.json`;

    return {
      buffer,
      contentType: 'application/json',
      filename,
    };
  }

  /**
   * Extract specified fields from booking data
   */
  private extractFields(data: BookingExportData, fields: ExportField[]): Record<string, any> {
    const { booking, rateQuote } = data;
    const result: Record<string, any> = {};

    fields.forEach(field => {
      switch (field) {
        case ExportField.BOOKING_NUMBER:
          result[field] = booking.bookingNumber.value;
          break;
        case ExportField.STATUS:
          result[field] = booking.status.value;
          break;
        case ExportField.CREATED_AT:
          result[field] = booking.createdAt.toISOString();
          break;
        case ExportField.CARRIER:
          result[field] = rateQuote.carrierName;
          break;
        case ExportField.ORIGIN:
          result[field] = `${rateQuote.origin.name} (${rateQuote.origin.code})`;
          break;
        case ExportField.DESTINATION:
          result[field] = `${rateQuote.destination.name} (${rateQuote.destination.code})`;
          break;
        case ExportField.ETD:
          result[field] = rateQuote.etd.toISOString();
          break;
        case ExportField.ETA:
          result[field] = rateQuote.eta.toISOString();
          break;
        case ExportField.SHIPPER:
          result[field] = booking.shipper.name;
          break;
        case ExportField.CONSIGNEE:
          result[field] = booking.consignee.name;
          break;
        case ExportField.CONTAINER_TYPE:
          result[field] = booking.containers.map(c => c.type).join(', ');
          break;
        case ExportField.CONTAINER_COUNT:
          result[field] = booking.containers.length;
          break;
        case ExportField.TOTAL_TEUS:
          result[field] = booking.containers.reduce((total, c) => {
            return total + (c.type.startsWith('20') ? 1 : 2);
          }, 0);
          break;
        case ExportField.PRICE:
          result[field] = `${rateQuote.pricing.currency} ${rateQuote.pricing.totalAmount.toFixed(
            2
          )}`;
          break;
      }
    });

    return result;
  }

  /**
   * Get human-readable field label
   */
  private getFieldLabel(field: ExportField): string {
    const labels: Record<ExportField, string> = {
      [ExportField.BOOKING_NUMBER]: 'Booking Number',
      [ExportField.STATUS]: 'Status',
      [ExportField.CREATED_AT]: 'Created At',
      [ExportField.CARRIER]: 'Carrier',
      [ExportField.ORIGIN]: 'Origin',
      [ExportField.DESTINATION]: 'Destination',
      [ExportField.ETD]: 'ETD',
      [ExportField.ETA]: 'ETA',
      [ExportField.SHIPPER]: 'Shipper',
      [ExportField.CONSIGNEE]: 'Consignee',
      [ExportField.CONTAINER_TYPE]: 'Container Type',
      [ExportField.CONTAINER_COUNT]: 'Container Count',
      [ExportField.TOTAL_TEUS]: 'Total TEUs',
      [ExportField.PRICE]: 'Price',
    };
    return labels[field];
  }

  /**
   * Escape CSV value (handle commas, quotes, newlines)
   */
  private escapeCSVValue(value: string): string {
    const stringValue = String(value);
    if (stringValue.includes(',') || stringValue.includes('"') || stringValue.includes('\n')) {
      return `"${stringValue.replace(/"/g, '""')}"`;
    }
    return stringValue;
  }
}
