// ─── Key Types ─────────────────────────────────────────────────

/** Long-term identity: Ed25519 for signing + X25519 for DH */
export interface IdentityKeyPair {
  /** Ed25519 public key (32 bytes) — used to sign prekeys */
  signingPublicKey: Uint8Array;
  /** Ed25519 private key (32 bytes) */
  signingPrivateKey: Uint8Array;
  /** X25519 public key (32 bytes) — used for DH in X3DH */
  dhPublicKey: Uint8Array;
  /** X25519 private key (32 bytes) */
  dhPrivateKey: Uint8Array;
}

/** Generic asymmetric keypair (X25519 or Ed25519) */
export interface KeyPair {
  publicKey: Uint8Array;
  privateKey: Uint8Array;
}

/** Medium-term signed pre-key (rotated periodically, e.g. weekly) */
export interface SignedPreKey {
  keyId: number;
  keyPair: KeyPair;
  /** Ed25519 signature over the public key, by the identity signing key */
  signature: Uint8Array;
  timestamp: number;
}

/** Single-use one-time pre-key (consumed during X3DH, then deleted) */
export interface OneTimePreKey {
  keyId: number;
  keyPair: KeyPair;
}

// ─── PreKey Bundle (published to prekey server) ────────────────

/** Bundle fetched from the prekey server to initiate an X3DH session */
export interface PreKeyBundle {
  registrationId: number;
  /** Ed25519 public key (for signature verification) */
  identitySigningKey: Uint8Array;
  /** X25519 public key (for DH) */
  identityDHKey: Uint8Array;
  signedPreKey: {
    keyId: number;
    publicKey: Uint8Array;
    signature: Uint8Array;
  };
  oneTimePreKey?: {
    keyId: number;
    publicKey: Uint8Array;
  };
}

// ─── Wire Messages ─────────────────────────────────────────────

/** Initial message establishing a new session (contains X3DH info + first ratchet message) */
export interface PreKeyMessage {
  registrationId: number;
  /** One-time prekey ID consumed (if any) */
  preKeyId?: number;
  signedPreKeyId: number;
  /** Sender's ephemeral X25519 public key (generated for this X3DH) */
  ephemeralKey: Uint8Array;
  /** Sender's identity X25519 DH public key */
  identityDHKey: Uint8Array;
  /** The first ratchet message, encrypted under the X3DH-derived key */
  message: RatchetMessage;
}

/** Standard Double Ratchet message (used after session is established) */
export interface RatchetMessage {
  /** Sender's current DH ratchet public key */
  dhPublicKey: Uint8Array;
  /** Number of messages in the previous sending chain */
  previousCounter: number;
  /** Message number in the current sending chain */
  counter: number;
  /** AES-256-GCM encrypted payload */
  ciphertext: Uint8Array;
  /** 12-byte GCM nonce */
  nonce: Uint8Array;
}

/** Envelope wrapping either a PreKeyMessage or RatchetMessage on the wire */
export interface ShadeEnvelope {
  type: 'prekey' | 'ratchet';
  content: PreKeyMessage | RatchetMessage;
  timestamp: number;
  senderAddress: string;
}

// ─── Session State ─────────────────────────────────────────────

export interface ChainState {
  /** Current chain key (32 bytes) — ratcheted forward with each message */
  chainKey: Uint8Array;
  /** Number of messages sent/received in this chain */
  counter: number;
}

/** Serializable Double Ratchet session state */
export interface SessionState {
  /** Remote peer's identity DH public key (for verification) */
  remoteIdentityKey: Uint8Array;
  /** Current root key (32 bytes) */
  rootKey: Uint8Array;
  /** Our current sending chain */
  sendChain: ChainState;
  /** Our current receiving chain (null before first received message in a new DH ratchet) */
  receiveChain: ChainState | null;
  /** Our current DH ratchet keypair */
  dhSend: KeyPair;
  /** Remote's current DH ratchet public key */
  dhReceive: Uint8Array | null;
  /** Message count of the previous sending chain (sent in message headers) */
  previousSendCounter: number;
  /** Skipped message keys: Map<"base64(dhPub):counter", messageKey> for out-of-order decryption */
  skippedKeys: Map<string, Uint8Array>;
}

// ─── Constants ─────────────────────────────────────────────────

/** Max number of message keys to skip in a single chain (DoS protection) */
export const MAX_SKIP = 1000;

/** Max total skipped keys to cache per session */
export const MAX_CACHED_SKIPPED_KEYS = 2000;
