import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { DB } from "../generated/kysely-tailordb";
import { InvalidPricingContextError } from "../lib/errors.generated";
import {
  baseCompany,
  baseItem,
  baseCurrency,
  baseUnit,
  basePriceList,
  basePriceRule,
  baseCustomer,
  baseChannel,
} from "../testing/fixtures";
import { expectErr, expectOk } from "../testing/commandTestUtils";
import { run } from "./getApplicableSalesPrice";

describe("getApplicableSalesPrice", () => {
  it("returns the highest-precedence matching price rule", async () => {
    const { db, spies } = createMockDb<DB>();
    // First execute() → SalesPriceList, second execute() → SalesPriceRule
    spies.select.mockReturnValueOnce([basePriceList]);
    spies.select.mockReturnValueOnce([basePriceRule]);

    const result = await run(db, {
      companyId: baseCompany.id,
      itemId: baseItem.id,
      customerId: baseCustomer.id,
      channelId: baseChannel.id,
      currencyId: baseCurrency.id,
      unitId: baseUnit.id,
      quantity: 5,
      pricingDate: new Date("2024-06-01T00:00:00.000Z"),
    });
    const { priceSuggestion } = expectOk<{
      priceSuggestion: { ruleId: string; unitPrice: number } | null;
    }>(result as never);

    expect(priceSuggestion).not.toBeNull();
    expect(priceSuggestion!.ruleId).toBe(basePriceRule.id);
    expect(priceSuggestion!.unitPrice).toBe(100);
  });

  it("returns unmatched result when no active rule applies", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce([basePriceList]);
    spies.select.mockReturnValueOnce([]);

    const result = await run(db, {
      companyId: baseCompany.id,
      itemId: baseItem.id,
      customerId: baseCustomer.id,
      channelId: baseChannel.id,
      currencyId: baseCurrency.id,
      unitId: baseUnit.id,
      quantity: 5,
      pricingDate: new Date("2024-06-01T00:00:00.000Z"),
    });
    const { priceSuggestion } = expectOk<{
      priceSuggestion: { ruleId: string; unitPrice: number } | null;
    }>(result as never);

    expect(priceSuggestion).toBeNull();
  });

  it("resolves quantity-break pricing with compatible UoM conversion", async () => {
    const { db, spies } = createMockDb<DB>();
    const higherBreakRule = {
      ...basePriceRule,
      id: "price-rule-high",
      minimumQuantity: 10,
      unitPrice: 80,
      priority: 20,
    };
    spies.select.mockReturnValueOnce([basePriceList]);
    spies.select.mockReturnValueOnce([basePriceRule, higherBreakRule]);

    const result = await run(db, {
      companyId: baseCompany.id,
      itemId: baseItem.id,
      customerId: baseCustomer.id,
      channelId: baseChannel.id,
      currencyId: baseCurrency.id,
      unitId: baseUnit.id,
      quantity: 15,
      pricingDate: new Date("2024-06-01T00:00:00.000Z"),
    });
    const { priceSuggestion } = expectOk<{
      priceSuggestion: { ruleId: string; unitPrice: number } | null;
    }>(result as never);

    expect(priceSuggestion).not.toBeNull();
    expect(priceSuggestion!.ruleId).toBe("price-rule-high");
    expect(priceSuggestion!.unitPrice).toBe(80);
  });

  it("throws when required pricing context is missing", async () => {
    const { db } = createMockDb<DB>();

    const result = await run(db, {
      companyId: "",
      itemId: baseItem.id,
      currencyId: baseCurrency.id,
      unitId: baseUnit.id,
      quantity: 5,
    });

    expectErr(result as never, InvalidPricingContextError);
  });
});
