import { type EmptyFields, type FieldsToInsertable } from "@tailor-platform/erp-kit/core";
import { type TailorAnyDBField } from "@tailor-platform/sdk";
import type { defineModule as defineItemManagementModule } from "../item-management";
import type { defineModule as defineOrganizationModule } from "../organization";
import type { defineModule as definePrimitivesModule } from "../primitives";
import { assignSerialNumber } from "./command/assignSerialNumber.generated";
import { cancelStockAdjustment } from "./command/cancelStockAdjustment.generated";
import { closeInventorySupplyPlan } from "./command/closeInventorySupplyPlan.generated";
import { closeStockReservation } from "./command/closeStockReservation.generated";
import { confirmStockAdjustment } from "./command/confirmStockAdjustment.generated";
import { createGoodsIssue } from "./command/createGoodsIssue.generated";
import { createGoodsReceipt } from "./command/createGoodsReceipt.generated";
import { createInventorySupplyPlan } from "./command/createInventorySupplyPlan.generated";
import { createLot } from "./command/createLot.generated";
import { createStockAdjustment } from "./command/createStockAdjustment.generated";
import { createStockReservation } from "./command/createStockReservation.generated";
import { createStorageLocation } from "./command/createStorageLocation.generated";
import { createValuationPolicy } from "./command/createValuationPolicy.generated";
import { deactivateStorageLocation } from "./command/deactivateStorageLocation.generated";
import { postGoodsIssue } from "./command/postGoodsIssue.generated";
import { postGoodsReceipt } from "./command/postGoodsReceipt.generated";
import { reactivateStorageLocation } from "./command/reactivateStorageLocation.generated";
import { rejectStockAdjustment } from "./command/rejectStockAdjustment.generated";
import { submitStockAdjustment } from "./command/submitStockAdjustment.generated";
import { updateGoodsIssue } from "./command/updateGoodsIssue.generated";
import { updateGoodsReceipt } from "./command/updateGoodsReceipt.generated";
import { updateInventorySupplyPlan } from "./command/updateInventorySupplyPlan.generated";
import { updateLot } from "./command/updateLot.generated";
import { updateStockAdjustment } from "./command/updateStockAdjustment.generated";
import { updateStockReservation } from "./command/updateStockReservation.generated";
import { updateStorageLocation } from "./command/updateStorageLocation.generated";
import { updateValuationPolicy } from "./command/updateValuationPolicy.generated";
import { createCostLayerType } from "./db/costLayer";
import {
  createInventoryExecutionType,
  type CreateInventoryExecutionTypeParams,
} from "./db/inventoryExecution";
import {
  createInventoryExecutionLineType,
  type CreateInventoryExecutionLineTypeParams,
} from "./db/inventoryExecutionLine";
import { createInventoryLedgerType } from "./db/inventoryLedger";
import {
  createInventorySupplyPlanType,
  type CreateInventorySupplyPlanTypeParams,
} from "./db/inventorySupplyPlan";
import { createItemValuationType } from "./db/itemValuation";
import { createLotType } from "./db/lot";
import { createLotStockLevelType } from "./db/lotStockLevel";
import { createSerialNumberType } from "./db/serialNumber";
import { createStockAdjustmentType } from "./db/stockAdjustment";
import { createStockAdjustmentLineType } from "./db/stockAdjustmentLine";
import { createStockLevelType } from "./db/stockLevel";
import { createStockReservationType } from "./db/stockReservation";
import {
  createStorageLocationType,
  type CreateStorageLocationTypeParams,
} from "./db/storageLocation";
import { createValuationPolicyType } from "./db/valuationPolicy";
import { getGoodsIssue } from "./query/getGoodsIssue.generated";
import { getGoodsReceipt } from "./query/getGoodsReceipt.generated";
import { getInventorySupplyPlan } from "./query/getInventorySupplyPlan.generated";
import { getItemValuation } from "./query/getItemValuation.generated";
import { getLot } from "./query/getLot.generated";
import { getSerialNumber } from "./query/getSerialNumber.generated";
import { getSerialNumberHistory } from "./query/getSerialNumberHistory.generated";
import { getSiteStockSummary } from "./query/getSiteStockSummary.generated";
import { getStockAdjustment } from "./query/getStockAdjustment.generated";
import { getStockLevel } from "./query/getStockLevel.generated";
import { getStorageLocation } from "./query/getStorageLocation.generated";
import { getValuationPolicy } from "./query/getValuationPolicy.generated";
import { listCostLayers } from "./query/listCostLayers.generated";
import { listExpiringLots } from "./query/listExpiringLots.generated";
import { listGoodsIssues } from "./query/listGoodsIssues.generated";
import { listGoodsReceipts } from "./query/listGoodsReceipts.generated";
import { listInventorySupplyPlans } from "./query/listInventorySupplyPlans.generated";
import { listItemValuations } from "./query/listItemValuations.generated";
import { listLots } from "./query/listLots.generated";
import { listPurchaseReceiptEvidenceForMatching } from "./query/listPurchaseReceiptEvidenceForMatching.generated";
import { listSerialNumbers } from "./query/listSerialNumbers.generated";
import { listStockAdjustments } from "./query/listStockAdjustments.generated";
import { listStockLevels } from "./query/listStockLevels.generated";
import { listStorageLocations } from "./query/listStorageLocations.generated";
import { listValuationPolicies } from "./query/listValuationPolicies.generated";
import { traceLotBackward } from "./query/traceLotBackward.generated";
import { traceLotForward } from "./query/traceLotForward.generated";

