import type { TailorAnyDBField } from "@tailor-platform/sdk";
import type { defineModule as defineBusinessPartnerModule } from "../business-partner";
import type { defineModule as defineInventoryModule } from "../inventory";
import type { defineModule as defineItemManagementModule } from "../item-management";
import type { defineModule as defineOrganizationModule } from "../organization";
import type { defineModule as definePrimitivesModule } from "../primitives";
import type { defineModule as defineUserManagementModule } from "../user-management";
import { activatePurchasePaymentTerm } from "./command/activatePurchasePaymentTerm.generated";
import { activatePurchasePriceList } from "./command/activatePurchasePriceList.generated";
import { activatePurchasePriceRule } from "./command/activatePurchasePriceRule.generated";
import { amendOrderedPurchaseOrder } from "./command/amendOrderedPurchaseOrder.generated";
import { approvePurchaseOrder } from "./command/approvePurchaseOrder.generated";
import { approvePurchaseRequisition } from "./command/approvePurchaseRequisition.generated";
import { cancelPurchaseBill } from "./command/cancelPurchaseBill.generated";
import { cancelPurchaseOrder } from "./command/cancelPurchaseOrder.generated";
import { cancelPurchaseRequisition } from "./command/cancelPurchaseRequisition.generated";
import { closePurchaseOrder } from "./command/closePurchaseOrder.generated";
import { closePurchaseRequisition } from "./command/closePurchaseRequisition.generated";
import { convertPurchaseRequisitionToPurchaseOrder } from "./command/convertPurchaseRequisitionToPurchaseOrder.generated";
import { createPurchaseBill } from "./command/createPurchaseBill.generated";
import { createPurchaseOrder } from "./command/createPurchaseOrder.generated";
import { createPurchasePaymentTerm } from "./command/createPurchasePaymentTerm.generated";
import { createPurchasePriceList } from "./command/createPurchasePriceList.generated";
import { createPurchasePriceRule } from "./command/createPurchasePriceRule.generated";
import { createPurchaseRequisition } from "./command/createPurchaseRequisition.generated";
import { deactivatePurchasePaymentTerm } from "./command/deactivatePurchasePaymentTerm.generated";
import { deactivatePurchasePriceList } from "./command/deactivatePurchasePriceList.generated";
import { deactivatePurchasePriceRule } from "./command/deactivatePurchasePriceRule.generated";
import { matchPurchaseBill } from "./command/matchPurchaseBill.generated";
import { recallPurchaseRequisition } from "./command/recallPurchaseRequisition.generated";
import { rejectPurchaseOrder } from "./command/rejectPurchaseOrder.generated";
import { rejectPurchaseRequisition } from "./command/rejectPurchaseRequisition.generated";
import { releasePurchaseBill } from "./command/releasePurchaseBill.generated";
import { setSupplierDefaultPurchasePaymentTerm } from "./command/setSupplierDefaultPurchasePaymentTerm.generated";
import { submitPurchaseOrder } from "./command/submitPurchaseOrder.generated";
import { submitPurchaseRequisition } from "./command/submitPurchaseRequisition.generated";
import { updatePurchaseBill } from "./command/updatePurchaseBill.generated";
import { updatePurchaseOrder } from "./command/updatePurchaseOrder.generated";
import { updatePurchasePaymentTerm } from "./command/updatePurchasePaymentTerm.generated";
import { updatePurchasePriceList } from "./command/updatePurchasePriceList.generated";
import { updatePurchasePriceRule } from "./command/updatePurchasePriceRule.generated";
import { updatePurchaseRequisition } from "./command/updatePurchaseRequisition.generated";
import { createPurchaseBillType, type CreatePurchaseBillTypeParams } from "./db/purchaseBill";
import {
  createPurchaseBillLineType,
  type CreatePurchaseBillLineTypeParams,
} from "./db/purchaseBillLine";
import { createPurchaseOrderType, type CreatePurchaseOrderTypeParams } from "./db/purchaseOrder";
import { createPurchaseOrderFieldChangeType } from "./db/purchaseOrderFieldChange";
import {
  createPurchaseOrderLineType,
  type CreatePurchaseOrderLineTypeParams,
} from "./db/purchaseOrderLine";
import { createPurchaseOrderRevisionType } from "./db/purchaseOrderRevision";
import {
  createPurchasePaymentTermType,
  type CreatePurchasePaymentTermTypeParams,
} from "./db/purchasePaymentTerm";
import { createPurchasePaymentTermLineType } from "./db/purchasePaymentTermLine";
import {
  createPurchasePriceListType,
  type CreatePurchasePriceListTypeParams,
} from "./db/purchasePriceList";
import {
  createPurchasePriceRuleType,
  type CreatePurchasePriceRuleTypeParams,
} from "./db/purchasePriceRule";
import {
  createPurchaseRequisitionType,
  type CreatePurchaseRequisitionTypeParams,
} from "./db/purchaseRequisition";
import {
  createPurchaseRequisitionLineType,
  type CreatePurchaseRequisitionLineTypeParams,
} from "./db/purchaseRequisitionLine";
import { createPurchaseRequisitionLineAllocationType } from "./db/purchaseRequisitionLineAllocation";
import {
  createSupplierProfileType,
  type CreateSupplierProfileTypeParams,
} from "./db/supplierProfile";
import { createThreeWayMatchEventType } from "./db/threeWayMatchEvent";
import type { Transaction } from "./generated/kysely-tailordb";
import { recalculatePurchaseOrderReceiptStatusInternal } from "./internal/purchaseOrder";
import { calculatePurchaseBillDueSchedule } from "./query/calculatePurchaseBillDueSchedule.generated";
import { calculatePurchasePrice } from "./query/calculatePurchasePrice.generated";
import { getPurchaseBill } from "./query/getPurchaseBill.generated";
import { getPurchaseOrder } from "./query/getPurchaseOrder.generated";
import { getPurchasePaymentTerm } from "./query/getPurchasePaymentTerm.generated";
import { getPurchasePriceList } from "./query/getPurchasePriceList.generated";
import { getPurchaseRequisition } from "./query/getPurchaseRequisition.generated";
import { getSupplierProfile } from "./query/getSupplierProfile.generated";

