/**
 * API contains various utility functions to communicate with the backend
 *
 * @param {string} endpoint The <a href="https://github.com/radiantearth/stac-api-spec/tree/master/item-search">STAC Item Search</a> API endpoint
 * @param {string} [picturesTiles] The pictures vector tiles endpoint, defaults to /api/map/{z}/{x}/{y}.mvt relatively to endpoint parameter
 * @private
 */
class API {
  constructor(endpoint, picturesTiles) {
    // Parse local endpoints
    if(endpoint.startsWith("/")) {
      endpoint = window.location.href.split("/").slice(0, 3).join("/") + endpoint;
    }
      //endpoint = 'https://geovisio.osc-fr1.scalingo.io';

    // Check endpoint
    if(!API.isValidHttpUrl(endpoint)) {
      throw new Error("endpoint parameter is not a valid URL");
    }

    this._endpoint = endpoint;
    this._picturesTiles = picturesTiles;
  }

  /**
   * Get full URL for listing pictures around a specific location
   *
   * @param {number} lat Latitude
   * @param {number} lon Longitude
   * @returns {string} The corresponding URL
   */
  getPicturesAroundCoordinatesUrl(lat, lon) {
    if(isNaN(parseFloat(lat)) || isNaN(parseFloat(lon))) {
      throw new Error("lat and lon parameters should be valid numbers");
    }

    const factor = 0.0005;
    const bbox = [ lon - factor, lat - factor, lon + factor, lat + factor ].map(d => d.toFixed(4)).join(",");
    return `${this._endpoint}?bbox=[${bbox}]`;
  }

  /**
   * Get full URL for retrieving a specific picture metadata
   *
   * @param {string} picId The picture unique identifier
   * @returns {string} The corresponding URL
   */
  getPictureMetadataUrl(picId) {
    API.isPictureIdValid(picId);
    return `${this._endpoint}?ids=["${picId}"]`;
  }

  /**
   * Get full URL for pictures vector tiles
   *
   * @returns {string} The URL
   * @fires Error If URL can't be determined
   */
  getPicturesTilesUrl() {
    // Explicitly defined URL
    if(this._picturesTiles) {
      return this._picturesTiles;
    }
    // GeoVisio endpoint
  else if(this.isGeoVisioEndpoint()) {
      return this._endpoint.replace("/api/search", "/api/map/{z}/{x}/{y}.mvt");
    }
    // Unknown endpoint, throw an error
    else {
      throw new Error("Pictures vector tiles URL is unknown");
    }
  }

  /**
   * Get a picture ID for a given sequence
   *
   * @param {string} seqId The sequence ID
   * @returns {Promise} Promise resolving on a single picture metadata, or null if no image found
   */
  getPictureThumbnailForSequence(seqId) {
    const url = `${this._endpoint}?limit=1&collections=["${seqId}"]`;

    return fetch(url)
    .then(res => res.json())
    .then(res => res.features.length > 0 ? res.features[0] : null);
  }

  /**
   * Get thumbnail URL for a specific picture
   *
   * @param {string} picId The picture unique identifier
   * @returns {Promise} The corresponding URL on resolve, or undefined if no thumbnail could be found
   */
  getPictureThumbnailUrl(picId) {
    if(this.isGeoVisioEndpoint()) {
      return Promise.resolve(`${this.getGeoVisioRoot()}/api/pictures/${picId}/thumb.jpg`);
    }
    else {
      return fetch(this.getPictureMetadataUrl(picId))
      .then(res => res.json())
      .then(res => Object.values(res?.features.pop()?.assets || {})
        .find(a => a.roles.includes("thumbnail") && a.type.startsWith("image/"))
        ?.href
      );
    }
  }

  /**
   * Checks if given endpoint is a GeoVisio server or not
   *
   * @returns {boolean} True if is a GeoVisio instance
   */
  isGeoVisioEndpoint() {
    return this._endpoint.includes("/api/search");
  }

  /**
   * Get the GeoVisio root API endpoint (instead of search)
   * This is mainly an internal utility
   *
   * @returns {string} The root of GeoVisio endpoint
   * @fires Error If it's not a Geovisio API
   */
  getGeoVisioRoot() {
    if(this.isGeoVisioEndpoint()) {
      return this._endpoint.replace(/\/api\/search.*$/, "");
    }
    else {
      throw new Error("Can't get root endpoint on a third-party STAC API");
    }
  }

  /**
   * Checks URL string validity
   *
   * @param {string} str The URL to check
   * @returns {boolean} True if valid
   */
  static isValidHttpUrl(str) {
    let url;

    try {
      url = new URL(str);
    } catch (_) {
      return false;
    }

    return url.protocol === "http:" || url.protocol === "https:";
  }

  /**
   * Checks picture ID validity
   *
   * @param {string} picId The picture unique identifier
   * @returns {boolean} True if valid
   * @throws {Error} If not valid
   */
  static isPictureIdValid(picId) {
    if(!picId || typeof picId !== "string" || picId.length === 0) {
      throw new Error("picId should be a valid picture unique identifier");
    }
    return true;
  }
}

export default API;
