// core/types/interfaces/ledger.ts
// Ledger state and operation types for Ledger - Verifiable Ledger Kernel

// ============================================================================
// Ledger Entry Types
// ============================================================================

/**
 * Ledger Entry - Immutable record of a signed transaction
 */
export interface ILedgerEntry {
  /** Global incrementing sequence number */
  sequence: number;

  /** Transaction hash (0x + 64 hex chars) */
  hash: string;

  /** Signer DID */
  from: string;

  /** Anti-replay nonce */
  nonce: number;

  /** Public key */
  pk: string;

  /** Signature */
  signature: string;

  /** Base64 encoded raw transaction bytes */
  txRaw: string;

  /** Decoded JSON object (redundant storage for querying) */
  tx: Record<string, unknown>;

  /** Transaction type (e.g., 'fg:t:transfer') */
  txType: string;

  /** Entry timestamp (ms) */
  timestamp: number;

  /** Chain DID this entry belongs to */
  chainId: string;

  /** hash(sequence + txRaw) for Merkle Tree leaf */
  entryHash: string;

  /** Transaction verification context (passkey, gasStakeHeaders, etc.) */
  txExtra?: Record<string, unknown>;

  /** State commit hash (e.g. Dolt commit) that contains this tx's state changes */
  stateCommitHash?: string;

  /** Whether the entry has been finalized (stateCommitHash set and entryHash recomputed) */
  finalized: boolean;
}

/**
 * Storage row type for ledger entries (used by adapters)
 * Uses camelCase to match TypeScript conventions and statedb-dolt patterns
 */
export interface ILedgerEntryRow {
  sequence: number;
  hash: string;
  from: string;
  nonce: number;
  pk: string;
  signature: string;
  txRaw: string;
  tx: string; // JSON string
  txType: string;
  timestamp: number;
  chainId: string;
  entryHash: string;
  txExtra: string | null; // JSON string for verification context
  stateCommitHash: string | null;
  finalized: number; // 0 or 1 for SQL compatibility
}

// ============================================================================
// Checkpoint Types
// ============================================================================

/**
 * Checkpoint - Verifiable snapshot proof at a Ledger position
 */
export interface ICheckpoint {
  /** Checkpoint incrementing number */
  height: number;

  /** Checkpoint's own hash */
  hash: string;

  /** Last entry sequence covered */
  sequence: number;

  /** Previous checkpoint's sequence */
  prevSequence: number;

  /** Merkle root of ledger entries */
  txRoot: string;

  /** Cumulative transaction count */
  txCount: number;

  /** State root (optional, e.g., Dolt commit hash) */
  stateRoot?: string;

  /** Creation timestamp (ms) */
  timestamp: number;

  /** Previous checkpoint hash */
  prevCheckpoint: string | null;

  /** Proposer DID */
  proposer: string;

  /** Proposer signature */
  signature: string;
}

/**
 * Storage row type for checkpoints (used by adapters)
 * Uses camelCase to match TypeScript conventions and statedb-dolt patterns
 */
export interface ICheckpointRow {
  height: number;
  hash: string;
  sequence: number;
  prevSequence: number;
  txRoot: string;
  txCount: number;
  stateRoot: string | null;
  timestamp: number;
  prevCheckpoint: string | null;
  proposer: string;
  signature: string;
}

// ============================================================================
// Merkle Proof Types
// ============================================================================

/**
 * Merkle Proof - Proves an entry belongs to a checkpoint
 */
export interface IMerkleProof {
  /** Entry position (sequence - 1) */
  index: number;

  /** Entry hash (leaf) */
  leaf: string;

  /** Merkle path (sibling hashes) */
  proof: string[];

  /** Checkpoint's txRoot */
  root: string;
}

// ============================================================================
// Health Metrics
// ============================================================================

/**
 * Ledger health snapshot used by /ledger-health monitoring endpoint.
 *
 * Designed to surface the "stuck checkpoint" failure mode (see ledger.ts
 * createCheckpointIfNeeded): if any entry in the pending range stays
 * finalized=0, createCheckpoint is permanently skipped and lag grows
 * unbounded.
 */
