// core/types/interfaces/indexdb.ts

import type { Promisable } from './base';
import type {
  IAccountState,
  IAssetFactoryState,
  IAssetState,
  IDelegateState,
  IRollupBlock,
  IRollupState,
  IStakeState,
  ITokenFactoryState,
  ITokenState,
  TokenBalanceMap,
} from './state';

// Re-export from trace-type_pb
export type { TPage, TPageOrder, TPageInfo } from '../lib/trace-type_pb';
export type {
  TAddressFilter,
  TTimeFilter,
  TTypeFilter,
  TValidityFilter,
  TAssetFilter,
  TTokenFilter,
  TAccountFilter,
  TFactoryFilter,
  TRollupFilter,
  TStakeFilter,
  TDelegationFilter,
  TTxFilter,
  TTokenFactoryFilter,
  TValidatorFilter,
  TRangeFilter,
  DirectionMap,
  ValidityMap,
} from '../lib/trace-type_pb';
export type { TTokenMeta, TAccountToken } from '../lib/trace-type_pb';
export type {
  TIndexedTransaction,
  TIndexedAccountState,
  TIndexedAssetState,
  TIndexedTokenState,
  TIndexedFactoryState,
  TIndexedStakeState,
  TIndexedDelegationState,
  TIndexedRollupState,
  TIndexedRollupBlock,
  TIndexedRollupValidator,
  TIndexedTokenFactoryState,
  TTokenDistribution,
  TSearchResult,
} from '../lib/trace-type_pb';

// Re-export from rpc_pb
export type {
  TRequestListTransactions,
  TRequestListAssets,
  TRequestListTopAccounts,
  TRequestListTokens,
  TRequestListFactories,
  TRequestListStakes,
  TRequestListRollups,
  TRequestListRollupBlocks,
  TRequestListRollupValidators,
  TRequestListDelegations,
  TRequestListTokenFactories,
} from '../lib/rpc_pb';
export type {
  TResponseListTransactions,
  TResponseListAssets,
  TResponseListTopAccounts,
  TResponseListTokens,
  TResponseListFactories,
  TResponseListStakes,
  TResponseListRollups,
  TResponseListRollupBlocks,
  TResponseListRollupValidators,
  TResponseListDelegations,
  TResponseListTokenFactories,
} from '../lib/rpc_pb';

// IndexDB types
import type {
  TIndexedAccountState,
  TIndexedAssetState,
  TIndexedDelegationState,
  TIndexedFactoryState,
  TIndexedRollupBlock,
  TIndexedRollupState,
  TIndexedRollupValidator,
  TIndexedStakeState,
  TIndexedTokenFactoryState,
  TIndexedTokenState,
  TIndexedTransaction,
  TPageInfo,
  TSearchResult,
  TTokenDistribution,
} from '../lib/trace-type_pb';

import type {
  TRequestListAssets,
  TRequestListDelegations,
  TRequestListFactories,
  TRequestListRollupBlocks,
  TRequestListRollupValidators,
  TRequestListRollups,
  TRequestListStakes,
  TRequestListTokenFactories,
  TRequestListTokens,
  TRequestListTopAccounts,
  TRequestListTransactions,
} from '../lib/rpc_pb';

// ============================================================================
// Token Format Conversion Types
// ============================================================================
// StateDB stores tokens as Record<address, balance> for O(1) lookup
// IndexDB stores tokens as Array<TTokenMeta> for GraphQL/protobuf compatibility
// These types help with explicit conversion between formats

/**
 * Token balance in StateDB format: address -> balance mapping
 * Used by: IAccountState, IAssetFactoryState, IStakeState
 * @example { "z35n6...": "1000000", "z35n7...": "500" }
 */
export type { TokenBalanceMap } from './state';

/**
 * Input for converting StateDB tokens to IndexDB format
 * Requires token metadata lookup to populate symbol/decimal/unit
 */
export interface TokenConversionInput {
  /** Token balances from StateDB (address -> balance) */
  tokens: TokenBalanceMap;
  /** Token states for metadata lookup */
  tokenStates: Array<{ address: string; symbol?: string; decimal?: number; unit?: string; icon?: string }>;
}

// ============================================================================
// Unified Indexed State Type
// ============================================================================

/**
 * Union of all indexed state types stored in IndexDB
 * Useful for generic document handling
 */
export type TIndexedState =
  | TIndexedTransaction
  | TIndexedAccountState
  | TIndexedAssetState
  | TIndexedTokenState
  | TIndexedFactoryState
  | TIndexedStakeState
  | TIndexedDelegationState
  | TIndexedRollupState
  | TIndexedRollupBlock
  | TIndexedRollupValidator
  | TIndexedTokenFactoryState
  | TTokenDistribution;

// ============================================================================
// Index Table Type Mappings
// ============================================================================

