/**
 * Guardian-side persistence for received shares.
 *
 * Each guardian holds, per protected user, exactly one record per
 * `setupId`:
 *
 *   {
 *     originalAddress,          // the protected user's Shade address
 *     setupId,                  // disambiguates if a user re-runs setup
 *     shareIndex,               // their assigned x-coordinate
 *     shareBytes,               // base64-encoded Shamir share
 *     shareSecretCiphertext,    // base64 — encrypted backup payload
 *     shareSecretNonce,         // base64 — AES-GCM nonce
 *     setupFingerprint,         // safety number at deposit time
 *     guardianCount, threshold, // informational
 *     receivedAt
 *   }
 *
 * The interface is deliberately minimal so consumers can back it with
 * whatever they already use for app state — IndexedDB, AsyncStorage,
 * SQLite, etc. The package ships an in-memory implementation suitable
 * for tests and small one-device demos. Guardian apps that survive
 * restart MUST supply a persistent implementation.
 */

export interface GuardianShareEntry {
  originalAddress: string;
  setupId: string;
  shareIndex: number;
  shareBytes: string;
  shareSecretCiphertext: string;
  shareSecretNonce: string;
  setupFingerprint: string;
  guardianCount: number;
  threshold: number;
  receivedAt: number;
}

/**
 * The minimal contract a guardian-side share store must implement.
 *
 * Shapes:
 *  - `save` is upsert by (originalAddress, setupId). If a user re-runs
 *    setup with a different setupId, both entries persist; the guardian
 *    can choose which to release based on the recovery-request's
 *    setupId. If the same (originalAddress, setupId) is saved twice,
 *    the second write wins (idempotent re-deposit).
 *  - `get` returns the deposit for the (originalAddress, setupId) pair,
 *    or `null` when none exists.
 *  - `list` enumerates everything (used by guardian-UX widgets).
 *  - `delete` removes a single deposit; the guardian-UX exposes this so
 *    a user can prune deposits from people they no longer wish to
 *    protect.
 */
export interface RecoveryStore {
  save(entry: GuardianShareEntry): Promise<void>;
  get(originalAddress: string, setupId: string): Promise<GuardianShareEntry | null>;
  list(): Promise<GuardianShareEntry[]>;
  delete(originalAddress: string, setupId: string): Promise<void>;
}

/**
 * Process-local in-memory store. Suitable for tests and one-shot demos;
 * LOSES STATE ON RESTART. Production guardian apps must supply a
 * persistent `RecoveryStore` (see `docs/recovery.md` for backing-store
 * recommendations).
 */
export class MemoryRecoveryStore implements RecoveryStore {
  private readonly entries = new Map<string, GuardianShareEntry>();

  async save(entry: GuardianShareEntry): Promise<void> {
    this.entries.set(keyOf(entry.originalAddress, entry.setupId), { ...entry });
  }

  async get(originalAddress: string, setupId: string): Promise<GuardianShareEntry | null> {
    const found = this.entries.get(keyOf(originalAddress, setupId));
    return found === undefined ? null : { ...found };
  }

  async list(): Promise<GuardianShareEntry[]> {
    return Array.from(this.entries.values()).map((e) => ({ ...e }));
  }

  async delete(originalAddress: string, setupId: string): Promise<void> {
    this.entries.delete(keyOf(originalAddress, setupId));
  }
}

function keyOf(originalAddress: string, setupId: string): string {
  return `${originalAddress} ${setupId}`;
}
