import type { IdentityKeyPair, SignedPreKey, OneTimePreKey, SessionState } from './types.js';

/** A retired identity kept in history during the rotation grace period */
export interface RetiredIdentity {
  keyPair: IdentityKeyPair;
  retiredAt: number;
}

/**
 * Persisted stream-transfer resume record. Holds enough state for either
 * side of a transfer to resume after restart. The `streamSecret` MUST be
 * encrypted before storage (see `@shade/transfer` for the deviceKey-based
 * AES-GCM at-rest scheme).
 */
export interface PersistedStreamState {
  streamId: string;
  direction: 'send' | 'receive';
  peerAddress: string;
  status: 'active' | 'paused' | 'finished' | 'aborted';
  /** JSON-serialized `StreamMetadata`. */
  metadataJson: string;
  /** JSON-serialized `LaneInitSpec[]`. */
  partitionJson: string;
  /** JSON-serialized per-lane progress array (laneId/nextSeq/bytesProcessed). */
  laneStateJson: string;
  /** JSON-serialized I/O descriptor (file path / file handle reference / buffer). */
  ioDescriptorJson: string;
  /** AES-GCM-encrypted streamSecret (under deviceKey). */
  secretEnc: Uint8Array;
  /** AES-GCM nonce used for `secretEnc`. */
  secretNonce: Uint8Array;
  /**
   * Reserved for future hasher serialization. Empty in v0.2.0; resume
   * re-hashes received bytes from disk.
   */
  overallHashState?: string;
  createdAt: number;
  updatedAt: number;
}

/**
 * Why a peer's fingerprint was deemed verified.
 *  - `user`: human did the side-channel comparison and confirmed.
 *  - `transitive`: trust derived from another verified party (reserved for V3.10).
 *  - `tofu-after-warning`: caller bypassed gate after seeing a warning.
 */
export type PeerVerificationSource = 'user' | 'transitive' | 'tofu-after-warning';

/**
 * Persisted broadcast-channel state (V4.6). Holds the sender-key chain that
 * lets the channel owner encrypt a single ciphertext and fan it out to all
 * paired members.
 *
 * - `ownerRole === 'sender'`: this device created the channel; the record
 *   carries the full chain (chainKey + iteration + signing keypair).
 * - `ownerRole === 'receiver'`: this device joined a channel that
 *   `ownerAddress` owns; we hold a tracking copy of the chain (chainKey +
 *   iteration + signing public key only — no private signing key).
 *
 * `generation` is bumped each time `removeMember` rotates the chain. Stale
 * broadcasts at lower generations are silently dropped on receive.
 */
export interface BroadcastChannelRecord {
  /** Opaque, stable across restarts. UUID-style. */
  channelId: string;
  ownerRole: 'sender' | 'receiver';
  /** The address that owns the channel (i.e. the only sender). */
  ownerAddress: string;
  label?: string;
  /** Sender-key generation — bumped on rotation. Starts at 0. */
  generation: number;
  /** Current chain key (32 bytes). */
  chainKey: Uint8Array;
  /** Counter advanced by `senderKeyEncrypt` / `senderKeyDecrypt`. */
  iteration: number;
  /** Owner's Ed25519 signing public key (32 bytes). */
  signingPublicKey: Uint8Array;
  /** Owner's Ed25519 signing private key. Present iff `ownerRole === 'sender'`. */
  signingPrivateKey?: Uint8Array;
  createdAt: number;
  updatedAt: number;
}

/** Membership row for a broadcast channel (sender-side only). */
export interface BroadcastMemberRecord {
  channelId: string;
  peerAddress: string;
  joinedAt: number;
  /** When this peer was revoked. `null` while still active. */
  removedAt: number | null;
}

/**
 * Persistent record that a peer's safety number was verified at a point
 * in time. `identityVersion` is the local counter for that peer's identity:
 * incrementing it (e.g. via `bumpPeerIdentityVersion` on `acceptIdentityChange`)
 * invalidates the saved verification because `isPeerVerified` requires the
 * stored version to equal the current version.
 */
export interface PeerVerification {
  peerAddress: string;
  fingerprint: string;
  verifiedAt: number;
  verifiedBy: PeerVerificationSource;
  identityVersion: number;
}

/**
 * StorageProvider — abstract interface for persisting cryptographic state.
 *
 * Implementations per platform:
 *  - In-memory (testing)
 *  - IndexedDB (browser)
 *  - SQLite/PostgreSQL (server)
 *  - EncryptedSharedPreferences (Android)
 */
export interface StorageProvider {
  // ─── Identity ──────────────────────────────────────────────

  /** Get our local identity keypair, or null if not yet generated */
  getIdentityKeyPair(): Promise<IdentityKeyPair | null>;

  /** Persist our local identity keypair */
  saveIdentityKeyPair(keyPair: IdentityKeyPair): Promise<void>;

  /** Get our local registration ID (unique per installation) */
  getLocalRegistrationId(): Promise<number>;

  /** Save our local registration ID */
  saveLocalRegistrationId(id: number): Promise<void>;

  // ─── Signed Pre-Keys ──────────────────────────────────────

  /** Get a signed prekey by ID */
  getSignedPreKey(keyId: number): Promise<SignedPreKey | null>;