export interface ILedgerHealth {
  /** Latest entry sequence in the ledger */
  latestSequence: number;

  /** Latest checkpoint (null if none created yet) */
  latestCheckpoint: {
    height: number;
    sequence: number;
    timestamp: number;
  } | null;

  /** latestSequence - latestCheckpoint.sequence (0 if no checkpoint) */
  checkpointLag: number;

  /** ms since latest checkpoint.timestamp (null if no checkpoint) */
  checkpointAgeMs: number | null;

  /** Count of entries with finalized=0 in the pending range (after latest checkpoint) */
  unfinalizedCount: number;

  /** Timestamp (ms) of oldest unfinalized entry in pending range, null if none */
  oldestUnfinalizedTimestamp: number | null;
}

// ============================================================================
// Configuration Types
// ============================================================================

/**
 * Checkpoint configuration
 */
export interface ICheckpointConfig {
  /** Trigger batch size: create checkpoint every N transactions */
  batchSize: number;

  /** Time trigger: max time since last checkpoint (ms) */
  timeoutMs: number;

  /** Enable auto checkpoint */
  auto: boolean;
}

/**
 * Ledger capabilities for testing
 */
export interface ILedgerCapabilities {
  /** Persistent storage */
  persistence: boolean;

  /** True transaction semantics */
  transaction: boolean;

  /** Supports stateRoot */
  stateRoot: boolean;

  /** Supports schema migration */
  migration: boolean;
}

// ============================================================================
// Input Types
// ============================================================================

/**
 * Signed transaction input for ledger.append()
 */
export interface ISignedTransactionInput {
  txRaw: string;
  tx: Record<string, unknown>;
  txHash: string;
  txType: string;
  from: string;
  nonce: number | string;
  pk: string;
  signature: string;
  chainId: string;
  timestamp: number;
  /** Transaction verification context (passkey, gasStakeHeaders, etc.) */
  txExtra?: Record<string, unknown>;
}

// ============================================================================
// Storage Interface
// ============================================================================

/**
 * Storage adapter interface - SQLite/Dolt implement this
 */
export interface ILedgerStorage {
  // Entry operations
  appendEntries(entries: ILedgerEntry[]): Promise<void>;

  /**
   * Atomically append an entry with database-generated sequence and entryHash.
   * This method is concurrency-safe: sequence is generated within a transaction
   * using MAX(sequence)+1, ensuring no conflicts under multi-instance writes.
   *
   * The entry is created with finalized=false. Call finalizeEntry() after
   * statedb commit to set stateCommitHash, recompute entryHash, and mark finalized.
   *
   * @param entry - Entry data without sequence, entryHash, and finalized (set by storage)
   * @returns Complete entry with sequence, entryHash, and finalized=false
   */
  appendEntryAtomic(entry: Omit<ILedgerEntry, 'sequence' | 'entryHash' | 'finalized'>): Promise<ILedgerEntry>;
  getEntryByHash(hash: string): Promise<ILedgerEntry | null>;
  getEntryBySequence(seq: number): Promise<ILedgerEntry | null>;
  getEntriesByRange(from: number, to: number): Promise<ILedgerEntry[]>;
  getLatestSequence(): Promise<number>;
  /**
   * Iterate entry hashes in a sequence range (inclusive).
   *
   * @param fromSeq - Start sequence (inclusive)
   * @param toSeq - End sequence (inclusive)
   * @param callback - Called for each entry in ascending sequence order
   */
  iterateEntryHashes(
    fromSeq: number,
    toSeq: number,
    callback: (seq: number, entryHash: string) => Promise<void>
  ): Promise<void>;

  /**
   * Finalize an entry: set stateCommitHash, recompute entryHash, mark as finalized.
   *
   * @param txHash - Transaction hash identifying the entry
   * @param stateCommitHash - State commit hash from statedb
   * @param sequence - Entry sequence (needed for entryHash recomputation)
   * @param txRaw - Raw transaction bytes (needed for entryHash recomputation)
   */
  finalizeEntry(txHash: string, stateCommitHash: string, sequence: number, txRaw: string): Promise<void>;

