Source: src/js/models/CrossRefModel.js

define(["backbone"], (Backbone) => {
  const CACHE_PREFIX = "crossref_";
  /**
   * @class CrossRef
   * @classdesc Handles querying CrossRef API for metadata about a DOI.
   * @classcategory Models
   * @augments Backbone.Model
   * @constructs
   * @augments Backbone.Model
   * @since 2.32.0
   */
  const CrossRef = Backbone.Model.extend(
    /** @lends CrossRef.prototype */
    {
      /** @inheritdoc */
      type: "CrossRef",

      /**
       * Defaults for the CrossRef model.
       * @type {object}
       * @property {string} baseURL - The base URL for the CrossRef API.
       * @property {string} email - The email address to use for "polite"
       * requests. See https://github.com/CrossRef/rest-api-doc#good-manners--more-reliable-service).
       */
      defaults() {
        return {
          baseURL:
            MetacatUI.appModel.get("crossRefAPI") ||
            "https://api.crossref.org/works/",
          email: MetacatUI.appModel.get("emailContact") || "",
        };
      },

      /** @inheritdoc */
      url() {
        let doi = this.get("doi");
        if (!doi) return null;
        // Make sure the DOI is formatted correctly
        doi = MetacatUI.appModel.removeAllDOIPrefixes(doi);
        this.set("doi", doi);
        const doiStr = encodeURIComponent(doi);
        const email = this.get("email");
        const emailStr = email ? `?mailto:${email}` : "";
        const baseURL = this.get("baseURL");
        const url = `${baseURL}${doiStr}${emailStr}`;
        return url;
      },

      /** @inheritdoc */
      fetch() {
        // first check if there's a cached response
        const doi = this.get("doi");
        const cachedResponse = this.getCachedResponse(doi);
        if (cachedResponse) {
          this.set(cachedResponse);
          this.trigger("sync");
          return;
        }

        const url = this.url();
        if (!url) return;
        const model = this;
        // Make the request using native fetch
        fetch(url)
          .then((response) => {
            if (!response.ok) {
              throw new Error("Network response was not ok");
            }
            return response.json();
          })
          .then((responseJSON) => {
            const parsedData = responseJSON.message;
            model.cacheResponse(doi, parsedData);
            model.set(parsedData);
            model.trigger("sync");
          })
          .catch((error) => {
            model.trigger("error", error);
            model.set("error", "fetchError");
            model.set("errorMessage", error.message);
          });
      },

      /**
       * Cache the response from the CrossRef API
       * @param {string} doi The DOI for the response
       * @param {object} response The response from the CrossRef API
       */
      cacheResponse(doi, response) {
        localStorage.setItem(`${CACHE_PREFIX}${doi}`, JSON.stringify(response));
      },

      /**
       * Get the cached response for a DOI
       * @param {string} doi The DOI to get the cached response for
       * @returns {object|null} The cached response or null if there is no cached response
       */
      getCachedResponse(doi) {
        const cachedResponse = localStorage.getItem(`${CACHE_PREFIX}${doi}`);
        if (!cachedResponse) return null;
        return JSON.parse(cachedResponse);
      },

      /** Clear the cache of CrossRef responses */
      clearCache() {
        const keysToRemove = Object.keys(localStorage).filter((key) =>
          key.startsWith(CACHE_PREFIX),
        );
        keysToRemove.forEach((key) => localStorage.removeItem(key));
      },
    },
  );

  return CrossRef;
});