import { createKyselyMock } from "@tailor-platform/sdk/vitest";
import { describe, expect, it } from "vitest";
import type { Namespace } from "../generated/kysely-tailordb";
import {
  ChannelOrderMappingFailedError,
  InvalidChannelOrderStatusError,
} from "../lib/errors.generated";
import { commandCtx, expectErr, expectOk } from "../testing/commandTestUtils";
import {
  baseChannelOrder,
  baseCurrency,
  baseListing,
  baseSalesOrder,
  baseSalesOrderLine,
} from "../testing/fixtures";
import { mockPrimitivesQueries } from "../testing/moduleMocks";
import { run } from "./convertChannelOrder";

describe("convertChannelOrder", () => {
  it("converts a pending channel order into a sales order", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const createdSalesOrder = { ...baseSalesOrder, id: "sales-order-converted" };
    const convertedChannelOrder = {
      ...baseChannelOrder,
      salesOrderId: createdSalesOrder.id,
      status: "CONVERTED",
      updatedAt: new Date("2024-01-02T00:00:00.000Z"),
    };
    mock.enqueueResults(
      [baseChannelOrder],
      [baseListing],
      [createdSalesOrder],
      [{ ...baseSalesOrderLine, salesOrderId: createdSalesOrder.id }],
      [convertedChannelOrder],
    );

    const result = await mock.withTx((trx) =>
      run(
        trx,
        { channelOrderId: baseChannelOrder.id },
        commandCtx,
        mockPrimitivesQueries(baseCurrency),
      ),
    );

    const value = expectOk(result);
    expect(value.salesOrder.id).toBe(createdSalesOrder.id);
    expect(mock.inserts[0].insertValues()).toEqual(
      expect.objectContaining({
        companyId: baseChannelOrder.companyId,
        customerId: baseChannelOrder.customerId,
      }),
    );
  });

  it("stores linked sales order reference after conversion", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const createdSalesOrder = { ...baseSalesOrder, id: "sales-order-converted" };
    const convertedChannelOrder = {
      ...baseChannelOrder,
      salesOrderId: createdSalesOrder.id,
      status: "CONVERTED",
      updatedAt: new Date("2024-01-02T00:00:00.000Z"),
    };
    mock.enqueueResults(
      [baseChannelOrder],
      [baseListing],
      [createdSalesOrder],
      [{ ...baseSalesOrderLine, salesOrderId: createdSalesOrder.id }],
      [convertedChannelOrder],
    );

    const result = await mock.withTx((trx) =>
      run(
        trx,
        { channelOrderId: baseChannelOrder.id },
        commandCtx,
        mockPrimitivesQueries(baseCurrency),
      ),
    );

    const value = expectOk(result);
    expect(value.channelOrder.salesOrderId).toBe(createdSalesOrder.id);
    expect(mock.updates[0].updateValues()).toEqual(
      expect.objectContaining({
        salesOrderId: createdSalesOrder.id,
        status: "CONVERTED",
      }),
    );
  });

  it("throws when channel order is not pending conversion", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([{ ...baseChannelOrder, status: "EXCEPTION" }]);

    const result = await mock.withTx((trx) =>
      run(
        trx,
        { channelOrderId: baseChannelOrder.id },
        commandCtx,
        mockPrimitivesQueries(baseCurrency),
      ),
    );

    expectErr(result, InvalidChannelOrderStatusError);
  });

  it("throws when required mappings are missing", async () => {
    const mock = createKyselyMock<Namespace["main-db"]>();
    const channelOrder = {
      ...baseChannelOrder,
      normalizedPayload: JSON.stringify({
        customerId: baseChannelOrder.customerId,
        lines: [
          {
            itemId: baseListing.itemId,
            listingId: "missing-listing",
            quantity: 1,
            unitPrice: 100,
            currencyId: baseCurrency.id,
          },
        ],
      }),
    };
    mock.enqueueResults([channelOrder], []);

    const result = await mock.withTx((trx) =>
      run(
        trx,
        { channelOrderId: baseChannelOrder.id },
        commandCtx,
        mockPrimitivesQueries(baseCurrency),
      ),
    );

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