import {Injectable} from '@angular/core';
import {HttpClient, HttpErrorResponse} from "@angular/common/http";
import {throwError} from 'rxjs';
import {ResultLandingInfo} from '../../utils/entities/resultLandingInfo';
import {EnvProperties} from '../../utils/properties/env-properties';
import {ParsingFunctions} from '../landing-utils/parsingFunctions.class';
import {map} from "rxjs/operators";
import {HostedByCollectedFrom, Organization} from "../../utils/result-preview/result-preview";
import {Dates, Identifier, StringUtils} from "../../utils/string-utils.class";
import {properties} from "../../../../environments/environment";
import {HelperFunctions} from "../../utils/HelperFunctions.class";
import {SearchFields} from "../../utils/properties/searchFields";

@Injectable()
export class ResultLandingService {

  constructor(private http: HttpClient) {
    this.parsingFunctions = new ParsingFunctions();
  }

  public parsingFunctions: ParsingFunctions;
  resultLandingInfo: ResultLandingInfo;

  private buildResultLandingInfoUrl(id: string, identifier: Identifier, type: string): string {
    if (id) {
      let url = properties.searchAPIURLLAst;
      if (type === 'publication') {
        url += 'publications/';
      } else if (type === 'dataset') {
        url += 'datasets/';
      } else if (type === 'software') {
        url += 'software/';
      } else if (type === 'orp' || type === 'other') {
        url += 'other/';
      } else if (type === 'result') {
        url += 'results/';
      }
      url += id + '?format=json';
      return url;
    } else if (identifier) {
      // pid = "10.3389/fphys.2014.00466";
      let url = properties.searchAPIURLLAst + "resources2";
      url += "?query=(pid exact \"" + encodeURIComponent(identifier.id) + "\")&type=";
      if (type === 'publication') {
        url += 'publications';
      } else if (type === 'dataset') {
        url += 'datasets';
      } else if (type === 'software') {
        url += 'software';
      } else if (type === 'orp') {
        url += 'other';
      } else if (type === 'result') {
        url += 'results';
      }
      url += "&format=json";
      return url;
    }
  }

  getResultLandingInfo(id: string, identifier: Identifier, type: string,
                       subjectsVocabulary: any,
                       properties: EnvProperties): any {
    let url: string = this.buildResultLandingInfoUrl(id, identifier, type);

    let finalUrl: string = (properties.useCache) ? (properties.cacheUrl + encodeURIComponent(url)) : url;
    return this.http.get(finalUrl)
        .pipe(map(res => {
          if (!id && identifier) {
            if (!res['results'] || res['results'].length == 0) {
              throw new HttpErrorResponse({
                status: 404,
                statusText: "Not found",
                url: finalUrl,
                error: "Http failure response for " + finalUrl + ": 404 Not Found"
              });
            }
            return res['results'][0];
          } else {
            return res;
          }
        }))
        .pipe(map(res => [res['result']['header']['dri:status'], res['result']['metadata']['oaf:entity'], res]))
        .pipe(map(res => [
          res[1]['oaf:result'],                 // 0
          res[1]['oaf:result']['title'],        // 1
          res[1]['oaf:result']['rels']['rel'],  // 2
          res[1]['oaf:result']['children'],     // 3
          res[1]['oaf:result']['pid'],          // 4
          res[1]['oaf:result']['journal'],      // 5
          res[1]['oaf:result']['language'],     // 6
          res[1]['oaf:result']['eoscifguidelines'],     // 7
          res[1]['oaf:result']['subject'],      // 8
          res[1]['oaf:result']['context'],      // 9
          res[1]['oaf:result']['creator'],      // 10
          res[1]['oaf:result']['country'],     // 11
          res[1]['oaf:result']['programmingLanguage'],  // 12 - software
          //res[1]['oaf:result']['resulttype'],
          (res[1]['extraInfo'] !== undefined && res[1]['extraInfo']['references'] !== undefined)
              ? res[1]['extraInfo']['references']['reference'] : null,  // 13
          res[0],                               // 14
          res[2],                               // 15
        ]))
        .pipe(map(res => this.parseResultLandingInfo(res, subjectsVocabulary, properties)));
  }

