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 definePrimitivesModule } from "../primitives";
import { activateProduct } from "./command/activateProduct.generated";
import { assignAttributeValueToProduct } from "./command/assignAttributeValueToProduct.generated";
import { assignProductToCategory } from "./command/assignProductToCategory.generated";
import { createProduct } from "./command/createProduct.generated";
import { createProductAttribute } from "./command/createProductAttribute.generated";
import { createProductAttributeValue } from "./command/createProductAttributeValue.generated";
import { createProductCategory } from "./command/createProductCategory.generated";
import { deactivateProduct } from "./command/deactivateProduct.generated";
import { deleteProduct } from "./command/deleteProduct.generated";
import { deleteProductAttribute } from "./command/deleteProductAttribute.generated";
import { deleteProductAttributeValue } from "./command/deleteProductAttributeValue.generated";
import { deleteProductCategory } from "./command/deleteProductCategory.generated";
import { generateVariants } from "./command/generateVariants.generated";
import { moveProductCategory } from "./command/moveProductCategory.generated";
import { reactivateProduct } from "./command/reactivateProduct.generated";
import { removeAttributeValueFromProduct } from "./command/removeAttributeValueFromProduct.generated";
import { removeProductFromCategory } from "./command/removeProductFromCategory.generated";
import { updateProduct } from "./command/updateProduct.generated";
import { updateProductAttribute } from "./command/updateProductAttribute.generated";
import { updateProductAttributeValue } from "./command/updateProductAttributeValue.generated";
import { updateProductCategory } from "./command/updateProductCategory.generated";
import { createProductType, type CreateProductTypeParams } from "./db/product";
import {
  createProductAttributeType,
  type CreateProductAttributeTypeParams,
} from "./db/productAttribute";
import {
  createProductAttributeAssignmentType,
  type CreateProductAttributeAssignmentTypeParams,
} from "./db/productAttributeAssignment";
import {
  createProductAttributeValueType,
  type CreateProductAttributeValueTypeParams,
} from "./db/productAttributeValue";
import {
  createProductCategoryType,
  type CreateProductCategoryTypeParams,
} from "./db/productCategory";
import {
  createProductCategoryAssignmentType,
  type CreateProductCategoryAssignmentTypeParams,
} from "./db/productCategoryAssignment";
import { createProductVariantType, type CreateProductVariantTypeParams } from "./db/productVariant";
import { calculateCategoryDepth } from "./query/calculateCategoryDepth.generated";
import { detectCategoryCircularReference } from "./query/detectCategoryCircularReference.generated";
import { getProduct } from "./query/getProduct.generated";
import { getProductAttribute } from "./query/getProductAttribute.generated";
import { getProductAttributeAssignment } from "./query/getProductAttributeAssignment.generated";
import { getProductAttributeValue } from "./query/getProductAttributeValue.generated";
import { getProductCategory } from "./query/getProductCategory.generated";
import { getProductCategoryAssignment } from "./query/getProductCategoryAssignment.generated";
import { getProductVariant } from "./query/getProductVariant.generated";
import { listAttributeAssignmentsByAttribute } from "./query/listAttributeAssignmentsByAttribute.generated";
import { listCategoryAssignmentsByProduct } from "./query/listCategoryAssignmentsByProduct.generated";
import { listProductAttributeAssignments } from "./query/listProductAttributeAssignments.generated";
import { listProductAttributeValues } from "./query/listProductAttributeValues.generated";
import { listProductCategoryAssignments } from "./query/listProductCategoryAssignments.generated";
import { listProductCategoryChildren } from "./query/listProductCategoryChildren.generated";
import { listProductVariants } from "./query/listProductVariants.generated";

type PrimitivesModule = ReturnType<typeof definePrimitivesModule>;
type PrimitivesDB = PrimitivesModule["db"];
export type PrimitivesQueries = PrimitivesModule["queries"];

type ItemManagementModule = ReturnType<typeof defineItemManagementModule>;
type ItemManagementDB = ItemManagementModule["db"];
export type ItemManagementCommands = ItemManagementModule["commands"];

export type SkuStrategy = (
  productCode: string,
  axisValues: string[],
  currentVariantCount: number,
  axisLabels?: string[],
) => string;

const DEFAULT_MAX_DEPTH = 10;

const defaultSkuStrategy: SkuStrategy = (productCode, _axisValues, currentVariantCount) => {
  const seq = String(currentVariantCount + 1).padStart(3, "0");
  return `${productCode}-${seq}`;
};

export interface DefineModuleParams<
  PF extends Record<string, TailorAnyDBField>,
  PAF extends Record<string, TailorAnyDBField>,
  PAVF extends Record<string, TailorAnyDBField>,
  PAAF extends Record<string, TailorAnyDBField>,
  PCF extends Record<string, TailorAnyDBField>,
  PCAF extends Record<string, TailorAnyDBField>,
  PVF extends Record<string, TailorAnyDBField>,
