import {
  type CommonReservedFields,
  type NoReservedFields,
  defaultGqlPermission,
  defaultPermission,
} from "@tailor-platform/erp-kit/core";
import { db, type TailorAnyDBField, type TailorAnyDBType } from "@tailor-platform/sdk";
import { accountPayableDocumentLifecycle } from "./accountPayableDocument.lifecycle.generated";

export const ACCOUNT_PAYABLE_DOCUMENT_TYPES = ["INVOICE", "CREDIT_MEMO"] as const;

const builtins = (params: {
  companyType?: TailorAnyDBType;
  businessPartnerType?: TailorAnyDBType;
  currencyType?: TailorAnyDBType;
  accountType?: TailorAnyDBType;
}) => {
  const companyId = db.uuid().description("Foreign key to Company");
  const businessPartnerId = db.uuid().description("Foreign key to payable BusinessPartner");
  const currencyId = db.uuid().description("Foreign key to transaction Currency");
  const payableControlAccountId = db
    .uuid()
    .description("Foreign key to the AP payable control Account");
  return {
    companyId: params.companyType
      ? companyId.relation({
          type: "n-1",
          toward: { type: params.companyType },
          backward: "accountPayableDocuments",
        })
      : companyId,
    businessPartnerId: params.businessPartnerType
      ? businessPartnerId.relation({
          type: "n-1",
          toward: { type: params.businessPartnerType },
          backward: "accountPayableDocuments",
        })
      : businessPartnerId,
    currencyId: params.currencyType
      ? currencyId.relation({
          type: "n-1",
          toward: { type: params.currencyType },
          backward: "accountPayableDocuments",
        })
      : currencyId,
    payableControlAccountId: params.accountType
      ? payableControlAccountId.relation({
          type: "n-1",
          toward: { type: params.accountType },
          backward: "accountPayableDocuments",
        })
      : payableControlAccountId,
    documentType: db.enum(ACCOUNT_PAYABLE_DOCUMENT_TYPES).description("AP document type"),
    correctionOfId: db
      .uuid({ optional: true })
      .relation({
        type: "n-1",
        toward: { type: "self" },
        backward: "correctionDocuments",
      })
      .description("Optional self-reference to the posted AP document being corrected"),
    documentNumber: db
      .string()
      .index()
      .unique()
      .serial({
        start: 1,
        maxValue: 99999999,
        format: "AP-%08d",
      })
      .description("System-generated AP document number"),
    externalDocumentNumber: db
      .string({ optional: true })
      .description("Optional external document number from the business partner"),
    documentDate: db.date().description("AP document date"),
    postingDate: db.date().description("Accounting posting date"),
    totalAmount: db.decimal().description("Total AP document amount in transaction currency"),
    description: db.string({ optional: true }).description("Optional AP document description"),
    status: db
      .enum(accountPayableDocumentLifecycle.states)
      .description("AP document lifecycle status"),
    registeredAt: db.datetime({ optional: true }).description("Timestamp when registered"),
    postedAt: db.datetime({ optional: true }).description("Timestamp when posted"),
    cancelledAt: db.datetime({ optional: true }).description("Timestamp when cancelled"),
  };
};

type ReservedFields = keyof ReturnType<typeof builtins> | CommonReservedFields;

export interface CreateAccountPayableDocumentTypeParams<
  F extends Record<string, TailorAnyDBField>,
> {
  fields?: NoReservedFields<F, ReservedFields>;
  companyType?: TailorAnyDBType;
  businessPartnerType?: TailorAnyDBType;
  currencyType?: TailorAnyDBType;
  accountType?: TailorAnyDBType;
}

export function createAccountPayableDocumentType<const F extends Record<string, TailorAnyDBField>>(
  params: CreateAccountPayableDocumentTypeParams<F>,
) {
  return db
    .type("AccountPayableDocument", {
      ...builtins(params),
      ...((params.fields ?? {}) as F),
      ...db.fields.timestamps(),
    })
    .permission(defaultPermission)
    .gqlPermission(defaultGqlPermission);
}

export const accountPayableDocument = createAccountPayableDocumentType({});