  // getProvenanceActionVocabulary (properties: EnvProperties): any {
  //   let url = properties.vocabulariesAPI+"dnet:provenanceActions.json";
  //
  //   return this.http.get((properties.useCache) ? (properties.cacheUrl + encodeURIComponent(url)) : url)
  //     .pipe(map(res => res['terms']))
  //     .pipe(map(res => this.parseProvenanceActionVocabulary(res, properties)));
  // }
  //
  // parseProvenanceActionVocabulary(terms: any, properties: EnvProperties) {
  //   var provenanceActionVocabulary: {} = {};
  //   for(let term of terms) {
  //     provenanceActionVocabulary[term.code] = term.englishName;
  //   }
  //   return provenanceActionVocabulary;
  // }

  private handleError(error: HttpErrorResponse) {
    // in a real world app, we may send the error to some remote logging infrastructure
    // instead of just logging it to the console
    console.log(error);
    return throwError(error || 'Server error');
  }

  parseResultLandingInfo(data: any, subjectsVocabulary: any, properties: EnvProperties): any {
    this.resultLandingInfo = new ResultLandingInfo();
    // res
    this.resultLandingInfo.record = data[15];
    this.resultLandingInfo.objIdentifier = data[15]["result"]["header"]["dri:objIdentifier"];
    this.resultLandingInfo.relcanId = ParsingFunctions.parseRelCanonicalId(this.resultLandingInfo.record, "result");
    this.resultLandingInfo.resultType = data[0].resulttype.classid;

    // res['result']['metadata']['oaf:entity']['oaf:result']
    if (data[0] != null) {
      let date: string = (data[0].dateofacceptance ? data[0].dateofacceptance : '') + ''; // transform to string in case it is an integer
      this.resultLandingInfo.date = (date && (date).indexOf('-') !== -1) ? date.split('-')[0] : date;
      this.resultLandingInfo.dateofacceptance = data[0].dateofacceptance ? Dates.getDate(data[0].dateofacceptance) : null;
      this.resultLandingInfo.publisher = data[0].publisher;
      this.resultLandingInfo.description = this.parsingFunctions.parseDescription(data[0] && data[0].description ? data[0].description : []);
      this.resultLandingInfo.embargoEndDate = data[0].embargoenddate ? Dates.getDate(data[0].embargoenddate) : null;

      if(data[0].hasOwnProperty("publiclyfunded") && data[0].publiclyfunded) {
        this.resultLandingInfo.publiclyFunded = data[0].publiclyfunded;
      }
      if((data[0].hasOwnProperty("isgreen") && data[0].isgreen)
        || (data[0].hasOwnProperty("openaccesscolor") && data[0].openaccesscolor)
        || (data[0].hasOwnProperty("isindiamondjournal") && data[0].isindiamondjournal)) {
        this.resultLandingInfo.oaRoutes = {
          "green": data[0].isgreen,
          "oaColor": data[0].openaccesscolor,
          "isInDiamondJournal":data[0].isindiamondjournal
        };
      }
    }

    if (data[0]['bestaccessright'] && data[0]['bestaccessright'].hasOwnProperty("classname")) {
      this.resultLandingInfo.accessMode = data[0]['bestaccessright'].classname;
    }
    // res['result']['metadata']['oaf:entity']['oaf:result']['title']
    if (data[1] != null) {
      if (Array.isArray(data[1])) {
        for (let i = 0; i < data[1].length; i++) {
          if (data[1][i] && data[1][i].content) {
            if (!this.resultLandingInfo.title || data[1][i].classid == "main title") {
              this.resultLandingInfo.title = StringUtils.HTMLToString(String(data[1][i].content));
            }
            if (!this.resultLandingInfo.subtitle && data[1][i].classid === 'subtitle') {
              this.resultLandingInfo.subtitle = StringUtils.HTMLToString(String(data[1][i].content));
            }
            // if(data[1][i].classid == "main title") {
            //   break;
            // }
          }
        }
        if (this.resultLandingInfo.title === this.resultLandingInfo.subtitle) {
          this.resultLandingInfo.subtitle = "";
        }
        if (!this.resultLandingInfo.title) {
          this.resultLandingInfo.title = "";
        }
        // this.resultLandingInfo.title = (data[1][0] && data[1][0].content) ? String(data[1][0].content) : "";
      } else {
        this.resultLandingInfo.title = (data[1] && data[1].content) ? StringUtils.HTMLToString(String(data[1].content)) : "";
      }
    }

    // res['result']['metadata']['oaf:entity']['oaf:result']['rels']['rel']
    if (data[2] != null) {
      let relation;
      let length = Array.isArray(data[2]) ? data[2].length : 1;

      for (let i = 0; i < length; i++) {
        relation = Array.isArray(data[2]) ? data[2][i] : data[2];
        if (relation.hasOwnProperty("to")) {
          if (relation['to'].class && relation['to'].class.toLowerCase() == "isproducedby") {
            this.resultLandingInfo.fundedByProjects = this.parsingFunctions.parseFundingByProjects(this.resultLandingInfo.fundedByProjects, relation);
          }
          if (relation['to'].scheme && relation['to'].scheme == "dnet:result_result_relations") {
            let relationName: string = relation.to.class;
            if (!this.resultLandingInfo.relatedClassFilters.has(relationName)) {
              this.resultLandingInfo.relatedClassFilters.add(relationName);
            }

            let provenanceAction: string = relation.provenanceaction;

            this.resultLandingInfo.relatedResults = this.parsingFunctions.parseResults(this.resultLandingInfo.relatedResults, relation, provenanceAction, relationName);
          } else if (relation['to'].class && relation['to'].class.toLowerCase() == "hasauthorinstitution") {
            this.resultLandingInfo.organizations = this.parseRelatedOrganizations(this.resultLandingInfo.organizations, relation);
          }
        }
      }
    }

    // res['result']['metadata']['oaf:entity']['oaf:result']['children']
    if (data[3] != null) {
      if (data[3].hasOwnProperty("result")) {
        this.resultLandingInfo.deletedByInferenceIds = [];
        let length = Array.isArray(data[3]['result']) ? data[3]['result'].length : 1;

        for (let i = 0; i < length; i++) {
          let result = Array.isArray(data[3]['result']) ? data[3]['result'][i] : data[3]['result'];
          this.resultLandingInfo.deletedByInferenceIds.push(result.objidentifier);
        }

      }

      if (data[3].hasOwnProperty("instance")) {
        this.resultLandingInfo.hostedBy_collectedFrom = new Array<HostedByCollectedFrom>();


        this.resultLandingInfo.types = new Array<string>();
        let types = new Set<string>();

        let counter = 0;
        let instance;

        let length = Array.isArray(data[3]['instance']) ? data[3]['instance'].length : 1;

        for (let i = 0; i < length; i++) {
          instance = Array.isArray(data[3]['instance']) ? data[3]['instance'][i] : data[3]['instance'];

          this.parsingFunctions.parseTypes(this.resultLandingInfo.types, types, instance);

          if (instance.hasOwnProperty("webresource")) {
            let url;
            if (!Array.isArray(instance['webresource'])) {
              url = instance['webresource'].url;
            } else {
              url = instance['webresource'][0].url;
            }
            if (url.includes('&amp;')) {
              url = url.replace(/&amp;/gmu, '&');
            }

            /**********************************************************/
            if (instance.hasOwnProperty("hostedby")) {
              this.parsingFunctions.parseHostedBy_collectedFrom(this.resultLandingInfo.hostedBy_collectedFrom, instance, url, this.resultLandingInfo.accessMode);
            }
            /**********************************************************/
          }
        }

        /* Order Download from via openness*/
        this.resultLandingInfo.hostedBy_collectedFrom.sort(this.parsingFunctions.compareHostedByCollectedFrom);
      }

      if (data[3].hasOwnProperty("externalreference")) {
        let externalResults: Map<string, Map<string, string>> = this.parseBioentitiesAndSoftware(data[3]);
        this.resultLandingInfo.bioentities = externalResults;
      }
    }

    // res['result']['metadata']['oaf:entity']['oaf:result']['pid']
    if (data[4] != null) {
      this.resultLandingInfo.identifiers = this.parsingFunctions.parseIdentifiers(data[4]);
    }

    // res['result']['metadata']['oaf:entity']['oaf:result']['journal']
    if (data[5] != null) {
      this.resultLandingInfo.journal = {
        "journal": "",
        "issn": "",
        "lissn": "",
        "eissn": "",
        "issue": "",
        "volume": "",
        "start_page": "",
        "end_page": ""
      }

      this.resultLandingInfo.journal['journal'] = data[5].content;
      this.resultLandingInfo.journal['issn'] = data[5].issn;
      this.resultLandingInfo.journal['lissn'] = data[5].lissn;
      this.resultLandingInfo.journal['eissn'] = data[5].eissn;
      this.resultLandingInfo.journal['issue'] = data[5].iss;
      this.resultLandingInfo.journal['volume'] = data[5].vol;
      this.resultLandingInfo.journal['start_page'] = data[5].sp;
      this.resultLandingInfo.journal['end_page'] = data[5].ep;
    }

    // res['result']['metadata']['oaf:entity']['oaf:result']['language']
    if (data[6] != null) {
      this.resultLandingInfo.languages = this.parsingFunctions.parseLanguages(data[6]);
      // let languagesAndCodes: string[][] = this.parsingFunctions.parseLanguages(data[6]);
      // this.resultLandingInfo.languages = languagesAndCodes[0];
      // this.resultLandingInfo.languageCodes = languagesAndCodes[1];
    }

    // res['result']['metadata']['oaf:entity']['oaf:result']['country']
    if (data[11] != null) {
      this.resultLandingInfo.countries = this.parsingFunctions.parseCountries(data[11]);
    }

    // res['result']['metadata']['oaf:entity']['oaf:result']['eoscifguidelines']
    if (data[7] != null) {
      this.resultLandingInfo.eoscSubjects = this.parsingFunctions.parseEoscSubjects(data[7]);
    }

    // res['result']['metadata']['oaf:entity']['oaf:result']['subject']
    if (data[8] != null) {
      let subjectResults: [string[], Map<string, string[]>, Map<string, string[]>, string[], string[]] = this.parsingFunctions.parseAllSubjects(data[8], subjectsVocabulary);
      this.resultLandingInfo.subjects = subjectResults[0];
      this.resultLandingInfo.otherSubjects = subjectResults[1];
      this.resultLandingInfo.classifiedSubjects = subjectResults[2];

      if (subjectResults[3]) {
        let searchFieldsHelper: SearchFields = new SearchFields();
        subjectResults[3].forEach(element => {
          this.resultLandingInfo.fos.push(
            {id: element, label: searchFieldsHelper.getFosParameter() == "foslabel" ? element.replace(/^\d+/, '').trim() : element}
          );
        });
      }
      if (this.resultLandingInfo.fos) {
        this.resultLandingInfo.fos.sort((a, b) => a.id.localeCompare(b.id));
      }
      if(properties.dashboard != "irish") {
        this.resultLandingInfo.sdg = subjectResults[4];
        if (this.resultLandingInfo.sdg) {
          this.resultLandingInfo.sdg.sort((a, b) => {
            return HelperFunctions.sortSDGs(a, b);
          })
        }
      }

      // if(!this.resultLandingInfo.eoscSubjects) {
      //   this.resultLandingInfo.eoscSubjects = subjectResults[5];
      // }
    }

    this.resultLandingInfo.hostedBy_collectedFrom = this.parsingFunctions.addPublisherToHostedBy_collectedFrom(
        this.resultLandingInfo.hostedBy_collectedFrom, this.resultLandingInfo.publisher,
        this.resultLandingInfo.journal?.journal, this.resultLandingInfo.identifiers);

    // res['result']['metadata']['oaf:entity']['oaf:result']['programmingLanguage']
    if (data[12] != null) {
      this.resultLandingInfo.programmingLanguages = this.parsingFunctions.parseProgrammingLanguages(data[12]);
    }

    // res['result']['metadata']['oaf:entity']['extraInfo']['references']['reference']
    if (data[13] != null) {
      this.resultLandingInfo.references = this.parsingFunctions.parseReferences(data[13]);
    }

    // res['result']['metadata']['oaf:entity']['oaf:result']['context']
    if (data[9] != null) {
      this.resultLandingInfo.contexts = this.parsingFunctions.parseContexts(data[9]);
    }

    // res['result']['header']['dri:status']
    if (data[14] != null && data[14] == "under curation") {
      this.resultLandingInfo.underCurationMessage = true;
    } else {
      this.resultLandingInfo.underCurationMessage = false;
    }

    // res['result']['metadata']['oaf:entity']['oaf:result']['creator']
    if (data[10] != null) {
      if (this.resultLandingInfo.authors == undefined) {
        this.resultLandingInfo.authors = new Array<{ "fullName": string, "orcid": string, "orcid_pending": string }>();
      }

      let authors = data[10];
      let length = Array.isArray(authors) ? authors.length : 1;

      for (let i = 0; i < length; i++) {
        let author = Array.isArray(authors) ? authors[i] : authors;
        if (author) {
          if (author.orcid) {
            author.orcid = author.orcid.toUpperCase();
          }
          if (author.orcid_pending) {
            author.orcid_pending = author.orcid_pending.toUpperCase();
          }

          if(this.resultLandingInfo['authors'][author.rank] && this.resultLandingInfo['authors'][author.rank].fullName == author.content) {
            if(!author.orcid && this.resultLandingInfo['authors'][author.rank].orcid) {
              author.orcid = this.resultLandingInfo['authors'][author.rank].orcid;
            } else if(!author.orcid_pending && this.resultLandingInfo['authors'][author.rank].orcid_pending) {
              author.orcid_pending = this.resultLandingInfo['authors'][author.rank].orcid_pending;
            }
          }

          this.resultLandingInfo['authors'][author.rank] = {
            "fullName": author.content,
            "orcid": author.orcid,
            "orcid_pending": author.orcid_pending
          };
        }
      }
      this.resultLandingInfo.authors = this.resultLandingInfo.authors.filter(function (item) {
        return (item != undefined && item.fullName != undefined);
      });
    }

    // res['result']['metadata']['oaf:entity']['oaf:result']['measure']
    if (data[0]?.measure) {
      this.resultLandingInfo.measure = this.parsingFunctions.parseMeasures(data[0].measure);
    }

    this.resultLandingInfo.relatedResults = this.parsingFunctions.sortByPercentage(this.resultLandingInfo.relatedResults);

    return this.resultLandingInfo;
  }

