import { describe, expect, it } from "vitest";
import type { Freight } from "@edr/types";
import { computeShipmentTotal } from "./total";
import type { ShipmentFormValues } from "./schema";

// The with_return surcharge is contract-gated and priced per returning
// container; it was previously omitted from the estimate entirely.
const contract = (over: Partial<Freight.IContract> = {}) =>
  ({
    freightType: "CONTAINER",
    paymentCurrency: "ETB",
    isHazardous: true,
    isReefer: true,
    equipmentReturn: "WITH_RETURN",
    pricingBreakdown: {
      currency: "ETB",
      lineItems: [
        {
          label: "40ft container",
          unit: "per_container",
          unitPrice: 100,
          containerSize: "40ft",
        },
        { label: "Hazardous", unit: "per_container", unitPrice: 10, conditionalOn: "is_hazardous" },
        { label: "Reefer", unit: "per_container", unitPrice: 20, conditionalOn: "is_reefer" },
        { label: "Empty return", unit: "per_container", unitPrice: 30, conditionalOn: "with_return" },
      ],
    },
    ...over,
  }) as unknown as Freight.IContract;

const values = (over: Record<string, unknown> = {}) =>
  ({
    containers: [
      {
        containerSize: "40ft",
        quantity: "10",
        hazardousQuantity: "0",
        reeferQuantity: "0",
        returnQuantity: "0",
        units: [],
      },
    ],
    cargoWeightTons: "",
    itemCount: "",
    bulkHazardousQuantity: "0",
    bulkReeferQuantity: "0",
    ...over,
  }) as unknown as ShipmentFormValues;

const line = (t: ReturnType<typeof computeShipmentTotal>, label: string) =>
  t.lines.find((l) => l.label === label);

describe("computeShipmentTotal — with_return surcharge", () => {
  it("charges the return surcharge per returning container", () => {
    const t = computeShipmentTotal(
      contract(),
      values({
        containers: [
          {
            containerSize: "40ft",
            quantity: "10",
            hazardousQuantity: "0",
            reeferQuantity: "0",
            returnQuantity: "4",
            units: [],
          },
        ],
      }),
    );
    expect(line(t, "Empty return")).toMatchObject({ quantity: 4, amount: 120 });
    expect(t.total).toBe(100 * 10 + 30 * 4);
  });

  it("omits it when no container returns", () => {
    const t = computeShipmentTotal(contract(), values());
    expect(line(t, "Empty return")).toBeUndefined();
    expect(t.total).toBe(1000);
  });

  it("omits it when the contract is not WITH_RETURN", () => {
    const t = computeShipmentTotal(
      contract({ equipmentReturn: "WITHOUT_RETURN" } as Partial<Freight.IContract>),
      values({
        containers: [
          {
            containerSize: "40ft",
            quantity: "10",
            hazardousQuantity: "0",
            reeferQuantity: "0",
            returnQuantity: "4",
            units: [],
          },
        ],
      }),
    );
    expect(line(t, "Empty return")).toBeUndefined();
    expect(t.total).toBe(1000);
  });

  it("sums return counts across container lines and stacks with haz/reefer", () => {
    const t = computeShipmentTotal(
      contract(),
      values({
        containers: [
          {
            containerSize: "40ft",
            quantity: "10",
            hazardousQuantity: "2",
            reeferQuantity: "3",
            returnQuantity: "4",
            units: [],
          },
          {
            containerSize: "40ft",
            quantity: "5",
            hazardousQuantity: "0",
            reeferQuantity: "0",
            returnQuantity: "1",
            units: [],
          },
        ],
      }),
    );
    expect(line(t, "Empty return")).toMatchObject({ quantity: 5, amount: 150 });
    expect(line(t, "Hazardous")).toMatchObject({ quantity: 2, amount: 20 });
    expect(line(t, "Reefer")).toMatchObject({ quantity: 3, amount: 60 });
    expect(t.total).toBe(100 * 10 + 100 * 5 + 20 + 60 + 150);
  });

  it("does not double-count the base rate against the return surcharge", () => {
    const t = computeShipmentTotal(
      contract(),
      values({
        containers: [
          {
            containerSize: "40ft",
            quantity: "2",
            hazardousQuantity: "0",
            reeferQuantity: "0",
            returnQuantity: "2",
            units: [],
          },
        ],
      }),
    );
    expect(line(t, "40ft container")).toMatchObject({ quantity: 2, amount: 200 });
    expect(t.total).toBe(200 + 60);
  });
});
