import { DataSource } from 'typeorm';

type MileRecord = {
  bookingId?: string | null;
  advancedPayment?: number | string | null;
  booking?: {
    cargoTotalWeightVgm?: number | string | null;
    bookingContainers?: Array<{
      units?: Array<{ vgmTons?: number | string | null }> | null;
    }> | null;
    /** Attached here: the train schedule the booking rides, for mile alignment. */
    trainSchedule?: { trainNumber: string | null; departureDate: string | null } | null;
  } | null;
};

/**
 * Display enrichment for first/last-mile lists (Assign Vehicle modal etc.):
 *  - Advance payment: mile records are created with advanced_payment 0 — the
 *    real advance is the FIRST_MILE/LAST_MILE line the customer already paid
 *    on the booking invoice.
 *  - Cargo tons: container bookings often carry tonnage on the per-unit VGMs
 *    while cargo_total_weight_vgm stays 0 — fall back to the summed units.
 * Fills both in-memory on the loaded records; nothing is persisted.
 */
export async function attachMileFinancials(
  dataSource: DataSource,
  records: MileRecord[],
  chargeType: 'FIRST_MILE' | 'LAST_MILE',
): Promise<void> {
  for (const r of records) {
    const b = r.booking;
    if (!b || Number(b.cargoTotalWeightVgm) > 0) continue;
    const unitTons = (b.bookingContainers ?? []).reduce(
      (sum, bc) =>
        sum + (bc.units ?? []).reduce((s, u) => s + (Number(u.vgmTons) || 0), 0),
      0,
    );
    if (unitTons > 0) b.cargoTotalWeightVgm = Number(unitTons.toFixed(3));
  }

  // Train alignment: which schedule each booking rides (mile pickups/deliveries
  // are planned against the train's departure).
  const bookingIds = [...new Set(records.map((r) => r.bookingId).filter(Boolean))] as string[];
  if (bookingIds.length) {
    const schedules: Array<{
      bookingId: string;
      trainNumber: string | null;
      departureDate: string | null;
    }> = await dataSource.query(
      `SELECT DISTINCT ON (tsb.booking_id)
              tsb.booking_id AS "bookingId",
              ts.train_number AS "trainNumber",
              COALESCE(ts.actual_departure_at, ts.scheduled_departure_date)::text AS "departureDate"
         FROM freight.train_schedule_bookings tsb
         JOIN freight.train_schedules ts
           ON ts.id = tsb.train_schedule_id AND ts.deleted_at IS NULL
        WHERE tsb.booking_id = ANY($1::uuid[]) AND tsb.deleted_at IS NULL
        ORDER BY tsb.booking_id, tsb.created_at DESC`,
      [bookingIds],
    );
    const byBookingSchedule = new Map(schedules.map((s) => [s.bookingId, s]));
    for (const r of records) {
      const s = r.bookingId ? byBookingSchedule.get(r.bookingId) : undefined;
      if (r.booking && s) {
        r.booking.trainSchedule = {
          trainNumber: s.trainNumber,
          departureDate: s.departureDate,
        };
      }
    }
  }

  const needAdvance = records.filter(
    (r) => r.bookingId && !(Number(r.advancedPayment) > 0),
  );
  if (!needAdvance.length) return;

  const rows: Array<{ bookingId: string; amount: string }> = await dataSource.query(
    `SELECT i.source_id AS "bookingId", SUM(il.amount) AS amount
       FROM freight.invoice_lines il
       JOIN freight.invoices i ON i.id = il.invoice_id AND i.deleted_at IS NULL
      WHERE i.source = 'booking'
        AND i.status = 'PAID'
        AND i.source_id = ANY($1::text[])
        AND il.charge_type = $2
        AND il.deleted_at IS NULL
      GROUP BY i.source_id`,
    [needAdvance.map((r) => r.bookingId), chargeType],
  );
  const byBooking = new Map(rows.map((r) => [r.bookingId, Number(r.amount)]));
  for (const r of needAdvance) {
    const paid = byBooking.get(r.bookingId as string);
    if (paid) r.advancedPayment = paid;
  }
}
