/**
 * Hybrid Logical Clock (HLC) for causal ordering in distributed systems.
 * Combines physical time (48 bits) and a logical counter (16 bits).
 * 
 * Patent Reference: AEGIS-PAT-2026-001
 * Component: Consistency Engine (220)
 * 
 * Storage: Store as BIGINT in PostgreSQL for fast sorting
 * Usage: ORDER BY hlc_timestamp ASC for causal ordering
 */

export class HLC {
  private nodeId: string;
  private l: number = 0;  // Maximum physical time seen so far (48 bits)
  private c: number = 0;  // Logical counter (16 bits)

  constructor(nodeId: string) {
    this.nodeId = nodeId;
  }

  /**
   * Returns current time in milliseconds (48-bit friendly)
   */
  private getPhysicalTime(): number {
    return Date.now();
  }

  /**
   * Generates a new HLC timestamp for a local event.
   * Thread-safe in single-threaded Node.js environment.
   */
  now(): bigint {
    const pt = this.getPhysicalTime();
    const lOld = this.l;

    // Update l to max(l_old, pt)
    this.l = Math.max(lOld, pt);

    if (this.l === lOld) {
      // If physical time hasn't advanced, increment counter
      this.c += 1;
    } else {
      // Physical time advanced, reset counter
      this.c = 0;
    }

    return this.pack();
  }

  /**
   * Updates the local clock based on a received remote timestamp.
   * Used during Delta-Sync reconciliation.
   */
  receive(remoteTimestamp: bigint): bigint {
    const [remoteL, remoteC] = HLC.unpack(remoteTimestamp);

    const pt = this.getPhysicalTime();
    const lOld = this.l;

    // Update l to max(l_old, remote_l, pt)
    this.l = Math.max(lOld, remoteL, pt);

    if (this.l === lOld && this.l === remoteL) {
      this.c = Math.max(this.c, remoteC) + 1;
    } else if (this.l === lOld) {
      this.c += 1;
    } else if (this.l === remoteL) {
      this.c = remoteC + 1;
    } else {
      this.c = 0;
    }

    return this.pack();
  }

  /**
   * Packs l (48 bits) and c (16 bits) into a single 64-bit BigInt
   */
  private pack(): bigint {
    return (BigInt(this.l) << BigInt(16)) | (BigInt(this.c) & BigInt(0xFFFF));
  }

  /**
   * Unpacks a 64-bit BigInt back into (l, c)
   */
  static unpack(timestamp: bigint): [number, number] {
    const l = Number(timestamp >> BigInt(16));
    const c = Number(timestamp & BigInt(0xFFFF));
    return [l, c];
  }

  /**
   * Converts an HLC BigInt to a readable string.
   */
  static toString(timestamp: bigint): string {
    const [l, c] = HLC.unpack(timestamp);
    const date = new Date(l);
    const iso = date.toISOString();
    return `${iso} (${c})`;
  }

  /**
   * Compares two HLC timestamps.
   * Returns: -1 if a < b, 0 if a === b, 1 if a > b
   */
  static compare(a: bigint, b: bigint): number {
    if (a < b) return -1;
    if (a > b) return 1;
    return 0;
  }

  /**
   * Returns the node ID for this clock instance.
   */
  getNodeId(): string {
    return this.nodeId;
  }

  /**
   * Creates an HLC timestamp tuple for storage.
   * Returns: { timestamp: bigint, nodeId: string, readable: string }
   */
  createTimestamp(): { timestamp: bigint; nodeId: string; readable: string } {
    const timestamp = this.now();
    return {
      timestamp,
      nodeId: this.nodeId,
      readable: HLC.toString(timestamp),
    };
  }
}

// Singleton instance for the edge node
let globalClock: HLC | null = null;

/**
 * Gets or creates the global HLC instance for this edge node.
 */
export function getGlobalClock(nodeId: string = 'edge-node-001'): HLC {
  if (!globalClock) {
    globalClock = new HLC(nodeId);
  }
  return globalClock;
}

/**
 * Convenience function to get current HLC timestamp.
 */
export function hlcNow(nodeId: string = 'edge-node-001'): bigint {
  return getGlobalClock(nodeId).now();
}

/**
 * Convenience function to create a full timestamp record.
 */
export function createHLCTimestamp(nodeId: string = 'edge-node-001') {
  return getGlobalClock(nodeId).createTimestamp();
}
