import { SingleAbstract } from '../singleAbstract';
import * as helper from './helper';
import { errorCode } from '../definitions';

export class Single extends SingleAbstract {
  constructor(protected url: string) {
    super(url);
    this.logger = con.m(this.shortName, '#3db4f2');
    return this;
  }

  private animeInfo: any;

  private displayUrl = '';

  shortName = 'AniList';

  authenticationUrl = 'https://anilist.co/api/v2/oauth/authorize?client_id=1487&response_type=token';

  protected handleUrl(url) {
    if (url.match(/anilist\.co\/(anime|manga)\/\d*/i)) {
      this.type = utils.urlPart(url, 3) === 'anime' ? 'anime' : 'manga';
      this.ids.ani = Number(utils.urlPart(url, 4));
      return;
    }
    if (url.match(/myanimelist\.net\/(anime|manga)\/\d*/i)) {
      this.type = utils.urlPart(url, 3) === 'anime' ? 'anime' : 'manga';
      this.ids.mal = Number(utils.urlPart(url, 4));
      return;
    }
    throw this.errorObj(errorCode.UrlNotSuported, 'Url not supported');
  }

  getCacheKey() {
    return helper.getCacheKey(this.ids.mal, this.ids.ani);
  }

  _getStatus() {
    return parseInt(helper.statusTranslate[this.animeInfo.mediaListEntry.status]);
  }

  _setStatus(status) {
    this.animeInfo.mediaListEntry.status = helper.statusTranslate[status];
  }

  _getScore() {
    if (this.animeInfo.mediaListEntry.score === 0) return 0;
    const score = Math.round(this.animeInfo.mediaListEntry.score / 10);
    if (score === 0) return 1;
    return score;
  }

  _setScore(score) {
    this.animeInfo.mediaListEntry.score = score * 10;
  }

  _getEpisode() {
    return this.animeInfo.mediaListEntry.progress;
  }

  _setEpisode(episode) {
    this.animeInfo.mediaListEntry.progress = parseInt(`${episode}`);
  }

  _getVolume() {
    return this.animeInfo.mediaListEntry.progressVolumes;
  }

  _setVolume(volume) {
    this.animeInfo.mediaListEntry.progressVolumes = volume;
  }

  _getTags() {
    let tags = this.animeInfo.mediaListEntry.notes;
    if (tags === null || tags === 'null') tags = '';
    return tags;
  }

  _setTags(tags) {
    this.animeInfo.mediaListEntry.notes = tags;
  }

  _getTitle() {
    return this.animeInfo.title.userPreferred;
  }

  _getTotalEpisodes() {
    const eps = this.animeInfo.episodes ? this.animeInfo.episodes : this.animeInfo.chapters;
    if (eps === null) return 0;
    return eps;
  }

  _getTotalVolumes() {
    const vol = this.animeInfo.volumes;
    if (!vol) return 0;
    return vol;
  }

  _getDisplayUrl() {
    return this.displayUrl !== '' && this.displayUrl !== null ? this.displayUrl : this.url;
  }

  _getImage() {
    return Promise.resolve(this.animeInfo.coverImage.large);
  }

  _getRating() {
    return Promise.resolve(this.animeInfo.averageScore);
  }

  async _update() {
    let selectId = this.ids.mal;
    let selectQuery = 'idMal';
    if (Number.isNaN(this.ids.mal)) {
      selectId = this.ids.ani;
      selectQuery = 'id';
    }

    const query = `
    query ($id: Int, $type: MediaType) {
      Media (${selectQuery}: $id, type: $type) {
        id
        idMal
        siteUrl
        episodes
        chapters
        volumes
        averageScore
        coverImage{
          large
        }
        title {
          userPreferred
        }
        mediaListEntry {
          status
          progress
          progressVolumes
          score(format: POINT_100)
          repeat
          notes
        }
      }
    }
    `;
    const variables = {
      id: selectId,
      type: this.type!.toUpperCase(),
    };

    this._authenticated = true;

    return this.apiCall(query, variables)
      .catch(e => {
        if (e.code === errorCode.NotAutenticated) {
          this._authenticated = false;
          return this.apiCall(query, variables, false);
        }
        throw e;
      })
      .then(json => {
        this.logger.log('[SINGLE]', 'Data', json);

        this.animeInfo = json.data.Media;

        this.ids.ani = this.animeInfo.id;
        if (Number.isNaN(this.ids.mal) && this.animeInfo.idMal) {
          this.ids.mal = this.animeInfo.idMal;
        }

        this.displayUrl = this.animeInfo.siteUrl;
        this._onList = true;
        if (this.animeInfo.mediaListEntry === null) {
          this._onList = false;
          this.animeInfo.mediaListEntry = {
            notes: '',
            progress: 0,
            progressVolumes: 0,
            repeat: 0,
            score: 0,
            status: 'PLANNING',
          };
        }

        if (!this._authenticated) throw this.errorObj(errorCode.NotAutenticated, 'Not Authenticated');
      });
  }

