import type { Album, AlbumId, MediaId, MediaItem, Person, PersonId } from '../types';

export interface PersistedState {
  media: MediaItem[];
  albums: Album[];
  people: Person[];
  /**
   * User's permanent object-tag renames (canonical lowercased detector label →
   * chosen label). Optional for backward compatibility with pre-rename data.
   */
  labelAliases?: Record<string, string>;
  deletedLabels?: string[];
  version: number;
}

/**
 * An incremental diff of the library, produced by the store on every persist.
 *
 * Only the entities that actually changed since the previous successful persist
 * are included: an entity appears in `upsert*` when it is new or its object
 * reference changed, and in `remove*` when it disappeared from state. The label
 * maps are included only when they differ from the last persisted values.
 * Every field is optional — an empty object means "nothing changed".
 */
export interface StateChanges {
  /** Media items that were created or modified since the last persist. */
  upsertMedia?: MediaItem[];
  /** Ids of media items that no longer exist (permanently deleted). */
  removeMedia?: MediaId[];
  /** User albums (never system albums) created or modified since the last persist. */
  upsertAlbums?: Album[];
  /** Ids of user albums that no longer exist. */
  removeAlbums?: AlbumId[];
  /** People/pet clusters created or modified since the last persist. */
  upsertPeople?: Person[];
  /** Ids of people/pet clusters that no longer exist. */
  removePeople?: PersonId[];
  /** Whole label-alias map — sent only when it changed (shallow compare). */
  labelAliases?: Record<string, string>;
  /** Whole deleted-label list — sent only when it changed (shallow compare). */
  deletedLabels?: string[];
}

/** The result of a durable byte upload: an adapter-owned ref plus a readable URL. */
export interface StoredBlob {
  /** Durable, adapter-owned reference for the bytes. Survives reloads. */
  ref: string;
  /** A URL the browser can render right now (may be a short-lived signed URL). */
  url: string;
}

/**
 * Storage adapter contract. Implement this to back the gallery with anything:
 * localStorage (default), IndexedDB, a REST/GraphQL API, S3, Postgres, etc.
 *
 * The default UI calls `load` once on mount and `save` (debounced) on change.
 * `putBlob` is optional and only used when importing local File objects that
 * need durable URLs.
 */
export interface StorageAdapter {
  readonly name: string;
  load(): Promise<PersistedState | null>;
  save(state: PersistedState): Promise<void>;
  /** Persist a binary blob and return a stable URL to it. */
  putBlob?(id: string, blob: Blob): Promise<string>;
  clear?(): Promise<void>;
  /**
   * Incremental persistence. When present the store calls this INSTEAD of
   * `save()` on every (debounced) change, passing only the entities that
   * differ from the previously persisted snapshot. Implement it when the
   * backend can apply partial writes — it avoids re-uploading the whole
   * library on every favourite toggle. If this rejects, the store rolls its
   * snapshot back so the next persist retries the same changes.
   */
  applyChanges?(changes: StateChanges): Promise<void>;
  /**
   * Durable byte storage. When present it is preferred over `putBlob()` for
   * imports and camera captures: the store sets `item.src = url` (renderable
   * now) and `item.storageRef = ref` (what survives a reload, so the host can
   * re-sign the URL later). Falls back to `putBlob()` and finally to an
   * inlined `data:` URL when absent.
   */
  putMedia?(id: string, blob: Blob, meta: { name: string; mime: string }): Promise<StoredBlob>;
}