type OrganizationModule = ReturnType<typeof defineOrganizationModule>;
type OrganizationDB = OrganizationModule["db"];
export type OrganizationQueries = OrganizationModule["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 interface DefineModuleParams<
  SF extends Record<string, TailorAnyDBField> = EmptyFields,
  ISPF extends Record<string, TailorAnyDBField> = EmptyFields,
  IEF extends Record<string, TailorAnyDBField> = EmptyFields,
  IELF extends Record<string, TailorAnyDBField> = EmptyFields,
> {
  storageLocation?: Omit<CreateStorageLocationTypeParams<SF>, "siteType">;
  inventorySupplyPlan?: Omit<CreateInventorySupplyPlanTypeParams<ISPF>, "itemType" | "siteType">;
  inventoryExecution?: CreateInventoryExecutionTypeParams<IEF>;
  inventoryExecutionLine?: Omit<CreateInventoryExecutionLineTypeParams<IELF>, "unitType">;
  organization: {
    db: { site: OrganizationDB["site"] };
    queries: { getSite: OrganizationQueries["getSite"] };
  };
  itemManagement: {
    db: { item: ItemManagementDB["item"] };
    queries: { getItem: ItemManagementQueries["getItem"] };
  };
  primitives: {
    db: { unit: PrimitivesDB["unit"] };
  };
}

/* @__NO_SIDE_EFFECTS__ */
export const defineModule = <
  const SF extends Record<string, TailorAnyDBField> = EmptyFields,
  const ISPF extends Record<string, TailorAnyDBField> = EmptyFields,
  const IEF extends Record<string, TailorAnyDBField> = EmptyFields,
  const IELF extends Record<string, TailorAnyDBField> = EmptyFields,
>(
  params: DefineModuleParams<SF, ISPF, IEF, IELF>,
) => {
  const storageLocation = createStorageLocationType({
    ...params.storageLocation,
    siteType: params.organization.db.site,
  });
  const valuationPolicy = createValuationPolicyType({});
  const stockLevel = createStockLevelType({});
  const stockReservation = createStockReservationType({
    itemType: params.itemManagement.db.item,
    siteType: params.organization.db.site,
  });
  const inventorySupplyPlan = createInventorySupplyPlanType({
    ...params.inventorySupplyPlan,
    itemType: params.itemManagement.db.item,
    siteType: params.organization.db.site,
  });
  const stockAdjustment = createStockAdjustmentType({});
  const stockAdjustmentLine = createStockAdjustmentLineType({});
  const inventoryExecution = createInventoryExecutionType({
    ...params.inventoryExecution,
  });
  const inventoryExecutionLine = createInventoryExecutionLineType({
    ...params.inventoryExecutionLine,
    unitType: params.primitives.db.unit,
  });
  const inventoryLedger = createInventoryLedgerType({});
  const lot = createLotType({});
  const lotStockLevel = createLotStockLevelType({});
  const serialNumber = createSerialNumberType({});
  const itemValuation = createItemValuationType({});
  const costLayer = createCostLayerType({});

  const orgQueries = params.organization.queries;
  const itemQueries = params.itemManagement.queries;

  return {
    db: {
      storageLocation,
      valuationPolicy,
      stockLevel,
      stockReservation,
      inventorySupplyPlan,
      stockAdjustment,
      stockAdjustmentLine,
      inventoryExecution,
      inventoryExecutionLine,
      inventoryLedger,
      lot,
      lotStockLevel,
      serialNumber,
      itemValuation,
      costLayer,
    },
    queries: {
      getStorageLocation,
      getValuationPolicy,
      getStockLevel,
      getInventorySupplyPlan,
      getStockAdjustment,
      getGoodsReceipt,
      getGoodsIssue,
      getLot,
      getSerialNumber,
      getSerialNumberHistory,
      getItemValuation,
      getSiteStockSummary,
      listStorageLocations,
      listValuationPolicies,
      listStockLevels,
      listGoodsReceipts,
      listPurchaseReceiptEvidenceForMatching,
      listGoodsIssues,
      listInventorySupplyPlans,
      listStockAdjustments,
      listLots,
      listSerialNumbers,
      listItemValuations,
      listCostLayers,
      listExpiringLots,
      traceLotForward,
      traceLotBackward,
    },
    commands: {
      createStorageLocation: createStorageLocation<FieldsToInsertable<SF>>(orgQueries),
      updateStorageLocation: updateStorageLocation(),
      deactivateStorageLocation: deactivateStorageLocation(),
      reactivateStorageLocation: reactivateStorageLocation(orgQueries),
      createValuationPolicy: createValuationPolicy(),
      updateValuationPolicy: updateValuationPolicy(),
      createLot: createLot(itemQueries),
      updateLot: updateLot(),
      assignSerialNumber: assignSerialNumber(itemQueries),
      createGoodsIssue: createGoodsIssue(itemQueries),
      updateGoodsIssue: updateGoodsIssue(itemQueries),
      postGoodsIssue: postGoodsIssue(itemQueries),
      createGoodsReceipt: createGoodsReceipt(itemQueries),
      updateGoodsReceipt: updateGoodsReceipt(itemQueries),
      postGoodsReceipt: postGoodsReceipt(itemQueries),
      createInventorySupplyPlan: createInventorySupplyPlan<FieldsToInsertable<ISPF>>(
        itemQueries,
        orgQueries,
      ),
      updateInventorySupplyPlan: updateInventorySupplyPlan<FieldsToInsertable<ISPF>>(
        itemQueries,
        orgQueries,
      ),
      closeInventorySupplyPlan: closeInventorySupplyPlan(),
      createStockAdjustment: createStockAdjustment(itemQueries),
      updateStockAdjustment: updateStockAdjustment(itemQueries),
      submitStockAdjustment: submitStockAdjustment(itemQueries),
      confirmStockAdjustment: confirmStockAdjustment(itemQueries),
      rejectStockAdjustment: rejectStockAdjustment(),
      cancelStockAdjustment: cancelStockAdjustment(),
      closeStockReservation: closeStockReservation(),
      createStockReservation: createStockReservation(itemQueries, orgQueries),
      updateStockReservation: updateStockReservation(itemQueries, orgQueries),
    },
  };
};
