import { type EmptyFields, type FieldsToInsertable } from "@tailor-platform/erp-kit/core";
import { type TailorAnyDBField } from "@tailor-platform/sdk";
import type { defineModule as defineOrganizationModule } from "../organization";
import type { defineModule as definePrimitivesModule } from "../primitives";
import { activatePartner } from "./command/activatePartner.generated";
import { assignRoleToPartner } from "./command/assignRoleToPartner.generated";
import { createContactPerson } from "./command/createContactPerson.generated";
// Commands
import { createPartner } from "./command/createPartner.generated";
import { createPartnerAddress } from "./command/createPartnerAddress.generated";
import { createPartnerBankAccount } from "./command/createPartnerBankAccount.generated";
import { createPartnerIdentification } from "./command/createPartnerIdentification.generated";
import { deactivateContactPerson } from "./command/deactivateContactPerson.generated";
import { deactivatePartner } from "./command/deactivatePartner.generated";
import { deleteContactPerson } from "./command/deleteContactPerson.generated";
import { deletePartner } from "./command/deletePartner.generated";
import { deletePartnerAddress } from "./command/deletePartnerAddress.generated";
import { deletePartnerBankAccount } from "./command/deletePartnerBankAccount.generated";
import { deletePartnerIdentification } from "./command/deletePartnerIdentification.generated";
import { reactivateContactPerson } from "./command/reactivateContactPerson.generated";
import { reactivatePartner } from "./command/reactivatePartner.generated";
import { removeRoleFromPartner } from "./command/removeRoleFromPartner.generated";
import { setDefaultPartnerAddress } from "./command/setDefaultPartnerAddress.generated";
import { setDefaultPartnerBankAccount } from "./command/setDefaultPartnerBankAccount.generated";
import { setPrimaryContactPerson } from "./command/setPrimaryContactPerson.generated";
import { updateContactPerson } from "./command/updateContactPerson.generated";
import { updatePartner } from "./command/updatePartner.generated";
import { updatePartnerAddress } from "./command/updatePartnerAddress.generated";
import { updatePartnerBankAccount } from "./command/updatePartnerBankAccount.generated";
import { updatePartnerIdentification } from "./command/updatePartnerIdentification.generated";
// DB types
import {
  createBusinessPartnerType,
  type CreateBusinessPartnerTypeParams,
} from "./db/businessPartner";
import { createContactPersonType, type CreateContactPersonTypeParams } from "./db/contactPerson";
import { createPartnerAddressType, type CreatePartnerAddressTypeParams } from "./db/partnerAddress";
import {
  createPartnerBankAccountType,
  type CreatePartnerBankAccountTypeParams,
} from "./db/partnerBankAccount";
import {
  createPartnerIdentificationType,
  type CreatePartnerIdentificationTypeParams,
} from "./db/partnerIdentification";
import { createPartnerRoleType, type CreatePartnerRoleTypeParams } from "./db/partnerRole";
import { getContactPerson } from "./query/getContactPerson.generated";
import { getDefaultPartnerAddress } from "./query/getDefaultPartnerAddress.generated";
import { getDefaultPartnerBankAccount } from "./query/getDefaultPartnerBankAccount.generated";
// Queries
import { getPartner } from "./query/getPartner.generated";
import { getPartnerAddress } from "./query/getPartnerAddress.generated";
import { getPartnerBankAccount } from "./query/getPartnerBankAccount.generated";
import { getPartnerIdentification } from "./query/getPartnerIdentification.generated";
import { getPartnerRole } from "./query/getPartnerRole.generated";
import { getPrimaryContactPerson } from "./query/getPrimaryContactPerson.generated";
import { listContactPersonsByPartner } from "./query/listContactPersonsByPartner.generated";
import { listPartnerAddressesByPartner } from "./query/listPartnerAddressesByPartner.generated";
import { listPartnerBankAccountsByPartner } from "./query/listPartnerBankAccountsByPartner.generated";
import { listPartnerIdentificationsByPartner } from "./query/listPartnerIdentificationsByPartner.generated";
import { listPartnersByRole } from "./query/listPartnersByRole.generated";

type OrganizationModule = ReturnType<typeof defineOrganizationModule>;
type OrganizationDB = OrganizationModule["db"];
export type OrganizationQueries = OrganizationModule["queries"];

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

export interface DefineModuleParams<
  BPF extends Record<string, TailorAnyDBField>,
  PRF extends Record<string, TailorAnyDBField>,
  PAF extends Record<string, TailorAnyDBField>,
  CPF extends Record<string, TailorAnyDBField>,
  BAF extends Record<string, TailorAnyDBField>,
  PIF extends Record<string, TailorAnyDBField>,