type OrganizationModule = ReturnType<typeof defineOrganizationModule>;
type OrganizationDB = OrganizationModule["db"];
type BusinessPartnerModule = ReturnType<typeof defineBusinessPartnerModule>;
type BusinessPartnerDB = BusinessPartnerModule["db"];
type InventoryModule = ReturnType<typeof defineInventoryModule>;
export type InventoryCommands = InventoryModule["commands"];
export type InventoryQueries = InventoryModule["queries"];
type ItemManagementModule = ReturnType<typeof defineItemManagementModule>;
type ItemManagementDB = ItemManagementModule["db"];
type PrimitivesModule = ReturnType<typeof definePrimitivesModule>;
type PrimitivesDB = PrimitivesModule["db"];
type UserManagementModule = ReturnType<typeof defineUserManagementModule>;
type UserManagementDB = UserManagementModule["db"];
type BusinessPartnerQueries = BusinessPartnerModule["queries"];
type ItemManagementQueries = ItemManagementModule["queries"];
type PrimitivesQueries = PrimitivesModule["queries"];

export interface DefineModuleParams<
  SPF extends Record<string, TailorAnyDBField>,
  PTF extends Record<string, TailorAnyDBField>,
  PLF extends Record<string, TailorAnyDBField>,
  PRF extends Record<string, TailorAnyDBField>,
  POF extends Record<string, TailorAnyDBField>,
  POLF extends Record<string, TailorAnyDBField>,
  PBF extends Record<string, TailorAnyDBField>,
  PBLF extends Record<string, TailorAnyDBField>,
  PRQF extends Record<string, TailorAnyDBField>,
  PRQLF extends Record<string, TailorAnyDBField>,
