/** */
export default class SimpleAnnotationServerV2Adapter {
  /** */
  constructor(canvasId, endpointUrl) {
    this.canvasId = canvasId;
    this.endpointUrl = endpointUrl;
  }

  /** */
  get annotationPageId() {
    return `${this.endpointUrl}/search?uri=${this.canvasId}`;
  }

  /** */
  async create(annotation) {
    return fetch(`${this.endpointUrl}/create`, {
      body: JSON.stringify(SimpleAnnotationServerV2Adapter.createV2Anno(annotation)),
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
      method: 'POST',
    })
      .then((response) => this.all())
      .catch(() => this.all());
  }

  /** */
  async update(annotation) {
    return fetch(`${this.endpointUrl}/update`, {
      body: JSON.stringify(SimpleAnnotationServerV2Adapter.createV2Anno(annotation)),
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
      method: 'POST',
    })
      .then((response) => this.all())
      .catch(() => this.all());
  }

  /** */
  async delete(annoId) {
    return fetch(`${this.endpointUrl}/destroy?uri=${encodeURIComponent(annoId)}`, {
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
      method: 'DELETE',
    })
      .then((response) => this.all())
      .catch(() => this.all());
  }

  /** */
  async get(annoId) {
    // SAS does not have GET for a single annotation
    const annotationPage = await this.all();
    if (annotationPage) {
      return annotationPage.items.find((item) => item.id === annoId);
    }
    return null;
  }

  /** Returns an AnnotationPage with all annotations */
  async all() {
    const resp = await fetch(this.annotationPageId);
    const annos = await resp.json();
    return this.createAnnotationPage(annos);
  }

  /** Creates a V2 annotation from a V3 annotation */
  static createV2Anno(v3anno) {
    const v2anno = {
      '@context': 'http://iiif.io/api/presentation/2/context.json',
      '@type': 'oa:Annotation',
      motivation: 'oa:commenting',
      on: {
        '@type': 'oa:SpecificResource',
        full: v3anno.target.source.id,
      },
    };
    // copy id if it is SAS-generated
    if (v3anno.id && v3anno.id.startsWith('http')) {
      v2anno['@id'] = v3anno.id;
    }
    if (Array.isArray(v3anno.body)) {
      v2anno.resource = v3anno.body.map((b) => this.createV2AnnoBody(b));
    } else {
      v2anno.resource = this.createV2AnnoBody(v3anno.body);
    }
    if (v3anno.target.selector) {
      if (Array.isArray(v3anno.target.selector)) {
        const selectors = v3anno.target.selector.map((s) => this.createV2AnnoSelector(s));
        // create choice, assuming two elements and 0 is default
        v2anno.on.selector = {
          '@type': 'oa:Choice',
          default: selectors[0],
          item: selectors[1],
        };
      } else {
        v2anno.on.selector = this.createV2AnnoSelector(v3anno.target.selector);
      }
      if (v3anno.target.source.partOf) {
        v2anno.on.within = {
          '@id': v3anno.target.source.partOf.id,
          '@type': 'sc:Manifest',
        };
      }
    }
    return v2anno;
  }

  /** */
  static createV2AnnoBody(v3body) {
    const v2body = {
      chars: v3body.value,
    };
    if (v3body.purpose === 'tagging') {
      v2body['@type'] = 'oa:Tag';
    } else {
      v2body['@type'] = 'dctypes:Text';
    }
    if (v3body.format) {
      v2body.format = v3body.format;
    }
    if (v3body.language) {
      v2body.language = v3body.language;
    }
    return v2body;
  }

  /** */
  static createV2AnnoSelector(v3selector) {
    switch (v3selector.type) {
      case 'SvgSelector':
        return {
          '@type': 'oa:SvgSelector',
          value: v3selector.value,
        };
      case 'FragmentSelector':
        return {
          '@type': 'oa:FragmentSelector',
          value: v3selector.value,
        };
      default:
        return null;
    }
  }

  /** Creates an AnnotationPage from a list of V2 annotations */
  createAnnotationPage(v2annos) {
    if (Array.isArray(v2annos)) {
      const v3annos = v2annos.map((a) => SimpleAnnotationServerV2Adapter.createV3Anno(a));
      return {
        id: this.annotationPageId,
        items: v3annos,
        type: 'AnnotationPage',
      };
    }
    return v2annos;
  }

  /** Creates a V3 annotation from a V2 annotation */
  static createV3Anno(v2anno) {
    const v3anno = {
      id: v2anno['@id'],
      motivation: 'commenting',
      type: 'Annotation',
    };
    if (Array.isArray(v2anno.resource)) {
      v3anno.body = v2anno.resource.map((b) => this.createV3AnnoBody(b));
    } else {
      v3anno.body = this.createV3AnnoBody(v2anno.resource);
    }
    let v2target = v2anno.on;
    if (Array.isArray(v2target)) {
      [v2target] = v2target;
    }
    v3anno.target = {
      selector: this.createV3AnnoSelector(v2target.selector),
      source: v2target.full,
    };
    if (v2target.within) {
      v3anno.target.source = {
        id: v2target.full,
        partOf: {
          id: v2target.within['@id'],
          type: 'Manifest',
        },
        type: 'Canvas',
      };
    }
    return v3anno;
  }

  /** */
  static createV3AnnoBody(v2body) {
    const v3body = {
      type: 'TextualBody',
      value: v2body.chars,
    };
    if (v2body.format) {
      v3body.format = v2body.format;
    }
    if (v2body.language) {
      v3body.language = v2body.language;
    }
    if (v2body['@type'] === 'oa:Tag') {
      v3body.purpose = 'tagging';
    }
    return v3body;
  }

  /** */
  static createV3AnnoSelector(v2selector) {
    switch (v2selector['@type']) {
      case 'oa:SvgSelector':
        return {
          type: 'SvgSelector',
          value: v2selector.value,
        };
      case 'oa:FragmentSelector':
        return {
          type: 'FragmentSelector',
          value: v2selector.value,
        };
      case 'oa:Choice':
        /* create alternate selectors */
        return [
          this.createV3AnnoSelector(v2selector.default),
          this.createV3AnnoSelector(v2selector.item),
        ];
      default:
        return null;
    }
  }
}