> {
  businessPartner?: CreateBusinessPartnerTypeParams<BPF>;
  partnerRole?: CreatePartnerRoleTypeParams<PRF>;
  partnerAddress?: CreatePartnerAddressTypeParams<PAF>;
  contactPerson?: CreateContactPersonTypeParams<CPF>;
  partnerBankAccount?: CreatePartnerBankAccountTypeParams<BAF>;
  partnerIdentification?: CreatePartnerIdentificationTypeParams<PIF>;
  organization: {
    db: { company: OrganizationDB["company"] };
    queries: Pick<OrganizationQueries, "getCompany">;
  };
  primitives: {
    db: { currency: PrimitivesDB["currency"] };
    queries: Pick<PrimitivesQueries, "getCurrency">;
  };
}

/* @__NO_SIDE_EFFECTS__ */
export const defineModule = <
  const BPF extends Record<string, TailorAnyDBField> = EmptyFields,
  const PRF extends Record<string, TailorAnyDBField> = EmptyFields,
  const PAF extends Record<string, TailorAnyDBField> = EmptyFields,
  const CPF extends Record<string, TailorAnyDBField> = EmptyFields,
  const BAF extends Record<string, TailorAnyDBField> = EmptyFields,
  const PIF extends Record<string, TailorAnyDBField> = EmptyFields,
>(
  params: DefineModuleParams<BPF, PRF, PAF, CPF, BAF, PIF>,
) => {
  const businessPartnerDb = createBusinessPartnerType({
    ...params.businessPartner,
    companyType: params.organization.db.company,
    currencyType: params.primitives.db.currency,
  });
  const partnerRoleDb = createPartnerRoleType(params.partnerRole ?? {});
  const partnerAddressDb = createPartnerAddressType(params.partnerAddress ?? {});
  const contactPersonDb = createContactPersonType(params.contactPerson ?? {});
  const partnerBankAccountDb = createPartnerBankAccountType({
    ...params.partnerBankAccount,
    currencyType: params.primitives.db.currency,
  });
  const partnerIdentificationDb = createPartnerIdentificationType(
    params.partnerIdentification ?? {},
  );

  return {
    db: {
      businessPartner: businessPartnerDb,
      partnerRole: partnerRoleDb,
      partnerAddress: partnerAddressDb,
      contactPerson: contactPersonDb,
      partnerBankAccount: partnerBankAccountDb,
      partnerIdentification: partnerIdentificationDb,
    },
    commands: {
      createPartner: createPartner<FieldsToInsertable<BPF>>(
        params.organization.queries,
        params.primitives.queries,
      ),
      updatePartner: updatePartner<FieldsToInsertable<BPF>>(params.primitives.queries),
      activatePartner: activatePartner(),
      deactivatePartner: deactivatePartner(),
      reactivatePartner: reactivatePartner(),
      deletePartner: deletePartner(),
      assignRoleToPartner: assignRoleToPartner(),
      removeRoleFromPartner: removeRoleFromPartner(),
      createPartnerAddress: createPartnerAddress<FieldsToInsertable<PAF>>(),
      updatePartnerAddress: updatePartnerAddress<FieldsToInsertable<PAF>>(),
      deletePartnerAddress: deletePartnerAddress(),
      setDefaultPartnerAddress: setDefaultPartnerAddress(),
      createContactPerson: createContactPerson<FieldsToInsertable<CPF>>(),
      updateContactPerson: updateContactPerson<FieldsToInsertable<CPF>>(),
      deactivateContactPerson: deactivateContactPerson(),
      reactivateContactPerson: reactivateContactPerson(),
      setPrimaryContactPerson: setPrimaryContactPerson(),
      deleteContactPerson: deleteContactPerson(),
      createPartnerBankAccount: createPartnerBankAccount<FieldsToInsertable<BAF>>(
        params.primitives.queries,
      ),
      updatePartnerBankAccount: updatePartnerBankAccount<FieldsToInsertable<BAF>>(
        params.primitives.queries,
      ),
      deletePartnerBankAccount: deletePartnerBankAccount(),
      setDefaultPartnerBankAccount: setDefaultPartnerBankAccount(),
      createPartnerIdentification: createPartnerIdentification<FieldsToInsertable<PIF>>(),
      updatePartnerIdentification: updatePartnerIdentification<FieldsToInsertable<PIF>>(),
      deletePartnerIdentification: deletePartnerIdentification(),
    },
    queries: {
      getPartner,
      getPartnerRole,
      getPartnerAddress,
      getDefaultPartnerAddress,
      listPartnerAddressesByPartner,
      getContactPerson,
      getPrimaryContactPerson,
      listContactPersonsByPartner,
      getPartnerBankAccount,
      getDefaultPartnerBankAccount,
      listPartnerBankAccountsByPartner,
      getPartnerIdentification,
      listPartnerIdentificationsByPartner,
      listPartnersByRole,
    },
  };
};