> {
  product?: Omit<CreateProductTypeParams<PF>, "unitType">;
  productAttribute?: CreateProductAttributeTypeParams<PAF>;
  productAttributeValue?: CreateProductAttributeValueTypeParams<PAVF>;
  productAttributeAssignment?: CreateProductAttributeAssignmentTypeParams<PAAF>;
  productCategory?: CreateProductCategoryTypeParams<PCF>;
  productCategoryAssignment?: CreateProductCategoryAssignmentTypeParams<PCAF>;
  productVariant?: Omit<CreateProductVariantTypeParams<PVF>, "itemType">;
  categoryTree?: { maxDepth?: number };
  skuStrategy?: SkuStrategy;
  primitives: {
    db: { unit: PrimitivesDB["unit"] };
    queries: { getUnit: PrimitivesQueries["getUnit"] };
  };
  itemManagement: {
    db: { item: ItemManagementDB["item"] };
    commands: { createItem: ItemManagementCommands["createItem"] };
  };
}

/* @__NO_SIDE_EFFECTS__ */
export const defineModule = <
  const PF extends Record<string, TailorAnyDBField> = EmptyFields,
  const PAF extends Record<string, TailorAnyDBField> = EmptyFields,
  const PAVF extends Record<string, TailorAnyDBField> = EmptyFields,
  const PAAF extends Record<string, TailorAnyDBField> = EmptyFields,
  const PCF extends Record<string, TailorAnyDBField> = EmptyFields,
  const PCAF extends Record<string, TailorAnyDBField> = EmptyFields,
  const PVF extends Record<string, TailorAnyDBField> = EmptyFields,
>(
  params: DefineModuleParams<PF, PAF, PAVF, PAAF, PCF, PCAF, PVF>,
) => {
  const product = createProductType({
    ...params.product,
    unitType: params.primitives.db.unit,
  });
  const productAttributeDb = createProductAttributeType(params.productAttribute ?? {});
  const productAttributeValueDb = createProductAttributeValueType(
    params.productAttributeValue ?? {},
  );
  const productAttributeAssignmentDb = createProductAttributeAssignmentType(
    params.productAttributeAssignment ?? {},
  );
  const productCategoryDb = createProductCategoryType(params.productCategory ?? {});
  const productCategoryAssignmentDb = createProductCategoryAssignmentType(
    params.productCategoryAssignment ?? {},
  );
  const productVariantDb = createProductVariantType({
    ...params.productVariant,
    itemType: params.itemManagement.db.item,
  });

  const maxDepth = params.categoryTree?.maxDepth ?? DEFAULT_MAX_DEPTH;
  const skuStrategy = params.skuStrategy ?? defaultSkuStrategy;

  return {
    db: {
      product,
      productAttribute: productAttributeDb,
      productAttributeValue: productAttributeValueDb,
      productAttributeAssignment: productAttributeAssignmentDb,
      productCategory: productCategoryDb,
      productCategoryAssignment: productCategoryAssignmentDb,
      productVariant: productVariantDb,
    },
    queries: {
      getProduct,
      getProductAttribute,
      getProductAttributeValue,
      getProductAttributeAssignment,
      getProductCategory,
      getProductCategoryAssignment,
      listProductAttributeAssignments,
      listAttributeAssignmentsByAttribute,
      listProductCategoryAssignments,
      listProductCategoryChildren,
      listProductVariants,
      getProductVariant,
      listCategoryAssignmentsByProduct,
      listProductAttributeValues,
      calculateCategoryDepth,
      detectCategoryCircularReference,
    },
    commands: {
      createProduct: createProduct<FieldsToInsertable<PF>>(params.primitives.queries),
      updateProduct: updateProduct<FieldsToInsertable<PF>>(params.primitives.queries),
      activateProduct: activateProduct(),
      deactivateProduct: deactivateProduct(),
      reactivateProduct: reactivateProduct(),
      deleteProduct: deleteProduct(),
      createProductAttribute: createProductAttribute<FieldsToInsertable<PAF>>(),
      updateProductAttribute: updateProductAttribute<FieldsToInsertable<PAF>>(),
      deleteProductAttribute: deleteProductAttribute(),
      createProductAttributeValue: createProductAttributeValue(),
      updateProductAttributeValue: updateProductAttributeValue(),
      deleteProductAttributeValue: deleteProductAttributeValue(),
      createProductCategory: createProductCategory<FieldsToInsertable<PCF>>({ maxDepth }),
      updateProductCategory: updateProductCategory<FieldsToInsertable<PCF>>(),
      moveProductCategory: moveProductCategory({ maxDepth }),
      deleteProductCategory: deleteProductCategory(),
      assignProductToCategory: assignProductToCategory(),
      removeProductFromCategory: removeProductFromCategory(),
      assignAttributeValueToProduct: assignAttributeValueToProduct(),
      removeAttributeValueFromProduct: removeAttributeValueFromProduct(),
      generateVariants: generateVariants({
        itemManagement: params.itemManagement.commands,
        skuStrategy,
      }),
    },
  };
};
