import { type FieldsToInsertable } from "@tailor-platform/erp-kit/core";
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 { 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 { 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 { createPurchaseOrder } from "./command/createPurchaseOrder.generated";
import { createPurchasePriceList } from "./command/createPurchasePriceList.generated";
import { createPurchasePriceRule } from "./command/createPurchasePriceRule.generated";
import { createPurchaseRequisition } from "./command/createPurchaseRequisition.generated";
import { deactivatePurchasePriceList } from "./command/deactivatePurchasePriceList.generated";
import { deactivatePurchasePriceRule } from "./command/deactivatePurchasePriceRule.generated";
import { recalculatePurchaseOrderBillingStatus } from "./command/recalculatePurchaseOrderBillingStatus.generated";
import { recallPurchaseRequisition } from "./command/recallPurchaseRequisition.generated";
import { rejectPurchaseOrder } from "./command/rejectPurchaseOrder.generated";
import { rejectPurchaseRequisition } from "./command/rejectPurchaseRequisition.generated";
import { submitPurchaseOrder } from "./command/submitPurchaseOrder.generated";
import { submitPurchaseRequisition } from "./command/submitPurchaseRequisition.generated";
import { updatePurchaseOrder } from "./command/updatePurchaseOrder.generated";
import { updatePurchasePriceList } from "./command/updatePurchasePriceList.generated";
import { updatePurchasePriceRule } from "./command/updatePurchasePriceRule.generated";
import { updatePurchaseRequisition } from "./command/updatePurchaseRequisition.generated";
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 {
  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 type { Transaction } from "./generated/kysely-tailordb";
import { recalculatePurchaseOrderReceiptStatusInternal } from "./internal/purchaseOrder";
import { calculatePurchasePrice } from "./query/calculatePurchasePrice.generated";
import { getPurchaseOrder } from "./query/getPurchaseOrder.generated";
import { getPurchasePriceList } from "./query/getPurchasePriceList.generated";
import { getPurchaseRequisition } from "./query/getPurchaseRequisition.generated";
import { listPurchaseOrderLinesForMatching } from "./query/listPurchaseOrderLinesForMatching.generated";

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

export interface DefineModuleParams<
  PLF extends Record<string, TailorAnyDBField>,
  PRF extends Record<string, TailorAnyDBField>,
  POF extends Record<string, TailorAnyDBField>,
  POLF extends Record<string, TailorAnyDBField>,
  PRQF extends Record<string, TailorAnyDBField>,
  PRQLF extends Record<string, TailorAnyDBField>,
> {
  purchasePriceList?: CreatePurchasePriceListTypeParams<PLF>;
  purchasePriceRule?: CreatePurchasePriceRuleTypeParams<PRF>;
  purchaseOrder?: CreatePurchaseOrderTypeParams<POF>;
  purchaseOrderLine?: CreatePurchaseOrderLineTypeParams<POLF>;
  purchaseRequisition?: CreatePurchaseRequisitionTypeParams<PRQF>;
  purchaseRequisitionLine?: CreatePurchaseRequisitionLineTypeParams<PRQLF>;
  organization: {
    db: {
      company: OrganizationDB["company"];
      site: OrganizationDB["site"];
    };
    queries: Pick<OrganizationQueries, "getCompany" | "getSite">;
  };
  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, "getCurrency" | "getUnit">;
  };
  inventory: {
    commands: {
      createGoodsReceipt: InventoryCommands["createGoodsReceipt"];
      updateGoodsReceipt: InventoryCommands["updateGoodsReceipt"];
      cancelGoodsReceipt: InventoryCommands["cancelGoodsReceipt"];
      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 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 PRQF extends Record<string, TailorAnyDBField> = Record<never, never>,
  const PRQLF extends Record<string, TailorAnyDBField> = Record<never, never>,
>(
  params: DefineModuleParams<PLF, PRF, POF, POLF, PRQF, PRQLF>,
) => {
  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 purchaseRequisitionLineAllocationDb = createPurchaseRequisitionLineAllocationType();

  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: {
      purchasePriceList: purchasePriceListDb,
      purchasePriceRule: purchasePriceRuleDb,
      purchaseOrder: purchaseOrderDb,
      purchaseOrderLine: purchaseOrderLineDb,
      purchaseOrderRevision: purchaseOrderRevisionDb,
      purchaseOrderFieldChange: purchaseOrderFieldChangeDb,
      purchaseRequisition: purchaseRequisitionDb,
      purchaseRequisitionLine: purchaseRequisitionLineDb,
      purchaseRequisitionLineAllocation: purchaseRequisitionLineAllocationDb,
    },
    commands: {
      activatePurchasePriceList: activatePurchasePriceList(),
      amendOrderedPurchaseOrder: amendOrderedPurchaseOrder<
        FieldsToInsertable<POF>,
        FieldsToInsertable<POLF>
      >(
        params.itemManagement.queries,
        params.inventory.commands,
        params.inventory.queries,
        params.organization.queries,
      ),
      activatePurchasePriceRule: activatePurchasePriceRule(
        params.itemManagement.queries,
        params.primitives.queries,
      ),
      approvePurchaseOrder: approvePurchaseOrder(
        params.businessPartner.queries,
        params.itemManagement.queries,
        params.inventory.commands,
      ),
      approvePurchaseRequisition: approvePurchaseRequisition(),
      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,
        params.organization.queries,
      ),
      createGoodsReceipt: params.inventory.commands.createGoodsReceipt,
      cancelGoodsReceipt: params.inventory.commands.cancelGoodsReceipt,
      createPurchaseOrder: createPurchaseOrder<FieldsToInsertable<POF>, FieldsToInsertable<POLF>>(
        params.businessPartner.queries,
        params.itemManagement.queries,
        params.organization.queries,
      ),
      createPurchaseRequisition: createPurchaseRequisition<
        FieldsToInsertable<PRQF>,
        FieldsToInsertable<PRQLF>
      >(params.businessPartner.queries, params.itemManagement.queries),
      createPurchasePriceList: createPurchasePriceList<FieldsToInsertable<PLF>>(
        params.businessPartner.queries,
        params.organization.queries,
      ),
      createPurchasePriceRule: createPurchasePriceRule<FieldsToInsertable<PRF>>(
        params.itemManagement.queries,
        params.primitives.queries,
      ),
      deactivatePurchasePriceList: deactivatePurchasePriceList(),
      deactivatePurchasePriceRule: deactivatePurchasePriceRule(),
      postGoodsReceipt: postGoodsReceiptWithPurchaseSync,
      recalculatePurchaseOrderBillingStatus: recalculatePurchaseOrderBillingStatus(),
      recallPurchaseRequisition: recallPurchaseRequisition(),
      rejectPurchaseOrder: rejectPurchaseOrder(),
      rejectPurchaseRequisition: rejectPurchaseRequisition(),
      updatePurchaseOrder: updatePurchaseOrder<FieldsToInsertable<POF>, FieldsToInsertable<POLF>>(
        params.itemManagement.queries,
        params.organization.queries,
      ),
      submitPurchaseOrder: submitPurchaseOrder(params.businessPartner.queries),
      submitPurchaseRequisition: submitPurchaseRequisition(),
      updateGoodsReceipt: params.inventory.commands.updateGoodsReceipt,
      updatePurchasePriceList: updatePurchasePriceList<FieldsToInsertable<PLF>>(),
      updatePurchaseRequisition: updatePurchaseRequisition<
        FieldsToInsertable<PRQF>,
        FieldsToInsertable<PRQLF>
      >(params.businessPartner.queries, params.itemManagement.queries),
      updatePurchasePriceRule: updatePurchasePriceRule<FieldsToInsertable<PRF>>(
        params.itemManagement.queries,
        params.primitives.queries,
      ),
    },
    queries: {
      calculatePurchasePrice,
      getPurchaseOrder,
      getPurchaseRequisition,
      getPurchasePriceList,
      listPurchaseOrderLinesForMatching,
    },
  };
};
