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

export const JOURNAL_ENTRY_SOURCE_DOCUMENT_TYPES = [
  "ACCOUNT_PAYABLE_DOCUMENT",
  "PURCHASE_BILL",
  "SALES_INVOICE",
  "INVENTORY_EXECUTION",
  "PRODUCTION_ORDER",
  "YEAR_END_CLOSE",
] as const;

const builtins = (params: { companyType?: TailorAnyDBType }) => {
  const companyId = db.uuid().description("Foreign key to Company");
  return {
    companyId: params.companyType
      ? companyId.relation({
          type: "n-1",
          toward: { type: params.companyType },
          backward: "journalEntries",
        })
      : companyId,
    accountingPeriodId: db
      .uuid()
      .relation({
        type: "n-1",
        toward: { type: accountingPeriod },
        backward: "journalEntries",
      })
      .description("Foreign key to AccountingPeriod"),
    entryDate: db.date().description("Journal entry document date"),
    status: db
      .enum(journalEntryLifecycle.states)
      .description("Journal entry lifecycle status (DRAFT, CANCELLED, POSTED)"),
    description: db
      .string({ optional: true })
      .description("Optional description of the journal entry"),
    sourceDocumentType: db
      .enum(JOURNAL_ENTRY_SOURCE_DOCUMENT_TYPES, { optional: true })
      .description("Optional source document type that produced the accounting entry"),
    sourceDocumentId: db
      .uuid({ optional: true })
      .description("Optional source document ID. Generic reference, not a typed foreign key."),
    reversalOfId: db
      .uuid({ optional: true })
      .relation({
        type: "1-1",
        toward: { type: "self" },
        backward: "reversals",
      })
      .description("Optional self-referencing FK to the original JournalEntry being reversed"),
    postedAt: db.datetime({ optional: true }).description("Timestamp when the entry was posted"),
  };
};

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

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

export function createJournalEntryType<const F extends Record<string, TailorAnyDBField>>(
  params: CreateJournalEntryTypeParams<F>,
) {
  return db
    .type("JournalEntry", {
      ...builtins(params),
      ...((params.fields ?? {}) as F),
      ...db.fields.timestamps(),
    })
    .indexes({
      fields: ["sourceDocumentType", "sourceDocumentId", "reversalOfId"],
      unique: false,
      name: "journal_entry_source_reversal_idx",
    })
    .permission(defaultPermission)
    .gqlPermission(defaultGqlPermission);
}

export const journalEntry = createJournalEntryType({});