/** Maps table name to its indexed document type */
export type IndexTableTypeMap = {
  tx: TIndexedTransaction;
  account: TIndexedAccountState;
  asset: TIndexedAssetState;
  token: TIndexedTokenState;
  factory: TIndexedFactoryState;
  stake: TIndexedStakeState;
  delegation: TIndexedDelegationState;
  rollup: TIndexedRollupState;
  rollupBlock: TIndexedRollupBlock;
  rollupValidator: TIndexedRollupValidator;
  tokenFactory: TIndexedTokenFactoryState;
  tokenDistribution: TTokenDistribution;
};

/** Valid index table names */
export type IndexTableType = keyof IndexTableTypeMap;

/** Get indexed type for a table name */
export type IndexedTypeFor<K extends IndexTableType> = IndexTableTypeMap[K];

// ============================================================================
// State to Indexed Type Mapping
// ============================================================================
// Maps StateDB state types to their IndexDB indexed counterparts
// Note: The main difference is token format (Record vs Array) and context structure

/** Maps StateDB types to IndexDB types for conversion */
export type StateToIndexedMap = {
  account: { state: IAccountState; indexed: TIndexedAccountState };
  asset: { state: IAssetState; indexed: TIndexedAssetState };
  token: { state: ITokenState; indexed: TIndexedTokenState };
  factory: { state: IAssetFactoryState; indexed: TIndexedFactoryState };
  stake: { state: IStakeState; indexed: TIndexedStakeState };
  delegation: { state: IDelegateState; indexed: TIndexedDelegationState };
  rollup: { state: IRollupState; indexed: TIndexedRollupState };
  rollupBlock: { state: IRollupBlock; indexed: TIndexedRollupBlock };
  tokenFactory: { state: ITokenFactoryState; indexed: TIndexedTokenFactoryState };
};

/** Get StateDB state type for a table name */
export type StateTypeOf<K extends keyof StateToIndexedMap> = StateToIndexedMap[K]['state'];

/** Get IndexDB indexed type for a table name */
export type IndexedTypeOf<K extends keyof StateToIndexedMap> = StateToIndexedMap[K]['indexed'];

// ============================================================================
// IIndexTable Interface
// ============================================================================

/** Hook function type for pre/post operation hooks */
export type IndexTableHookFn = (...args: unknown[]) => void;

/**
 * Interface for index table operations
 * Implemented by memory, fs, and elasticsearch adapters
 */
export interface IIndexTable<T> {
  /** Table name (e.g., 'tx', 'account', 'asset') */
  readonly name: string;

  /** Primary key field name (e.g., 'hash', 'address') */
  readonly primaryKey: string;

  /** Unique index definition - single field or composite */
  readonly uniqIndex: string | string[];

  /**
   * Get a document by primary key
   * @param key - Primary key value or composite key object
   * @returns Document or null if not found
   */
  get(key: string | Record<string, unknown>): Promisable<T | null>;

  /**
   * Insert a new document
   * @param doc - Document to insert (must include primary key)
   * @returns Inserted document (may include generated fields like $loki)
   */
  insert(doc: T | Record<string, unknown>): Promisable<T>;

  /**
   * Update an existing document
   * @param key - Primary key value or composite key object
   * @param updates - Partial document with fields to update
   * @returns Updated document
   * @throws Error if document not found
   */
  update(key: string | Record<string, unknown>, updates: Partial<T>): Promisable<T>;

  /**
   * Count documents matching query
   * @param query - Query object (implementation-specific)
   * @returns Number of matching documents
   */
  count(query?: unknown): Promisable<number>;

  /**
   * Reset/clear all documents in the table
   */
  reset(): Promisable<void>;

  /**
   * Register a callback for when the table is ready
   * @param cb - Callback function
   */
  onReady(cb: () => void): void;

  /**
   * Mark the table as ready (called by implementations after initialization)
   */
  markReady(): void;

  /**
   * Register a pre-operation hook
   * @param hookName - Operation name ('insert', 'get', 'update', 'reset')
   * @param fn - Hook function called before the operation
   */
  pre(hookName: string, fn: IndexTableHookFn): void;

  /**
   * Register a post-operation hook
   * @param hookName - Operation name ('insert', 'get', 'update', 'reset')
   * @param fn - Hook function called after the operation
   */
  post(hookName: string, fn: IndexTableHookFn): void;

  /**
   * Generate primary key from document or key object
   * @param doc - String key or document/key object
   * @returns Primary key string (may be MD5 hash for composite keys)
   */
  generatePrimaryKey(doc: string | Record<string, unknown>): string;

  /**
   * Optional: Direct access to underlying collection (LokiJS-based implementations)
   * Type is unknown to avoid coupling to specific implementations
   */
  collection?: unknown;
}

// ============================================================================
// List Result Types
// ============================================================================

export interface IListTransactionsResult {
  transactions: TIndexedTransaction[];
  paging: TPageInfo;
}

export interface IListAssetsResult {
  assets: TIndexedAssetState[];
  account?: TIndexedAccountState | null;
  paging: TPageInfo;
}

