import { assert, createBaseUrl } from './util/mod.ts';
import type { BaseOptions } from './config.ts';
import type { FavoritePayload } from './models/notifications.ts';


export class Notification {
  constructor(private readonly __config: BaseOptions) {}

  /**
   * Send click notification with ticket. Tries to queue fetch
   * request with `keepalive` option, with one retry if it fails.
   *
   * @param ticket is present on product and variant
   *
   * @example
   * ```ts
   * await api.notify.click('OzE7cHJ...yM7Mjg7');
   * ```
   * @see https://docs.elevate.voyado.cloud/elevate/4/integration/api/specifications/storefront/v3/notifications/click/
   */
  click(ticket: string) {
    return this.__notification('click', { ticket });
  }

  /**
   * Send impression notification. Tries to queue fetch
   * request with `keepalive` option, with one retry if it fails.
   *
   * @beta ⚠️ This request is currently limited to a private beta and will fail otherwise.
   *
   * @param ticket attached to a sponsored product or list
   *
   * @example
   * ```ts
   * await api.notify.adImpression('OzE7cHJ...yM7Mjg7');
   * ```
   * @see https://docs.elevate.voyado.cloud/elevate/4/integration/api/specifications/storefront/v3/notifications/ad-impression/
   */
  adImpression(ticket: string) {
    return this.__notification('ad-impression', { ticket });
  }

  /**
   * Send add to cart notification with ticket. Tries to queue fetch
   * request with `keepalive` option, with one retry if it fails.
   *
   * @param ticket is present on product and variant
   *
   * @example
   * ```ts
   * await api.notify.addToCart('OzE7cHJ...jOzI4Ow');
   * ```
   * @see https://docs.elevate.voyado.cloud/elevate/4/integration/api/specifications/storefront/v3/notifications/add-to-cart/
   */
  addToCart(ticket: string) {
    return this.__notification('add-to-cart', { ticket });
  }

   /**
   * Send add favorite notification with product-key. Tries to queue fetch
   * request with `keepalive` option, with one retry if it fails.
   *
   * @param productKeyOrPayload a `Product.key` or object with variant or product key
   *
   * @example
   * ```ts
   * await api.notify.addFavorite('pk_123456');
   * await api.notify.addFavorite({ variantKey: 'vk_234567' });
   * ```
   * @see https://docs.elevate.voyado.cloud/elevate/4/integration/api/specifications/storefront/v3/notifications/add-favorite/
   */
  addFavorite(productKeyOrPayload: string | FavoritePayload) {
    const body = typeof productKeyOrPayload === 'string' ?
      { productKey: productKeyOrPayload } : productKeyOrPayload;
    return this.__notification('add-favorite', body);
  }

  /**
   * Send remove favorite notification with product-key. Tries to queue fetch
   * request with `keepalive` option, with one retry if it fails.
   *
   * @param productKeyOrPayload a `Product.key` or object with variant or product key
   *
   * @example
   * ```ts
   * await api.notify.removeFavorite('pk_123456');
   * await api.notify.removeFavorite({ variantKey: 'vk_234567' });
   * ```
   * @see https://docs.elevate.voyado.cloud/elevate/4/integration/api/specifications/storefront/v3/notifications/remove-favorite/
   */
  removeFavorite(productKeyOrPayload: string | FavoritePayload) {
    const body = typeof productKeyOrPayload === 'string' ?
      { productKey: productKeyOrPayload } : productKeyOrPayload;
    return this.__notification('remove-favorite', body);
  }

  /**
   * Send a notification to remove recent searches for the current `customerKey`.
   *
   * The values in the array should match values on `RecentSearch.q` returned
   * from `Autocomplete.recentSearches`, to ensure they are removed correctly.
   *
   * Tries to queue fetch request with `keepalive` option, with one retry if it fails.
   *
   * @param phrases An array of recent search phrases to remove or the string 'removeAll' to remove all phrases.
   *
   * @example
   * ```ts
   * await api.notify.removeRecentSearches('removeAll');
   * await api.notify.removeRecentSearches(['gift card']);
   * await api.notify.removeRecentSearches(['dress', 'jacket', 'shoes']);
   * ```
   * @see https://docs.elevate.voyado.cloud/elevate/4/integration/api/specifications/storefront/v3/notifications/remove-recent-searches/
   */
  removeRecentSearches(phrases: string[] | 'removeAll') {
    return this.__notification('remove-recent-searches', phrases === 'removeAll' ?
      { removeAll: true } : { phrases });
  }

  /**
   * Send a notification to remove recently viewed products for the current `customerKey`.
   *
   * When removing individual products, `Product.key` should be used as ID for specific products
   * to remove from Recently viewed.
   *
   * Tries to queue fetch request with `keepalive` option, with one retry if it fails.
   *
   * @param productKeys An array of productKeys to remove or the string 'removeAll' to remove all products.
   *
   * @example
   * ```ts
   * await api.notify.removeRecentlyViewed('removeAll');
   * await api.notify.removeRecentlyViewed(['pk_123456']);
   * ```
   * @see https://docs.elevate.voyado.cloud/elevate/4/integration/api/specifications/storefront/v3/notifications/remove-recently-viewed/
   */
  removeRecentlyViewed(productKeys: string[] | 'removeAll') {
    return this.__notification('remove-recently-viewed', productKeys === 'removeAll' ?
      { removeAll: true } : { productKeys });
  }

  /**
   * Send end notification. Tries to queue fetch request with `keepalive` option,
   * with one retry if it fails.
   *
   * @example
   * ```ts
   * await api.notify.end();
   * ```
   * @see https://docs.elevate.voyado.cloud/elevate/4/integration/api/specifications/storefront/v3/notifications/end/
   */
  end() {
    return this.__notification('end');
  }

  private async __notification(endpoint: string, body?: object) {
    const send = async (keepalive: boolean) => {
      const url = await createBaseUrl(`notifications/${endpoint}`, this.__config);
      const data = body ? JSON.stringify(body) : undefined;
      const res = await fetch(url, { method: 'POST', body: data, keepalive });
      assert(res.ok);
    };

    try {
      await send(true);
    } catch {
      // TODO(csv): Add `this.__config` option to skip retrying without keepalive?
      // E.g. `Config.notificationMode: 'keepaliveWithRetry' | 'keepalive' | 'regular'

      // Retry without `keepalive`
      await send(false);
    }
  }
}
