import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  ChannelOrderMappingFailedError,
  InvalidChannelOrderStatusError,
} from "../lib/errors.generated";
import {
  baseChannelOrder,
  baseCurrency,
  baseListing,
  baseSalesOrder,
  baseSalesOrderLine,
} from "../testing/fixtures";
import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
import { run } from "./convertChannelOrder";

describe("convertChannelOrder", () => {
  it("converts a pending channel order into a sales order", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const createdSalesOrder = { ...baseSalesOrder, id: "sales-order-converted" };
    const convertedChannelOrder = {
      ...baseChannelOrder,
      salesOrderId: createdSalesOrder.id,
      status: "CONVERTED",
      updatedAt: new Date("2024-01-02T00:00:00.000Z"),
    };
    spies.select
      .mockReturnValueOnce(baseChannelOrder)
      .mockReturnValueOnce(baseCurrency)
      .mockReturnValueOnce(baseCurrency)
      .mockReturnValueOnce(baseListing);
    spies.insert
      .mockReturnValueOnce(createdSalesOrder)
      .mockReturnValueOnce({ ...baseSalesOrderLine, salesOrderId: createdSalesOrder.id });
    spies.update.mockReturnValueOnce(convertedChannelOrder);

    const result = await run(db, { channelOrderId: baseChannelOrder.id }, commandCtx);

    const value = expectOk(result);
    expect(value.salesOrder.id).toBe(createdSalesOrder.id);
    expect(spies.values).toHaveBeenNthCalledWith(
      1,
      expect.objectContaining({
        companyId: baseChannelOrder.companyId,
        customerId: baseChannelOrder.customerId,
      }),
    );
  });

  it("stores linked sales order reference after conversion", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const createdSalesOrder = { ...baseSalesOrder, id: "sales-order-converted" };
    const convertedChannelOrder = {
      ...baseChannelOrder,
      salesOrderId: createdSalesOrder.id,
      status: "CONVERTED",
      updatedAt: new Date("2024-01-02T00:00:00.000Z"),
    };
    spies.select
      .mockReturnValueOnce(baseChannelOrder)
      .mockReturnValueOnce(baseCurrency)
      .mockReturnValueOnce(baseCurrency)
      .mockReturnValueOnce(baseListing);
    spies.insert
      .mockReturnValueOnce(createdSalesOrder)
      .mockReturnValueOnce({ ...baseSalesOrderLine, salesOrderId: createdSalesOrder.id });
    spies.update.mockReturnValueOnce(convertedChannelOrder);

    const result = await run(db, { channelOrderId: baseChannelOrder.id }, commandCtx);

    const value = expectOk(result);
    expect(value.channelOrder.salesOrderId).toBe(createdSalesOrder.id);
    expect(spies.set).toHaveBeenCalledWith(
      expect.objectContaining({
        salesOrderId: createdSalesOrder.id,
        status: "CONVERTED",
      }),
    );
  });

  it("throws when channel order is not pending conversion", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValueOnce({ ...baseChannelOrder, status: "EXCEPTION" });

    const result = await run(db, { channelOrderId: baseChannelOrder.id }, commandCtx);

    expectErr(result, InvalidChannelOrderStatusError);
  });

  it("throws when required mappings are missing", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const channelOrder = {
      ...baseChannelOrder,
      normalizedPayload: JSON.stringify({
        customerId: baseChannelOrder.customerId,
        lines: [
          {
            itemId: baseListing.itemId,
            listingId: "missing-listing",
            quantity: 1,
            unitPrice: 100,
            currencyId: baseCurrency.id,
          },
        ],
      }),
    };
    spies.select
      .mockReturnValueOnce(channelOrder)
      .mockReturnValueOnce(baseCurrency)
      .mockReturnValueOnce(baseCurrency)
      .mockReturnValueOnce(undefined);

    const result = await run(db, { channelOrderId: baseChannelOrder.id }, commandCtx);

    expectErr(result, ChannelOrderMappingFailedError);
  });
});