export interface IListAccountsResult {
  accounts: TIndexedAccountState[];
  paging: TPageInfo;
}

export interface IListTokensResult {
  tokens: TIndexedTokenState[];
  paging: TPageInfo;
}

export interface IListFactoriesResult {
  factories: TIndexedFactoryState[];
  paging: TPageInfo;
}

export interface IListStakesResult {
  stakes: TIndexedStakeState[];
  paging: TPageInfo;
}

export interface IListDelegationsResult {
  delegations: TIndexedDelegationState[];
  paging: TPageInfo;
}

export interface IListRollupsResult {
  rollups: TIndexedRollupState[];
  paging: TPageInfo;
}

export interface IListRollupBlocksResult {
  blocks: TIndexedRollupBlock[];
  paging: TPageInfo;
}

export interface IListRollupValidatorsResult {
  validators: TIndexedRollupValidator[];
  paging: TPageInfo;
}

export interface IListTokenFactoriesResult {
  tokenFactories: TIndexedTokenFactoryState[];
  paging: TPageInfo;
}

/** Maps table name to its list result type */
export type ListResultTypeMap = {
  tx: IListTransactionsResult;
  account: IListAccountsResult;
  asset: IListAssetsResult;
  token: IListTokensResult;
  factory: IListFactoriesResult;
  stake: IListStakesResult;
  delegation: IListDelegationsResult;
  rollup: IListRollupsResult;
  rollupBlock: IListRollupBlocksResult;
  rollupValidator: IListRollupValidatorsResult;
  tokenFactory: IListTokenFactoriesResult;
};

/** Get list result type for a table name */
export type ListResultFor<K extends keyof ListResultTypeMap> = ListResultTypeMap[K];

// ============================================================================
// IIndexDB Interface
// ============================================================================

/**
 * Main IndexDB interface implemented by all adapters (memory, fs, elasticsearch)
 */
export interface IIndexDB {
  /** Adapter name (e.g., '@ocap/indexdb-memory') */
  name: string;

  /** Adapter version */
  version: string;

  // Index tables
  tx: IIndexTable<IndexTableTypeMap['tx']>;
  account: IIndexTable<IndexTableTypeMap['account']>;
  asset: IIndexTable<IndexTableTypeMap['asset']>;
  token: IIndexTable<IndexTableTypeMap['token']>;
  factory: IIndexTable<IndexTableTypeMap['factory']>;
  stake: IIndexTable<IndexTableTypeMap['stake']>;
  delegation: IIndexTable<IndexTableTypeMap['delegation']>;
  rollup: IIndexTable<IndexTableTypeMap['rollup']>;
  rollupBlock: IIndexTable<IndexTableTypeMap['rollupBlock']>;
  rollupValidator: IIndexTable<IndexTableTypeMap['rollupValidator']>;
  tokenDistribution: IIndexTable<IndexTableTypeMap['tokenDistribution']>;
  tokenFactory: IIndexTable<IndexTableTypeMap['tokenFactory']>;

  // List methods
  listTransactions(params?: Partial<TRequestListTransactions>): Promisable<IListTransactionsResult>;
  listAssets(params?: Partial<TRequestListAssets>): Promisable<IListAssetsResult>;
  listTopAccounts(params?: Partial<TRequestListTopAccounts>): Promisable<IListAccountsResult>;
  listTokens(params?: Partial<TRequestListTokens>): Promisable<IListTokensResult>;
  listFactories(params?: Partial<TRequestListFactories>): Promisable<IListFactoriesResult>;
  listStakes(params?: Partial<TRequestListStakes>): Promisable<IListStakesResult>;
  listDelegations(params?: Partial<TRequestListDelegations>): Promisable<IListDelegationsResult>;
  listRollups(params?: Partial<TRequestListRollups>): Promisable<IListRollupsResult>;
  listRollupBlocks(params?: Partial<TRequestListRollupBlocks>): Promisable<IListRollupBlocksResult>;
  listRollupValidators(params?: Partial<TRequestListRollupValidators>): Promisable<IListRollupValidatorsResult>;
  listTokenFactories(params?: Partial<TRequestListTokenFactories>): Promisable<IListTokenFactoriesResult>;

  /**
   * Search entities by semantic fields (moniker, name, symbol, description)
   * @param keyword - Search keyword
   * @returns Array of matching search results
   */
  search?(keyword: string): Promisable<TSearchResult[]>;

  /**
   * Get related addresses for account migration tracking
   * @param address - Account address
   * @returns Array of related addresses (migratedFrom chain)
   */
  getRelatedAddresses?(address: string): Promisable<string[]>;

  /**
   * Register a callback for when all tables are ready
   * @param cb - Callback function
   */
  onReady(cb: () => void): void;

  /**
   * Attach ready listeners to all tables
   * Should be called after all tables are initialized
   */
  attachReadyListeners(): void;

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