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

export interface PriceCalculationParams {
  volumeCBM: number;
  weightKG: number;
  hasDangerousGoods?: boolean;
}

export interface FobBreakdown {
  documentation: number;
  isps: number;
  handling: number;
  solas: number;
  customs: number;
  ams_aci: number;
  isf5: number;
  dgAdmin: number;
}

export type DgSurchargeStatus = 'computed' | 'on_request' | 'not_accepted';

export interface PriceBreakdown {
  // Freight (in freightCurrency)
  freightCharge: number;
  freightCurrency: string;

  // FOB charges (in fobCurrency)
  fobFixed: number; // doc + ISPS + solas + customs + AMS_ACI + ISF5
  fobHandling: number;
  fobDG: number; // fobDGAdmin only if DG
  fobCurrency: string;
  fobBreakdown: FobBreakdown;

  // DG surcharge (fobCurrency or dgSurchargeCurrency)
  dgSurchargeAmount: number | null; // null when on_request or not_accepted
  dgSurchargeCurrency: string;
  dgSurchargeStatus: DgSurchargeStatus;

  // Totals (each in their own currency)
  totalFreight: number; // = freightCharge in freightCurrency
  totalFob: number; // = fobFixed + fobHandling + fobDG + dgSurcharge in fobCurrency

  // Used for sorting/comparison only — naive sum treating both currencies as equal
  // Callers should be aware of potential currency mismatch
  totalPriceForSorting: number;
  primaryCurrency: string;
}

/**
 * Calculates price for a CSV rate given volume and weight.
 *
 * Formula:
 *   Fret       = max(freightRatePerCBM × V, freightMinimum)
 *   Handling   = max(fobHandling × max(V, W_tonnes), fobHandlingMinimum)  [if unit=W]
 *              = max(fobHandling × V, fobHandlingMinimum)                  [if unit=UP]
 *   FOB fixed  = doc + ISPS + solas + customs + AMS_ACI + ISF5
 *   Total      = Fret (freightCurrency) + FOB_fixed + Handling (fobCurrency)
 */
export class CsvRatePriceCalculatorService {
  calculatePrice(rate: CsvRate, params: PriceCalculationParams): PriceBreakdown {
    const V = params.volumeCBM;
    const W = params.weightKG / 1000; // convert KG → tonnes for W unit
    const isDG = params.hasDangerousGoods ?? false;

    // 1. Freight charge
    const freightCharge =
      rate.freight.freightRatePerCBM > 0
        ? Math.max(rate.freight.freightRatePerCBM * V, rate.freight.freightMinimum)
        : rate.freight.freightMinimum;

    // 2. Handling — "W" = tonne revenue (max of CBM and tonnes), "UP" = per CBM
    const handlingBase = rate.fob.fobHandlingUnit === 'W' ? Math.max(V, W) : V;
    const fobHandling = Math.max(rate.fob.fobHandling * handlingBase, rate.fob.fobHandlingMinimum);

    // 3. FOB fixed charges
    const fobFixed =
      rate.fob.fobDocumentation +
      rate.fob.fobISPS +
      rate.fob.fobSolas +
      rate.fob.fobCustoms +
      rate.fob.fobAMS_ACI +
      rate.fob.fobISF5;

    // 4. DG admin (FOB currency, only if DG)
    const fobDG = isDG ? rate.fob.fobDGAdmin : 0;

    // 5. DG surcharge (own currency, only if DG)
    let dgSurchargeAmount: number | null = null;
    let dgSurchargeStatus: DgSurchargeStatus = 'computed';

    if (isDG) {
      const dgRate = rate.dgSurcharge.dgSurchargeRate;
      if (dgRate === 'NOT ACCEPTED') {
        dgSurchargeStatus = 'not_accepted';
      } else if (dgRate === 'ON REQUEST') {
        dgSurchargeStatus = 'on_request';
      } else {
        dgSurchargeStatus = 'computed';
        const dgNum = typeof dgRate === 'number' ? dgRate : parseFloat(String(dgRate));
        let rawDG = 0;
        switch (rate.dgSurcharge.dgSurchargeUnit) {
          case 'UP':
            rawDG = dgNum * V;
            break;
          case 'LS':
            rawDG = dgNum;
            break;
          case '%':
            rawDG = freightCharge * (dgNum / 100);
            break;
        }
        const dgMin =
          typeof rate.dgSurcharge.dgSurchargeMin === 'number' ? rate.dgSurcharge.dgSurchargeMin : 0;
        dgSurchargeAmount = Math.max(rawDG, dgMin);
      }
    }

    // 6. Total FOB (in fobCurrency)
    const totalFob = fobFixed + fobHandling + fobDG + (dgSurchargeAmount ?? 0);

    // 7. Naive sum for sorting (ignores currency differences)
    const totalPriceForSorting = freightCharge + totalFob;

    return {
      freightCharge: round2(freightCharge),
      freightCurrency: rate.freight.freightCurrency,
      fobFixed: round2(fobFixed),
      fobHandling: round2(fobHandling),
      fobDG: round2(fobDG),
      fobCurrency: rate.fob.fobCurrency,
      fobBreakdown: {
        documentation: rate.fob.fobDocumentation,
        isps: rate.fob.fobISPS,
        handling: round2(fobHandling),
        solas: rate.fob.fobSolas,
        customs: rate.fob.fobCustoms,
        ams_aci: rate.fob.fobAMS_ACI,
        isf5: rate.fob.fobISF5,
        dgAdmin: isDG ? rate.fob.fobDGAdmin : 0,
      },
      dgSurchargeAmount: dgSurchargeAmount !== null ? round2(dgSurchargeAmount) : null,
      dgSurchargeCurrency: rate.dgSurcharge.dgSurchargeCurrency,
      dgSurchargeStatus,
      totalFreight: round2(freightCharge),
      totalFob: round2(totalFob),
      totalPriceForSorting: round2(totalPriceForSorting),
      primaryCurrency: rate.freight.freightCurrency,
    };
  }
}

function round2(n: number): number {
  return Math.round(n * 100) / 100;
}