  async _sync() {
    let query = `
      mutation ($mediaId: Int, $status: MediaListStatus, $progress: Int, $scoreRaw: Int, $notes: String) {
        SaveMediaListEntry (mediaId: $mediaId, status: $status, progress: $progress, scoreRaw: $scoreRaw, notes: $notes) {
          id
          status
          progress
        }
      }
    `;
    const variables = {
      mediaId: this.ids.ani,
      status: this.animeInfo.mediaListEntry.status,
      progress: this.animeInfo.mediaListEntry.progress,
      scoreRaw: this.animeInfo.mediaListEntry.score,
      notes: this.animeInfo.mediaListEntry.notes,
      volumes: null,
    };

    if (this.type === 'manga') {
      query = `
        mutation ($mediaId: Int, $status: MediaListStatus, $progress: Int, $scoreRaw: Int, $notes: String, $volumes: Int) {
          SaveMediaListEntry (mediaId: $mediaId, status: $status, progress: $progress, scoreRaw: $scoreRaw, notes: $notes, progressVolumes: $volumes) {
            id
            status
            progress
            progressVolumes
          }
        }
      `;
      variables.volumes = this.animeInfo.mediaListEntry.progressVolumes;
    }

    return this.apiCall(query, variables);
  }

  protected apiCall = helper.apiCall;

  private getScoreMode() {
    return api.settings.get('anilistOptions').scoreFormat;
  }

  public getScoreCheckbox() {
    switch (this.getScoreMode()) {
      case 'POINT_3':
        return [
          { value: '0', label: api.storage.lang('UI_Score_Not_Rated') },
          { value: '85', label: '🙂' },
          { value: '60', label: '😐' },
          { value: '35', label: '🙁' },
        ];
        break;
      case 'POINT_5':
        return [
          { value: '0', label: api.storage.lang('UI_Score_Not_Rated') },
          { value: '90', label: '★★★★★' },
          { value: '70', label: '★★★★' },
          { value: '50', label: '★★★' },
          { value: '30', label: '★★' },
          { value: '10', label: '★' },
        ];
        break;
      case 'POINT_10_DECIMAL': {
        const decArr = [{ value: '0', label: api.storage.lang('UI_Score_Not_Rated') }];
        for (let i = 1; i < 101; i++) {
          decArr.push({ value: i.toString(), label: (i / 10).toFixed(1) });
        }
        return decArr;
        break;
      }
      case 'POINT_100': {
        const resArr = [{ value: '0', label: api.storage.lang('UI_Score_Not_Rated') }];
        for (let i = 1; i < 101; i++) {
          resArr.push({ value: i.toString(), label: String(i) });
        }
        return resArr;
        break;
      }
      default:
        return super.getScoreCheckbox();
    }
  }

  public getScoreCheckboxValue() {
    const curScore = this.animeInfo.mediaListEntry.score;
    switch (this.getScoreMode()) {
      case 'POINT_3':
        if (!curScore) return 0;
        if (curScore >= 73) return 85;
        if (curScore <= 47) return 35;
        return 60;
        break;
      case 'POINT_5':
        if (!curScore) return 0;
        if (curScore < 20) return 10;
        if (curScore < 40) return 30;
        if (curScore < 60) return 50;
        if (curScore < 80) return 70;
        return 90;
        break;
      case 'POINT_10_DECIMAL':
      case 'POINT_100':
        return curScore;
        break;
      default:
        return super.getScoreCheckboxValue();
    }
  }

  public handleScoreCheckbox(value) {
    switch (this.getScoreMode()) {
      case 'POINT_3':
      case 'POINT_5':
      case 'POINT_10_DECIMAL':
      case 'POINT_100':
        this.animeInfo.mediaListEntry.score = value;
        break;
      default:
        super.handleScoreCheckbox(value);
    }
  }
}
