import { ok } from "@tailor-platform/erp-kit/core";
import { vi } from "vitest";
import type {
  AccountingEventCommands,
  BusinessPartnerQueries,
  CoaManagementQueries,
  InventoryQueries,
  OrganizationQueries,
  PrimitivesQueries,
  PurchaseCommands,
  PurchaseQueries,
} from "../module";

export type Company = NonNullable<
  Awaited<ReturnType<OrganizationQueries["getCompany"]>>["value"]["company"]
>;
export type Partner = NonNullable<
  Awaited<ReturnType<BusinessPartnerQueries["getPartner"]>>["value"]["partner"]
>;
export type Currency = NonNullable<
  Awaited<ReturnType<PrimitivesQueries["getCurrency"]>>["value"]["currency"]
>;
export type Account = NonNullable<
  Awaited<ReturnType<CoaManagementQueries["getAccount"]>>["value"]["account"]
>;
export type PurchaseOrderLineForMatching = Awaited<
  ReturnType<PurchaseQueries["listPurchaseOrderLinesForMatching"]>
>["value"]["purchaseOrderLines"][number];
export type PurchaseReceiptQuantityForMatching = Awaited<
  ReturnType<InventoryQueries["listPurchaseReceiptQuantitiesForMatching"]>
>["value"]["receiptQuantities"][number];

export const mockOrganizationQueries = (store: { companies?: Company[] }) => ({
  getCompany: vi.fn<OrganizationQueries["getCompany"]>((_db, input) =>
    Promise.resolve(
      ok({ company: store.companies?.find((company) => company.id === input.id) ?? null }),
    ),
  ),
});

export const mockBusinessPartnerQueries = (store: { partners?: Partner[] }) => ({
  getPartner: vi.fn<BusinessPartnerQueries["getPartner"]>((_db, input) =>
    Promise.resolve(
      ok({ partner: store.partners?.find((partner) => partner.id === input.id) ?? null }),
    ),
  ),
});

export const mockPrimitivesQueries = (store: { currencies?: Currency[] }) => ({
  getCurrency: vi.fn<PrimitivesQueries["getCurrency"]>((_db, input) =>
    Promise.resolve(
      ok({
        currency:
          ("id" in input
            ? store.currencies?.find((currency) => currency.id === input.id)
            : store.currencies?.find((currency) => currency.code === input.code)) ?? null,
      }),
    ),
  ),
});

export const mockCoaManagementQueries = (store: { accounts?: Account[] }) => ({
  getAccount: vi.fn<CoaManagementQueries["getAccount"]>((_db, input) =>
    Promise.resolve(
      ok({
        account:
          ("id" in input
            ? store.accounts?.find((account) => account.id === input.id)
            : store.accounts?.find(
                (account) => account.companyId === input.companyId && account.code === input.code,
              )) ?? null,
      }),
    ),
  ),
  listAccounts: vi.fn<CoaManagementQueries["listAccounts"]>((_db, input) =>
    Promise.resolve(
      ok({
        items: (store.accounts ?? []).filter(
          (account) =>
            account.companyId === input.companyId &&
            (!input.accountIds || input.accountIds.includes(account.id)) &&
            (!input.accountType || account.accountType === input.accountType) &&
            (!input.status || account.status === input.status),
        ),
        hasNextPage: false,
      }),
    ),
  ),
});

export const mockPurchaseQueries = (store: {
  purchaseOrderLines?: PurchaseOrderLineForMatching[];
}) => ({
  listPurchaseOrderLinesForMatching: vi.fn<PurchaseQueries["listPurchaseOrderLinesForMatching"]>(
    (_db, input) =>
      Promise.resolve(
        ok({
          purchaseOrderLines: (store.purchaseOrderLines ?? []).filter((line) =>
            input.purchaseOrderLineIds.includes(line.id),
          ),
        }),
      ),
  ),
});

export const mockInventoryQueries = (store: {
  receiptQuantities?: PurchaseReceiptQuantityForMatching[];
}) => ({
  listPurchaseReceiptQuantitiesForMatching: vi.fn<
    InventoryQueries["listPurchaseReceiptQuantitiesForMatching"]
  >((_db, input) =>
    Promise.resolve(
      ok({
        receiptQuantities: (store.receiptQuantities ?? []).filter((receiptQuantity) =>
          input.purchaseOrderLineIds.includes(receiptQuantity.purchaseOrderLineId),
        ),
      }),
    ),
  ),
});

export const mockPurchaseCommands = () => ({
  recalculatePurchaseOrderBillingStatus: vi.fn<
    PurchaseCommands["recalculatePurchaseOrderBillingStatus"]
  >(() => Promise.resolve(ok({ purchaseOrders: [] }))),
});

export const mockAccountingEventCommands = () => ({
  postAccountingEvent: vi.fn<AccountingEventCommands["postAccountingEvent"]>((_db, event) =>
    Promise.resolve(
      ok({
        accountingEvent: {
          id: "accounting-event-1",
          companyId: event.companyId,
          eventKind: event.eventKind,
          schemaVersion: event.schemaVersion,
          occurredAt: new Date("2024-01-15"),
          payloadJson: JSON.stringify(event.payload),
          createdAt: new Date("2024-01-15"),
          updatedAt: null,
        },
      }),
    ),
  ),
});
