/**
 * The persister-partykit-client module of the TinyBase project contains the
 * client portion of the PartyKit integration.
 *
 * It contains a Persister which, when run in a PartyKit client environment,
 * lets you save and load Store data from the client to the durable PartyKit
 * cloud storage of a server (that is using the complementary
 * persister-partykit-server module).
 *
 * This enables synchronization of the same Store across multiple clients in a
 * PartyKit party room.
 *
 * Note that both the client and server parts of this Persister are currently
 * experimental as PartyKit is new and still maturing.
 * @see Persistence guides
 * @packageDocumentation
 * @module persister-partykit-client
 * @since v4.3.0
 */
import type {PartySocket} from 'partysocket';
import type {
  OptionalSchemas,
  Store,
} from '../../../store/with-schemas/index.d.ts';
import type {Persister} from '../../with-schemas/index.d.ts';

/**
 * The PartyKitPersister interface represents a Persister that lets you save and
 * load Store data from the client to the durable PartyKit cloud storage of a
 * server.
 *
 * You should use the createPartyKitPersister function to create a
 * PartyKitPersister object.
 *
 * It is a minor extension to the Persister interface and simply provides an
 * extra getConnection method for accessing the PartySocket the Store is being
 * persisted to.
 * @category Persister
 * @since v4.3.14
 */
export interface PartyKitPersister<
  Schemas extends OptionalSchemas,
> extends Persister<Schemas> {
  /**
   * The getConnection method returns the PartySocket the Store is being
   * persisted to.
   * @returns The PartySocket.
   * @example
   * This example creates a Persister object against a newly-created Store and
   * then gets the PartySocket back out again.
   *
   * ```js yolo
   * import {PartySocket} from 'partysocket';
   * import {createStore} from 'tinybase';
   * import {createPartyKitPersister} from 'tinybase/persisters/persister-partykit-client';
   *
   * const partySocket = new PartySocket({
   *   host: '127.0.0.1:1999',
   *   room: 'my_room',
   * });
   * const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
   * const persister = createPartyKitPersister(store, partySocket);
   *
   * console.log(persister.getConnection() == partySocket);
   * // -> true
   *
   * await persister.destroy();
   * ```
   * @category Getter
   * @since v4.3.14
   */
  getConnection(): PartySocket;
}

/**
 * The PartyKitPersisterConfig type describes the configuration of a PartyKit
 * Persister on the client side.
 *
 * The defaults (if used on both the server and client) will work fine, but if
 * you are building more complex PartyKit apps and you need to configure path
 * names, for example, then this is the thing to use.
 * @example
 * When applied to a PartyKit Persister, this PartyKitPersisterConfig will load
 * and save a JSON serialization from and to an end point in your room called
 * `/my_tinybase`, and use HTTP (rather than the default HTTPS) as the protocol.
 *
 * Note that this would require you to also add the matching storePath setting
 * to the TinyBasePartyKitServerConfig on the server side.
 *
 * ```js
 * export const partyKitPersisterConfig = {
 *   storeProtocol: 'http',
 *   storePath: '/my_tinybase',
 * };
 * ```
 * @category Configuration
 * @since v4.3.9
 */
export type PartyKitPersisterConfig = {
  /**
   * The HTTP protocol to use (in addition to the websocket channel). This
   * defaults to 'https' but you may wish to use 'http' for local PartyKit
   * development.
   * @category Configuration
   * @since v4.3.0
   */
  storeProtocol?: 'http' | 'https';
  /**
   * The path used to set and get the whole Store over HTTP(S) on the server.
   * This must match the storePath property of the TinyBasePartyKitServerConfig
   * object used on the server. Both default to '/store'.
   * @category Configuration
   * @since v4.3.0
   */
  storePath?: string;
  /**
   * The prefix at the beginning of the web socket messages sent between the
   * client and the server when synchronizing the Store. Use this to make sure
   * they do not collide with any other message syntax that your room is using.
   * This must match the messagePrefix property of the
   * TinyBasePartyKitServerConfig object used on the server. Both default to an
   * empty string.
   * @category Configuration
   * @since v4.3.0
   */
  messagePrefix?: string;
};

/**
 * The createPartyKitPersister function creates a PartyKitPersister object that
 * can persist the Store to durable PartyKit storage, enabling synchronization
 * of the same Store across multiple clients.
 *
 * This has schema-based typing. The following is a simplified representation:
 *
 * ```ts override
 * createPartyKitPersister(
 *   store: Store,
 *   connection: PartySocket,
 *   configOrStoreProtocol?: PartyKitPersisterConfig | 'http' | 'https',
 *   onIgnoredError?: (error: any) => void,
 * ): PartyKitPersister;
 * ```
 *
 * A PartyKitPersister only supports regular Store objects, and cannot be used
 * to persist the metadata of a MergeableStore.
 *
 * As well as providing a reference to the Store to persist, you must provide a
 * `connection` parameter which is a PartyKit PartySocket that you have already
 * instantiated with details of the host and room.
 *
 * All suitably-equipped TinyBase clients connecting to that room will get to
 * share synchronized Store state.
 *
 * The server room's Store is considered the source of truth. If it is a
 * newly-created room, then calling the save method on this Persister will
 * initiate it. If, however, there is already a Store present on the server, the
 * save method will fail gracefully.
 *
 * In general, you are strongly recommended to use the auto-save and auto-load
 * functionality to stay in sync incrementally with the server, as per the
 * example below. This pattern will handle newly-created servers and
 * newly-created clients - and the synchronization involved in joining rooms,
 * leaving them, or temporarily going offline.
 *
 * See the [PartyKit client socket API
 * documentation](https://docs.partykit.io/reference/partysocket-api/) for more
 * details.
 * @param store The Store to persist.
 * @param connection The PartySocket to use for participating in the PartyKit
 * room.
 * @param configOrStoreProtocol The PartyKitPersisterConfig configuration for
 * the Persister, (or a string to specify a HTTP protocol to use, defaulting to
 * 'https').
 * @param onIgnoredError An optional handler for the errors that the Persister
 * would otherwise ignore when trying to save or load data. This is suitable for
 * debugging persistence issues in a development environment.
 * @returns A reference to the new PartyKitPersister object.
 * @example
 * This example creates a PartyKitPersister object and persists the Store to the
 * browser's IndexedDB storage.
 *
 * ```js yolo
 * import {PartySocket} from 'partysocket';
 * import {createStore} from 'tinybase';
 * import {createPartyKitPersister} from 'tinybase/persisters/persister-partykit-client';
 *
 * const store = createStore()
 *   .setTable('pets', {fido: {species: 'dog'}})
 *   .setTable('species', {dog: {price: 5}})
 *   .setValues({open: true});
 * const partySocket = new PartySocket({
 *   host: '127.0.0.1:1999',
 *   room: 'my_room',
 * });
 * const persister = createPartyKitPersister(store, partySocket);
 * await persister.startAutoLoad();
 * await persister.startAutoSave();
 * // Store will now be synchronized with the room.
 *
 * await persister.destroy();
 * ```
 * @category Creation
 * @since v4.3.0
 */
export function createPartyKitPersister<Schemas extends OptionalSchemas>(
  store: Store<Schemas>,
  connection: PartySocket,
  configOrStoreProtocol?: PartyKitPersisterConfig | 'http' | 'https',
  onIgnoredError?: (error: any) => void,
): PartyKitPersister<Schemas>;
