import { ok } from "@tailor-platform/erp-kit/core";
import type {
  BusinessPartnerQueries,
  InventoryCommands,
  InventoryQueries,
  ItemManagementQueries,
  OrganizationQueries,
  PrimitivesQueries,
} from "../module";

export type Company = NonNullable<
  Awaited<ReturnType<OrganizationQueries["getCompany"]>>["value"]["company"]
>;
export type Site = NonNullable<
  Awaited<ReturnType<OrganizationQueries["getSite"]>>["value"]["site"]
>;
export type Partner = NonNullable<
  Awaited<ReturnType<BusinessPartnerQueries["getPartner"]>>["value"]["partner"]
>;
export type PartnerRole = NonNullable<
  Awaited<ReturnType<BusinessPartnerQueries["getPartnerRole"]>>["value"]["partnerRole"]
>;
export type PartnerIdentification = Awaited<
  ReturnType<BusinessPartnerQueries["listPartnerIdentificationsByPartner"]>
>["value"]["items"][number];
export type Item = NonNullable<
  Awaited<ReturnType<ItemManagementQueries["getItem"]>>["value"]["item"]
>;
export type Currency = NonNullable<
  Awaited<ReturnType<PrimitivesQueries["getCurrency"]>>["value"]["currency"]
>;
export type Unit = NonNullable<Awaited<ReturnType<PrimitivesQueries["getUnit"]>>["value"]["unit"]>;
export type SupplyPlan = NonNullable<
  Awaited<ReturnType<InventoryQueries["getInventorySupplyPlan"]>>["value"]["supplyPlan"]
>;
export type ReceiptEvidence = Awaited<
  ReturnType<InventoryQueries["listPurchaseReceiptEvidenceForMatching"]>
>["value"]["receiptEvidence"][number];

export const mockOrgQueries = (
  overrides: { company?: Company | null; site?: Site | null } = {},
): Pick<OrganizationQueries, "getCompany" | "getSite"> => ({
  getCompany: () => Promise.resolve(ok({ company: overrides.company ?? null })),
  getSite: () => Promise.resolve(ok({ site: overrides.site ?? null })),
});

export const mockBPQueries = (
  overrides: {
    partner?: Partner | null;
    partnerRole?: PartnerRole | null;
    identifications?: PartnerIdentification[];
  } = {},
): Pick<
  BusinessPartnerQueries,
  "getPartner" | "getPartnerRole" | "listPartnerIdentificationsByPartner"
> => ({
  getPartner: () => Promise.resolve(ok({ partner: overrides.partner ?? null })),
  getPartnerRole: () => Promise.resolve(ok({ partnerRole: overrides.partnerRole ?? null })),
  listPartnerIdentificationsByPartner: () =>
    Promise.resolve(ok({ items: overrides.identifications ?? [], hasNextPage: false })),
});

export const mockItemQueries = (
  overrides: { item?: Item | null } = {},
): Pick<ItemManagementQueries, "getItem"> => ({
  getItem: () => Promise.resolve(ok({ item: overrides.item ?? null })),
});

export const mockPrimitivesQueries = (
  overrides: { currency?: Currency | null; unit?: Unit | null } = {},
): Pick<PrimitivesQueries, "getCurrency" | "getUnit"> => ({
  getCurrency: () => Promise.resolve(ok({ currency: overrides.currency ?? null })),
  getUnit: () => Promise.resolve(ok({ unit: overrides.unit ?? null })),
});

function buildSupplyPlan(input: Partial<SupplyPlan> = {}): SupplyPlan {
  return {
    id: input.id ?? "supply-plan-1",
    sourceType: input.sourceType ?? "PURCHASE_ORDER",
    sourceId: input.sourceId ?? "purchase-order-1",
    sourceLineId: input.sourceLineId ?? "po-line-1",
    itemId: input.itemId ?? "item-1",
    siteId: input.siteId ?? "site-1",
    expectedQuantity: input.expectedQuantity ?? "10",
    receivedQuantity: input.receivedQuantity ?? "0",
    status: input.status ?? "OPEN",
    expectedDate: input.expectedDate ?? new Date("2024-01-01T00:00:00.000Z"),
    lotId: input.lotId ?? null,
    serialNumberId: input.serialNumberId ?? null,
    openQuantity: input.openQuantity ?? "0",
    createdAt: input.createdAt ?? new Date("2024-01-01T00:00:00.000Z"),
    updatedAt: input.updatedAt ?? null,
  };
}

export const mockInventoryCommands = (): Pick<
  InventoryCommands,
  "createInventorySupplyPlan" | "updateInventorySupplyPlan" | "closeInventorySupplyPlan"
> => ({
  createInventorySupplyPlan: (_db, input) =>
    Promise.resolve(
      ok({
        supplyPlans: input.supplyPlans.map((supplyPlanInput, index) => ({
          ...buildSupplyPlan({
            id: `supply-plan-${index + 1}`,
            sourceType: input.sourceType,
            sourceId: input.sourceId,
          }),
          ...supplyPlanInput,
        })),
      }),
    ),
  updateInventorySupplyPlan: (_db, input) =>
    Promise.resolve(
      ok({
        supplyPlan: buildSupplyPlan({
          id: input.id,
          sourceType: input.sourceType,
          sourceId: input.sourceId,
          sourceLineId: input.sourceLineId,
          itemId: input.itemId,
          siteId: input.siteId,
          expectedQuantity: input.expectedQuantity,
          lotId: input.lotId,
          serialNumberId: input.serialNumberId,
        }),
      }),
    ),
  closeInventorySupplyPlan: (_db, input) =>
    Promise.resolve(
      input.target === "SOURCE_DOCUMENT" || input.target === "SOURCE_LINES"
        ? ok({ supplyPlans: [] })
        : ok({ supplyPlan: buildSupplyPlan({ id: input.id, status: "CLOSED" }) }),
    ),
});

export const mockInventoryQueries = (
  overrides: { receiptEvidence?: ReceiptEvidence[]; supplyPlans?: Partial<SupplyPlan>[] } = {},
): Pick<
  InventoryQueries,
  "getInventorySupplyPlan" | "listInventorySupplyPlans" | "listPurchaseReceiptEvidenceForMatching"
> => ({
  getInventorySupplyPlan: () => {
    const supplyPlan = overrides.supplyPlans?.[0];
    return Promise.resolve(
      supplyPlan ? ok({ supplyPlan: buildSupplyPlan(supplyPlan) }) : ok({ supplyPlan: null }),
    );
  },
  listInventorySupplyPlans: () =>
    Promise.resolve(
      ok({
        items: (overrides.supplyPlans ?? []).map((plan) => buildSupplyPlan(plan)),
        hasNextPage: false,
      }),
    ),
  listPurchaseReceiptEvidenceForMatching: () =>
    Promise.resolve(ok({ receiptEvidence: overrides.receiptEvidence ?? [] })),
});