> {
  supplierProfile?: CreateSupplierProfileTypeParams<SPF>;
  purchasePaymentTerm?: CreatePurchasePaymentTermTypeParams<PTF>;
  purchasePriceList?: CreatePurchasePriceListTypeParams<PLF>;
  purchasePriceRule?: CreatePurchasePriceRuleTypeParams<PRF>;
  purchaseOrder?: CreatePurchaseOrderTypeParams<POF>;
  purchaseOrderLine?: CreatePurchaseOrderLineTypeParams<POLF>;
  purchaseBill?: CreatePurchaseBillTypeParams<PBF>;
  purchaseBillLine?: CreatePurchaseBillLineTypeParams<PBLF>;
  purchaseRequisition?: CreatePurchaseRequisitionTypeParams<PRQF>;
  purchaseRequisitionLine?: CreatePurchaseRequisitionLineTypeParams<PRQLF>;
  organization: {
    db: {
      company: OrganizationDB["company"];
      site: OrganizationDB["site"];
    };
  };
  businessPartner: {
    db: {
      businessPartner: BusinessPartnerDB["businessPartner"];
      partnerRole: BusinessPartnerDB["partnerRole"];
      partnerIdentification: BusinessPartnerDB["partnerIdentification"];
    };
    queries: Pick<
      BusinessPartnerQueries,
      "getPartner" | "getPartnerRole" | "listPartnerIdentificationsByPartner"
    >;
  };
  itemManagement: {
    db: {
      item: ItemManagementDB["item"];
    };
    queries: Pick<ItemManagementQueries, "getItem">;
  };
  primitives: {
    db: {
      currency: PrimitivesDB["currency"];
      unit: PrimitivesDB["unit"];
    };
    queries: Pick<PrimitivesQueries, "getUnit">;
  };
  inventory: {
    commands: {
      createGoodsReceipt: InventoryCommands["createGoodsReceipt"];
      updateGoodsReceipt: InventoryCommands["updateGoodsReceipt"];
      postGoodsReceipt: InventoryCommands["postGoodsReceipt"];
      createInventorySupplyPlan: InventoryCommands["createInventorySupplyPlan"];
      updateInventorySupplyPlan: InventoryCommands["updateInventorySupplyPlan"];
      closeInventorySupplyPlan: InventoryCommands["closeInventorySupplyPlan"];
    };
    queries: {
      getInventorySupplyPlan: InventoryQueries["getInventorySupplyPlan"];
      listPurchaseReceiptEvidenceForMatching: InventoryQueries["listPurchaseReceiptEvidenceForMatching"];
    };
  };
  userManagement: {
    db: {
      user: UserManagementDB["user"];
    };
  };
}

/* @__NO_SIDE_EFFECTS__ */
export const defineModule = <
  const SPF extends Record<string, TailorAnyDBField> = Record<never, never>,
  const PTF extends Record<string, TailorAnyDBField> = Record<never, never>,
  const PLF extends Record<string, TailorAnyDBField> = Record<never, never>,
  const PRF extends Record<string, TailorAnyDBField> = Record<never, never>,
  const POF extends Record<string, TailorAnyDBField> = Record<never, never>,
  const POLF extends Record<string, TailorAnyDBField> = Record<never, never>,
  const PBF extends Record<string, TailorAnyDBField> = Record<never, never>,
  const PBLF extends Record<string, TailorAnyDBField> = Record<never, never>,
  const PRQF extends Record<string, TailorAnyDBField> = Record<never, never>,
  const PRQLF extends Record<string, TailorAnyDBField> = Record<never, never>,
