// core/types/interfaces/statedb.ts

import type { EventEmitter } from 'node:events';

import type { TTransactionReceipt } from '../lib/type_pb';
import type { Promisable } from './base';
import type {
  IAccountState,
  IAssetFactoryState,
  IAssetState,
  IDelegateState,
  IEvidenceState,
  IRollupBlock,
  IRollupState,
  IStakeState,
  IStateContext,
  ITokenFactoryState,
  ITokenState,
} from './state';

/** Hook function type - args can be any type depending on hook trigger */
export type HookFunction = (...args: unknown[]) => void;

/** Operation context - index signature allows for protocol-specific context data */
export interface IOperationContext {
  [key: string]: unknown;
}

/** Chain token configuration stored in chain state */
export interface IChainTokenInfo {
  symbol?: string;
  decimal?: number;
  name?: string;
  description?: string;
  unit?: string;
  totalSupply?: string;
  initialSupply?: string;
  address?: string;
  foreignToken?: { contractAddress?: string; chainId?: number } | null;
}

export interface IChainState {
  address: string;
  chainId: string;
  version?: string;
  accounts?: Array<{ address: string; pk?: string; balance?: number; moniker?: string }>;
  moderator?: { address: string; pk?: string };
  token?: IChainTokenInfo;
  transaction?: Record<string, unknown>;
  vaults?: { txGas?: string[]; txFee?: string[] };
  context?: IStateContext;
}

/** Transaction data stored in tx state */
export interface ITxData {
  chainId?: string;
  delegator?: string;
  from?: string;
  nonce?: number;
  serviceFee?: string;
  gasFee?: string;
  gasPaid?: string;
  pk?: string;
  signature?: string;
  signatures?: Array<{ signer?: string; delegator?: string; pk?: string; signature?: string }>;
  itx?: { __typename?: string; typeUrl?: string };
  itxJson?: Record<string, unknown>;
  /** Extra transaction data - format varies by transaction source */
  extra?: unknown;
}

export interface ITxState {
  hash: string;
  code?: string;
  height?: number;
  index?: number;
  receipts?: TTransactionReceipt[];
  receiptsVerified?: boolean;
  receiver?: string;
  sender?: string;
  time?: string;
  tx?: ITxData;
  type?: string;
  finalized?: boolean;
  valid?: boolean;
}

export interface IBalanceState {
  address: string;
  tokenAddress: string;
  balance: string;
  context?: IStateContext;
}

export type StateEventHandler<T> = (state: T, ctx?: IOperationContext) => void | Promise<void>;

export interface IStateTable<T = unknown> {
  name: string;
  primaryKey: string;
  uniqIndex?: string | string[];

  create(id: string, attrs: Partial<T>, context?: IOperationContext): Promisable<T>;
  get(id: string, context?: IOperationContext): Promisable<T | null>;
  update(id: string, updates: Partial<T>, context?: IOperationContext): Promisable<T>;
  updateOrCreate(exist: T | null, state: Partial<T>, context?: IOperationContext): Promisable<T>;
  history(id: string, context?: IOperationContext): Promisable<T[]>;
  reset(context?: IOperationContext): Promisable<void>;

  pre(name: string, fn: HookFunction): void;
  post(name: string, fn: HookFunction): void;
  on(event: 'create' | 'update', handler: StateEventHandler<T>): void;
  emit(event: 'create' | 'update', state: T, ctx?: IOperationContext): void;

  generatePrimaryKey(doc: string | Record<string, unknown>): string;
  updateCache(data: Record<string, unknown>, context: IOperationContext | null | undefined): void;
  getCache(key: string | Record<string, unknown>, context: IOperationContext): T | null;

  markReady(): void;
  onReady(cb: () => void): void;
}

export interface IBalanceTable extends IStateTable<IBalanceState> {
  getBalance(address: string, context?: IOperationContext): Promisable<Record<string, string>>;
  updateBalance(
    params: { address: string; tokens: Record<string, string>; context?: IOperationContext },
    context?: IOperationContext
  ): Promisable<Record<string, string>>;
}

export interface ITokenTable extends IStateTable<ITokenState> {
  existBySymbol(symbol: string, context?: IOperationContext): Promisable<boolean>;
}

export interface IRollupTable extends IStateTable<IRollupState> {
  existByToken(token: string, context?: IOperationContext): Promisable<boolean>;
}

export interface IReady extends EventEmitter {
  ready: boolean;
  readyCallbacks: Array<() => void>;
  readyMarks: Record<string, boolean>;
  markReady(mark?: string): void;
  onReady(cb: () => void): void;
}

/** Table names available in IStateDB */
export type StateDBTableName =
  | 'chain'
  | 'account'
  | 'asset'
  | 'token'
  | 'tx'
  | 'factory'
  | 'tokenFactory'
  | 'stake'
  | 'delegation'
  | 'rollup'
  | 'rollupBlock'
  | 'evidence'
  | 'balance';

export interface IStateDB extends IReady {
  name: string;
  version?: string;
  tables: readonly string[];
  readyListenersAttached: boolean;

  attachReadyListeners(): void;

  chain: IStateTable<IChainState>;
  account: IStateTable<IAccountState>;
  asset: IStateTable<IAssetState>;
  token: ITokenTable;
  tx: IStateTable<ITxState>;

  factory: IStateTable<IAssetFactoryState>;
  tokenFactory: IStateTable<ITokenFactoryState>;

  stake: IStateTable<IStakeState>;
  delegation: IStateTable<IDelegateState>;

  rollup: IRollupTable;
  rollupBlock: IStateTable<IRollupBlock>;

  evidence: IStateTable<IEvidenceState>;
  balance: IBalanceTable;

  /** Run function within a database transaction - txn type depends on StateDB implementation (dolt, fs, memory) */
  runAsLambda?<T>(fn: (txn: unknown) => T | Promise<T>, options?: Record<string, unknown>): Promise<T>;

  /** Get table by name - use this for dynamic table access */
  getTable?(name: StateDBTableName): IStateTable<unknown>;

  /**
   * Close the database connection and release all resources.
   * Must be called during graceful shutdown.
   */
  close(): Promise<void>;
}