  /** Persist a signed prekey */
  saveSignedPreKey(key: SignedPreKey): Promise<void>;

  /** Remove a signed prekey (after rotation grace period) */
  removeSignedPreKey(keyId: number): Promise<void>;

  // ─── One-Time Pre-Keys ────────────────────────────────────

  /** Get a one-time prekey by ID */
  getOneTimePreKey(keyId: number): Promise<OneTimePreKey | null>;

  /** Persist a one-time prekey */
  saveOneTimePreKey(key: OneTimePreKey): Promise<void>;

  /** Remove a consumed one-time prekey */
  removeOneTimePreKey(keyId: number): Promise<void>;

  /** Count remaining one-time prekeys */
  getOneTimePreKeyCount(): Promise<number>;

  // ─── Sessions ─────────────────────────────────────────────

  /** Get session state for a peer address (e.g. "device:abc123") */
  getSession(address: string): Promise<SessionState | null>;

  /** Persist session state for a peer */
  saveSession(address: string, state: SessionState): Promise<void>;

  /** Remove session for a peer */
  removeSession(address: string): Promise<void>;

  /** Check if we trust a remote identity key (for TOFU or pinned keys) */
  isTrustedIdentity(address: string, identityKey: Uint8Array): Promise<boolean>;

  /** Save a trusted remote identity key */
  saveTrustedIdentity(address: string, identityKey: Uint8Array): Promise<void>;

  // ─── Identity History (rotation with grace period) ──────

  /** Add an identity to the retired history */
  addRetiredIdentity(identity: RetiredIdentity): Promise<void>;

  /** Get all retired identities (for grace-period decryption) */
  getRetiredIdentities(): Promise<RetiredIdentity[]>;

  /** Remove retired identities older than the given timestamp */
  pruneRetiredIdentities(olderThan: number): Promise<void>;

  // ─── Peer verifications (V3.3) ────────────────────────────

  /**
   * Persist or replace the verification record for a peer. Idempotent
   * upsert on `peerAddress`.
   */
  savePeerVerification(verification: PeerVerification): Promise<void>;

  /** Look up the saved verification for a peer (null if never verified). */
  getPeerVerification(address: string): Promise<PeerVerification | null>;

  /** Remove a peer's verification record (e.g. user revoked trust). */
  removePeerVerification(address: string): Promise<void>;

  /**
   * Returns the current local identity-version counter for a peer.
   * Defaults to 1 when the peer has never been seen. Bumped by
   * `bumpPeerIdentityVersion` whenever the peer rotates identity.
   */
  getPeerIdentityVersion(address: string): Promise<number>;

  /**
   * Increment the peer's identity-version counter and return the new value.
   * Called from `acceptIdentityChange` so previous verification rows (which
   * carry the old version number) become stale.
   */
  bumpPeerIdentityVersion(address: string): Promise<number>;

  // ─── Stream-transfer resume state (optional, added in v0.2.0) ──

  /**
   * Persist or update the stream-state row for a given streamId. Idempotent:
   * upserts on `streamId`. Optional — providers that don't support resume
   * can omit this and consumers will fall back to in-memory state.
   */
  saveStreamState?(state: PersistedStreamState): Promise<void>;

  /** Look up the stream-state row by streamId. Returns null if absent. */
  getStreamState?(streamId: string): Promise<PersistedStreamState | null>;

  /** Remove a stream-state row (e.g. on completion or abort). */
  removeStreamState?(streamId: string): Promise<void>;

  /** List active or paused stream-state rows (for resume on startup). */
  listActiveStreamStates?(
    direction?: 'send' | 'receive',
  ): Promise<PersistedStreamState[]>;

  /** Prune stream-state rows in `'finished' | 'aborted'` status older than `olderThan`. */
  pruneStreamStates?(olderThan: number): Promise<void>;

  // ─── Broadcast channels (V4.6) — optional ─────────────────

  /**
   * Persist or replace the broadcast-channel record. Idempotent upsert on
   * `channelId`. Backends that don't implement broadcast-channel support
   * may omit this; the SDK throws a clear error when the app tries to
   * call `createBroadcastChannel` on such a backend.
   */
  saveBroadcastChannel?(channel: BroadcastChannelRecord): Promise<void>;

  /** Look up a broadcast channel by id. */
  getBroadcastChannel?(channelId: string): Promise<BroadcastChannelRecord | null>;

  /** Enumerate all broadcast channels persisted on this device. */
  listBroadcastChannels?(): Promise<BroadcastChannelRecord[]>;

  /** Drop a broadcast channel and all its membership rows. */
  removeBroadcastChannel?(channelId: string): Promise<void>;

  /** Persist or replace a broadcast-membership row (sender-side only). */
  saveBroadcastMember?(member: BroadcastMemberRecord): Promise<void>;

  /**
   * List membership rows for a channel. Includes revoked members (with
   * `removedAt !== null`); callers filter as needed.
   */
  getBroadcastMembers?(channelId: string): Promise<BroadcastMemberRecord[]>;

  /** Hard-delete a single membership row. */
  removeBroadcastMember?(channelId: string, peerAddress: string): Promise<void>;
}