>(
  params: DefineModuleParams<SPF, PTF, PLF, PRF, POF, POLF, PBF, PBLF, PRQF, PRQLF>,
) => {
  const purchasePaymentTermDb = createPurchasePaymentTermType({
    ...params.purchasePaymentTerm,
    companyType: params.organization.db.company,
  });
  const purchasePaymentTermLineDb = createPurchasePaymentTermLineType();
  const supplierProfileDb = createSupplierProfileType({
    ...params.supplierProfile,
    companyType: params.organization.db.company,
    supplierType: params.businessPartner.db.businessPartner,
  });
  const purchasePriceListDb = createPurchasePriceListType({
    ...params.purchasePriceList,
    companyType: params.organization.db.company,
    supplierType: params.businessPartner.db.businessPartner,
  });
  const purchasePriceRuleDb = createPurchasePriceRuleType({
    ...params.purchasePriceRule,
    itemType: params.itemManagement.db.item,
    currencyType: params.primitives.db.currency,
    unitType: params.primitives.db.unit,
  });
  const purchaseRequisitionDb = createPurchaseRequisitionType({
    ...params.purchaseRequisition,
    companyType: params.organization.db.company,
    userType: params.userManagement.db.user,
  });
  const purchaseRequisitionLineDb = createPurchaseRequisitionLineType({
    ...params.purchaseRequisitionLine,
    itemType: params.itemManagement.db.item,
    unitType: params.primitives.db.unit,
    suggestedSupplierType: params.businessPartner.db.businessPartner,
  });
  const purchaseOrderDb = createPurchaseOrderType({
    ...params.purchaseOrder,
    companyType: params.organization.db.company,
    supplierType: params.businessPartner.db.businessPartner,
    siteType: params.organization.db.site,
  });
  const purchaseOrderLineDb = createPurchaseOrderLineType({
    ...params.purchaseOrderLine,
    itemType: params.itemManagement.db.item,
    unitType: params.primitives.db.unit,
    siteType: params.organization.db.site,
  });
  const purchaseOrderRevisionDb = createPurchaseOrderRevisionType({
    userType: params.userManagement.db.user,
  });
  const purchaseOrderFieldChangeDb = createPurchaseOrderFieldChangeType();
  const purchaseBillDb = createPurchaseBillType({
    ...params.purchaseBill,
    companyType: params.organization.db.company,
    supplierType: params.businessPartner.db.businessPartner,
  });
  const purchaseBillLineDb = createPurchaseBillLineType({
    ...params.purchaseBillLine,
    unitType: params.primitives.db.unit,
  });

  const purchaseRequisitionLineAllocationDb = createPurchaseRequisitionLineAllocationType();

  const threeWayMatchEventDb = createThreeWayMatchEventType();

  const postGoodsReceiptWithPurchaseSync = async (
    db: Transaction,
    input: Parameters<InventoryCommands["postGoodsReceipt"]>[1],
    ctx: Parameters<InventoryCommands["postGoodsReceipt"]>[2],
  ) => {
    const result = await params.inventory.commands.postGoodsReceipt(db, input, ctx);
    if (!result.ok) return result;

    const execution = result.value.inventoryExecution;
    if (execution.sourceDocumentType === "PURCHASE_ORDER" && execution.sourceDocumentId) {
      const syncResult = await recalculatePurchaseOrderReceiptStatusInternal(
        db,
        { purchaseOrderId: execution.sourceDocumentId },
        ctx,
        params.inventory.queries,
      );
      if (!syncResult.ok) return syncResult;
    }

    return result;
  };

  return {
    db: {
      supplierProfile: supplierProfileDb,
      purchasePaymentTerm: purchasePaymentTermDb,
      purchasePaymentTermLine: purchasePaymentTermLineDb,
      purchasePriceList: purchasePriceListDb,
      purchasePriceRule: purchasePriceRuleDb,
      purchaseOrder: purchaseOrderDb,
      purchaseOrderLine: purchaseOrderLineDb,
      purchaseOrderRevision: purchaseOrderRevisionDb,
      purchaseOrderFieldChange: purchaseOrderFieldChangeDb,
      purchaseBill: purchaseBillDb,
      purchaseBillLine: purchaseBillLineDb,
      purchaseRequisition: purchaseRequisitionDb,
      purchaseRequisitionLine: purchaseRequisitionLineDb,
      purchaseRequisitionLineAllocation: purchaseRequisitionLineAllocationDb,
      threeWayMatchEvent: threeWayMatchEventDb,
    },
    commands: {
      activatePurchasePaymentTerm: activatePurchasePaymentTerm(),
      activatePurchasePriceList: activatePurchasePriceList(),
      amendOrderedPurchaseOrder: amendOrderedPurchaseOrder(
        params.itemManagement.queries,
        params.inventory.commands,
        params.inventory.queries,
      ),
      activatePurchasePriceRule: activatePurchasePriceRule(params.itemManagement.queries),
      approvePurchaseOrder: approvePurchaseOrder(
        params.businessPartner.queries,
        params.itemManagement.queries,
        params.inventory.commands,
      ),
      approvePurchaseRequisition: approvePurchaseRequisition(),
      cancelPurchaseBill: cancelPurchaseBill(),
      cancelPurchaseOrder: cancelPurchaseOrder(params.inventory.commands, params.inventory.queries),
      cancelPurchaseRequisition: cancelPurchaseRequisition(),
      closePurchaseOrder: closePurchaseOrder(params.inventory.commands, params.inventory.queries),
      closePurchaseRequisition: closePurchaseRequisition(),
      convertPurchaseRequisitionToPurchaseOrder: convertPurchaseRequisitionToPurchaseOrder(
        params.businessPartner.queries,
        params.itemManagement.queries,
      ),
      createGoodsReceipt: params.inventory.commands.createGoodsReceipt,
      createPurchaseBill: createPurchaseBill(params.businessPartner.queries),
      createPurchaseOrder: createPurchaseOrder(
        params.businessPartner.queries,
        params.itemManagement.queries,
      ),
      createPurchasePaymentTerm: createPurchasePaymentTerm(),
      createPurchaseRequisition: createPurchaseRequisition(params.businessPartner.queries),
      createPurchasePriceList: createPurchasePriceList(params.businessPartner.queries),
      createPurchasePriceRule: createPurchasePriceRule(params.itemManagement.queries),
      deactivatePurchasePaymentTerm: deactivatePurchasePaymentTerm(),
      deactivatePurchasePriceList: deactivatePurchasePriceList(),
      deactivatePurchasePriceRule: deactivatePurchasePriceRule(),
      matchPurchaseBill: matchPurchaseBill(params.inventory.queries),
      postGoodsReceipt: postGoodsReceiptWithPurchaseSync,
      recallPurchaseRequisition: recallPurchaseRequisition(),
      rejectPurchaseOrder: rejectPurchaseOrder(),
      rejectPurchaseRequisition: rejectPurchaseRequisition(),
      releasePurchaseBill: releasePurchaseBill(params.inventory.queries),
      updatePurchaseOrder: updatePurchaseOrder(params.itemManagement.queries),
      setSupplierDefaultPurchasePaymentTerm: setSupplierDefaultPurchasePaymentTerm(
        params.businessPartner.queries,
      ),
      submitPurchaseOrder: submitPurchaseOrder(params.businessPartner.queries),
      submitPurchaseRequisition: submitPurchaseRequisition(),
      updatePurchaseBill: updatePurchaseBill(),
      updateGoodsReceipt: params.inventory.commands.updateGoodsReceipt,
      updatePurchasePaymentTerm: updatePurchasePaymentTerm(),
      updatePurchasePriceList: updatePurchasePriceList(),
      updatePurchaseRequisition: updatePurchaseRequisition(params.businessPartner.queries),
      updatePurchasePriceRule: updatePurchasePriceRule(params.itemManagement.queries),
    },
    queries: {
      calculatePurchaseBillDueSchedule,
      calculatePurchasePrice,
      getPurchaseBill,
      getPurchaseOrder,
      getPurchasePaymentTerm,
      getPurchaseRequisition,
      getPurchasePriceList,
      getSupplierProfile,
    },
  };
};