  parseRelatedOrganizations(organizations: Organization[], relation: any): Organization[] {
    if (organizations == undefined) {
      organizations = []
    }

    let organization: {
      "name": string, "shortname": string,
      "id": string, "websiteUrl": string,
      "country": string, "trust": number
    } = {
      name: "", shortname: "",
      id: "", websiteUrl: "",
      country: "", trust: null
    };

    organization.id = relation['to'].content;
    organization.name = relation.legalname;
    organization.shortname = relation.legalshortname;
    organization.websiteUrl = relation.websiteurl;
    if (relation.country) {
      organization.country = relation.country.classname;
    }
    if (relation.trust) {
      organization.trust = Math.round(relation.trust * 100);
    }

    organizations.push(organization);
    return organizations;
  }

  parseBioentitiesAndSoftware(children: any): Map<string, Map<string, string>> {
    let bioentities: Map<string, Map<string, string>>;

    let length = Array.isArray(children['externalreference']) ? children['externalreference'].length : 1;

    let externalreference;
    for (let i = 0; i < length; i++) {
      externalreference = Array.isArray(children['externalreference']) ? children['externalreference'][i] : children['externalreference'];

      if (externalreference.hasOwnProperty("qualifier")) {
        if (externalreference['qualifier'].classid == "accessionNumber") {

          if (bioentities == undefined) {
            bioentities = new Map<string, Map<string, string>>();
          }

          if (!bioentities.has(externalreference.sitename)) {
            bioentities.set(externalreference.sitename, new Map<string, string>());
          }
          bioentities.get(externalreference.sitename).set(externalreference.refidentifier, externalreference.url);

        }
      }
    }

    return bioentities;
  }

  getEnermapsDetails(id: string) {
    let url = properties.enermapsURL + "/api/db/metadata?shared_id=eq." + id;
    return this.http.get((properties.useLongCache) ? (properties.cacheUrl + encodeURIComponent(url)) : url)
        // return this.http.get(url)
        .pipe(map(res => this.parseEnermapsDetails(res)));
  }

  parseEnermapsDetails(response: any) {
    let details = Array.isArray(response) ? response[0] : response;
    let metadata = (details && details.metadata) ? details.metadata : null;

    let entries = [];
    let keys = metadata ? Object.keys(metadata) : null;
    for (let key of keys) {
      if (key != "shared_id" && key && metadata[key]) {
        entries.push([key + "", metadata[key] + ""]);
      }
    }
    return entries;
  }
}
