0.1.2Updated 7 months ago
import type { Ledger } from "@infinity-beyond/classes/data_types/ledger/ledger.ts";
import type { EntityMetaConfig } from "@infinity-beyond/classes/entity_meta.ts";

export type LedgerEvents = {
  /**
   * Event for adding an entry to the ledger.
   * 
   * @param entry_values All values passed to the creation of the new entry
   */
  'ledger created': [Ledger]

  /**
   * Event for adding an entry to the ledger.
   * 
   * @param entry_values All values passed to the creation of the new entry
   */
  'entry_created': [entry: Omit<LedgerEntry, 'id' | 'timestamp'>]

  /**
   * Event for adding an entry to the ledger.
   * 
   * @param reason Why this entry could not be created
   */
  'entry_creation_failed': [reason: string, entry: Omit<LedgerEntry, 'id' | 'timestamp'>]
}

type expiration_measurement_singular = 'minute' | 'hour' | 'day' | 'week' | 'month'
type expiration_measurement_plural = `${expiration_measurement_singular}s`
export type LedgerExpirationMeasurement = expiration_measurement_singular | expiration_measurement_plural

export interface LedgerExpiration {
  /**
   * Whether entries expire.
   * 
   * Default: `false`
   */
  entries_expire?: boolean

  /**
   * How long entries have before they expire.
   * 
   * Only used if **entries_expire** is `true`.
   * 
   * Default: `[1, 'week']`
   */
  period?: [number, LedgerExpirationMeasurement]

  /**
   * After calculating an expiration timestamp, it will be rounded up to the end of this period.
   * 
   * Only used if **entries_expire** is `true`.
   * 
   * Default: `none`
   */
  round_up_to?: 'none' | expiration_measurement_singular

  /**
   * Instead of using **period** and **round_up_to**, pass a function 
   * 
   * @param entry The ledger entry being added. [Immutable]
   * @returns Date
   */
  custom_calculator?: () => Date

}

export interface LedgerOptions<PEMC extends EntityMetaConfig = EntityMetaConfig> {
  /**
   * Should this ledger allow users to go into negative balances?
   * 
   * If this is false, `.AddEntry()` will *fail* when trying to subtract below a zero balance.
   * 
   * **Default**: `false`
   * */
  allow_negative_balances?: boolean

  /**
   * What is the maximum length a key value could be?
   * 
   * A *key* is used to identify ledger data based on a user. Usually the `msisdn`.
   * 
   * This is used when creating the ledger tables to improve performance.
   * 
   * **Default**: `11`
   */
  key_max_length?: number

  /**
   * Additional single-value records that can be stored alongside this ledger
   */
  meta_fields?: PEMC
  
  /**
   * Expiration options for a ledger instance
   */
  expiration?: LedgerExpiration
}

interface LedgerExpirationOverride_False {
  override_expiration_date?: false
}

interface LedgerExpirationOverride_True {
  override_expiration_date: true
  expiration_date: Date
}

export type LedgerExpirationOverride = LedgerExpirationOverride_False | LedgerExpirationOverride_True


export interface LedgerEntry {
  id?: number

  key: string

  amount: number
  remaining: number

  timestamp: Date

  reason?: string
  description?: string
  reference?: string
  meta?: string

  source: string
  request_id?: string

  expiration_date?: Date
  expired?: boolean

  reference_entry_id?: number
}

export type UnenteredLedgerEntry = Omit<LedgerEntry, 'id' | 'timestamp' | 'remaining'> & { remaining?: number }

export interface LedgerUser {
  id?: number
  key: string
  balance: number
  entry_count: number
  first_entry: Date
  last_entry: Date
}

export type LedgerMetaType = {
  total_entries: 'int'
  aggregate_total: 'int'
  unique_users: 'int'
  money_spent: 'int'
}

export const LedgerMeta: EntityMetaConfig = {
  total_entries: 'int',
  aggregate_total: 'int',
  unique_users: 'int',
  money_spent: 'int',
} satisfies LedgerMetaType;