import { CsvRate } from '../entities/csv-rate.entity';

/**
 * Service Level Types
 *
 * - RAPID    : +20% price, -30% transit (express, priority)
 * - STANDARD : base price and transit
 * - ECONOMIC : -15% price, +50% transit (cheapest, slowest)
 */
export enum ServiceLevel {
  RAPID = 'RAPID',
  STANDARD = 'STANDARD',
  ECONOMIC = 'ECONOMIC',
}

export interface RateOffer {
  rate: CsvRate;
  serviceLevel: ServiceLevel;
  priceMultiplier: number;
  adjustedTransitDays: number;
  originalTransitDays: number;
  priceAdjustmentPercent: number;
  transitAdjustmentPercent: number;
  description: string;
}

interface ServiceLevelConfig {
  priceMultiplier: number;
  transitMultiplier: number;
  description: string;
}

/**
 * Generates RAPID / STANDARD / ECONOMIC variants for a given CSV rate.
 *
 * Price adjustment is applied to the total calculated price in the search service —
 * this service only stores the multiplier and the adjusted transit time.
 */
export class RateOfferGeneratorService {
  private readonly SERVICE_LEVEL_CONFIGS: Record<ServiceLevel, ServiceLevelConfig> = {
    [ServiceLevel.RAPID]: {
      priceMultiplier: 1.2,
      transitMultiplier: 0.7,
      description: 'Express — Livraison rapide avec service prioritaire',
    },
    [ServiceLevel.STANDARD]: {
      priceMultiplier: 1.0,
      transitMultiplier: 1.0,
      description: 'Standard — Service régulier au meilleur rapport qualité/prix',
    },
    [ServiceLevel.ECONOMIC]: {
      priceMultiplier: 0.85,
      transitMultiplier: 1.5,
      description: 'Économique — Tarif réduit avec délai étendu',
    },
  };

  private readonly MIN_TRANSIT_DAYS = 5;
  private readonly MAX_TRANSIT_DAYS = 90;

  generateOffers(rate: CsvRate): RateOffer[] {
    const offers: RateOffer[] = [];

    for (const serviceLevel of Object.values(ServiceLevel)) {
      const config = this.SERVICE_LEVEL_CONFIGS[serviceLevel];
      const rawTransit = rate.transitDays * config.transitMultiplier;
      const adjustedTransitDays = this.clampTransit(Math.round(rawTransit));

      offers.push({
        rate,
        serviceLevel,
        priceMultiplier: config.priceMultiplier,
        adjustedTransitDays,
        originalTransitDays: rate.transitDays,
        priceAdjustmentPercent: Math.round((config.priceMultiplier - 1) * 100),
        transitAdjustmentPercent: Math.round((config.transitMultiplier - 1) * 100),
        description: config.description,
      });
    }

    // ECONOMIC → STANDARD → RAPID (cheapest first)
    return offers.sort((a, b) => a.priceMultiplier - b.priceMultiplier);
  }

  generateOffersForRates(rates: CsvRate[]): RateOffer[] {
    return rates.flatMap(rate => this.generateOffers(rate));
  }

  generateOffersForServiceLevel(rates: CsvRate[], serviceLevel: ServiceLevel): RateOffer[] {
    return rates
      .map(rate => this.generateOffers(rate).find(o => o.serviceLevel === serviceLevel)!)
      .filter(Boolean);
  }

  getBestOffersPerServiceLevel(rates: CsvRate[]): {
    rapid: RateOffer | null;
    standard: RateOffer | null;
    economic: RateOffer | null;
  } {
    return {
      rapid: this.generateOffersForServiceLevel(rates, ServiceLevel.RAPID)[0] ?? null,
      standard: this.generateOffersForServiceLevel(rates, ServiceLevel.STANDARD)[0] ?? null,
      economic: this.generateOffersForServiceLevel(rates, ServiceLevel.ECONOMIC)[0] ?? null,
    };
  }

  isRateEligible(rate: CsvRate): boolean {
    if (rate.transitDays <= 0) return false;
    // A rate is usable if it has a freight rate or at least a freight minimum
    if (rate.freight.freightRatePerCBM <= 0 && rate.freight.freightMinimum <= 0) return false;
    if (!rate.isValidForDate(new Date())) return false;
    return true;
  }

  filterEligibleRates(rates: CsvRate[]): CsvRate[] {
    return rates.filter(rate => this.isRateEligible(rate));
  }

  private clampTransit(days: number): number {
    return Math.max(this.MIN_TRANSIT_DAYS, Math.min(this.MAX_TRANSIT_DAYS, days));
  }
}