  /**
   * Check if there are any unfinalized entries in the given sequence range (inclusive).
   *
   * @returns true if at least one entry in [fromSeq, toSeq] has finalized=false
   */
  hasUnfinalizedInRange(fromSeq: number, toSeq: number): Promise<boolean>;

  /**
   * Count unfinalized entries and find oldest timestamp in range.
   * Used by the health endpoint to detect stuck checkpoints.
   *
   * @returns count and oldest timestamp (null if count=0)
   */
  getUnfinalizedStats(fromSeq: number, toSeq: number): Promise<{ count: number; oldestTimestamp: number | null }>;

  // Checkpoint operations
  saveCheckpoint(checkpoint: ICheckpoint): Promise<void>;
  getCheckpointByHeight(height: number): Promise<ICheckpoint | null>;
  getLatestCheckpoint(): Promise<ICheckpoint | null>;
  getCheckpointForSequence(seq: number): Promise<ICheckpoint | null>;

  // Schema migration
  getMigrationVersion(): Promise<number>;
  runMigrations(): Promise<void>;
  rollbackMigration(targetVersion: number): Promise<void>;

  // Lifecycle
  init(): Promise<void>;
  close(): Promise<void>;
  reset(): Promise<void>;
}

// ============================================================================
// Ledger Interface
// ============================================================================

/**
 * Ledger interface - Main API
 */
export interface ILedger {
  readonly chainId: string;

  // Write (Append-only)
  append(tx: ISignedTransactionInput): Promise<ILedgerEntry>;

  /**
   * Batch append for offline / single-process scenarios (e.g. data migration, import).
   *
   * NOT concurrency-safe: sequences are computed in memory from a single
   * getLatestSequence() call, so concurrent writers would produce conflicts.
   * Use `append()` for online multi-instance workloads.
   */
  appendBatch(txs: ISignedTransactionInput[]): Promise<ILedgerEntry[]>;

  // Read
  get(hash: string): Promise<ILedgerEntry | null>;
  getBySequence(seq: number): Promise<ILedgerEntry | null>;
  getRange(from: number, to: number): Promise<ILedgerEntry[]>;

  // Metadata
  getLatestSequence(): Promise<number>;

  // State commit hash
  /**
   * Finalize a ledger entry: set stateCommitHash, recompute entryHash, mark finalized.
   *
   * @param txHash - Transaction hash identifying the entry
   * @param stateCommitHash - State commit hash from statedb
   * @param sequence - Entry sequence (needed for entryHash recomputation)
   * @param txRaw - Raw transaction bytes (needed for entryHash recomputation)
   */
  finalizeEntry(txHash: string, stateCommitHash: string, sequence: number, txRaw: string): Promise<void>;

  // Checkpoint
  createCheckpoint(): Promise<ICheckpoint>;
  /** Create a checkpoint if batchSize/timeoutMs threshold is reached. Returns checkpoint or null. */
  createCheckpointIfNeeded(): Promise<ICheckpoint | null>;
  /** Get checkpoint by height, or latest if height is undefined */
  getCheckpoint(height?: number): Promise<ICheckpoint | null>;
  getCheckpointForSequence(seq: number): Promise<ICheckpoint | null>;

  // Merkle
  getMerkleProof(sequence: number): Promise<IMerkleProof>;
  verifyMerkleProof(proof: IMerkleProof): boolean;

  // Monitoring
  getHealth(): Promise<ILedgerHealth>;

  // Lifecycle
  initialize(): Promise<void>;
  close(): Promise<void>;
}

// ============================================================================
// Factory Interface
// ============================================================================

/**
 * Ledger factory interface for testing
 */
export interface ILedgerFactory {
  name: string;
  capabilities: ILedgerCapabilities;
  create(): Promise<ILedger>;
  cleanup(ledger: ILedger): Promise<void>;
}

// ============================================================================
// Table Type Mapping
// ============================================================================

/**
 * Maps ledger table names to their row types
 */
export type LedgerTableTypeMap = {
  entries: ILedgerEntryRow;
  checkpoints: ICheckpointRow;
};

export type LedgerTableName = keyof LedgerTableTypeMap;
