import {Component, OnInit} from '@angular/core';
import {DOI, Identifier} from "../openaireLibrary/utils/string-utils.class";
import {EnvProperties} from "../openaireLibrary/utils/properties/env-properties";
import {ErrorCodes} from "../openaireLibrary/utils/properties/errorCodes";
import {Subscriber, timer} from "rxjs";
import {properties} from "../../environments/environment";
import {SearchResearchResultsService} from "../openaireLibrary/services/searchResearchResults.service";
import {map} from "rxjs/operators";
import {Filter} from "../openaireLibrary/searchPages/searchUtils/searchHelperClasses.class";
import {LogService} from "../openaireLibrary/utils/log/log.service";
import {BaseComponent} from '../openaireLibrary/sharedComponents/base/base.component';
import {ActivatedRoute, Router} from '@angular/router';
import {SEOService} from '../openaireLibrary/sharedComponents/SEO/SEO.service';
import {PiwikService} from '../openaireLibrary/utils/piwik/piwik.service';
import {Meta, Title} from '@angular/platform-browser';

@Component({
  selector: 'upload-dois',
  templateUrl: './upload-dois.component.html'
})
export class UploadDoisComponent extends BaseComponent implements OnInit {
  private static UPLOAD_TIMEOUT = 2000;
  page: number = 1;
  size: number = 100;
  requestSize: number = 80;
  properties: EnvProperties = properties;
  public errorCodes: ErrorCodes = new ErrorCodes();
  public warningMessage = "";
  public infoMessage = "";

  subscriptions = [];
  filesToUpload: Array<File>;

  public select: boolean = true;
  public results = [];
  public resultsToShow = [];

  allIds: string[] = [];
  foundIds: string[] = [];
  existedIds: string[] = [];
  duplicateIds: string[] = [];
  duplicateIdsRow: number[] = [];
  notFoundIds: string[] = [];
  notFoundIdsRow: number[] = [];
  noValidIds: string[] = [];
  noValidIdsRow: number[] = [];
  showReport: boolean = false;
  errorMessage = "";
  enableUpload: boolean = true;
  exceedsLimit = false;
  fileLimit = 5;
  stats = {
    open: 0,
    closed: 0,
    embargo: 0,
    restricted: 0,
    green:0,
    gold:0,
    hybrid:0,
    bronze:0,
    diamond:0
  }
  accessModeFilter:Filter = null;
  accessRouteFilter:Filter = null;
  onlyGreen:boolean = false;
  onlyDiamond:boolean = false;
  /*filterByAccessRouteOptions = [
    {label: "Green OA", value: "route-green"},
    {label: "Published in OA Diamond", value: "route-diamond"},
  ]*/
  sortByTitleAsc = true;


  keyword = "";
  loading = false;
  showFound = true;
  constructor(private _searchResearchResultsService: SearchResearchResultsService,
              private _logService: LogService,
              protected _router: Router,
              protected _route: ActivatedRoute,
              protected seoService: SEOService,
              protected _piwikService: PiwikService,
              protected _title: Title,
              protected _meta: Meta) {
    super();
  }

  ngOnInit() {
    this.title = 'Upload DOIs';
    this.description = 'Upload DOIs';
    this.setMetadata();
  }

  upload() {
    this.loading = true;
    this.enableUpload = false;
    this.showReport = false;
    this.errorMessage = "";
    this.results = [];
    this.resultsToShow = [];
    this.sortByTitleAsc = true;
    this.onlyGreen = false;
    this.onlyDiamond =false;
    this.keyword = "";
    this.showFound = true;
    this.stats = {
      open: 0,
      closed: 0,
      embargo: 0,
      restricted: 0,
      green:0,
      gold:0,
      hybrid:0,
      bronze:0,
      diamond:0
    }
    if (this.filesToUpload.length == 0) {
      this.errorMessage = "There is no selected file to upload.";
      return;
    } else {
      if (this.filesToUpload[0].name.indexOf(".csv") == -1 ||
        (this.filesToUpload[0].type != "text/csv" && this.filesToUpload[0].type != "application/vnd.ms-excel")) {
        this.errorMessage = "No valid file type. The required type is CSV";
        return;
      }
    }

    this.makeFileRequest(this.properties.utilsService + '/upload', [], this.filesToUpload).then((result) => {
      const rows = (result as any).split('\n');  // I have used space, you can use any thing.
      this.exceedsLimit = false;
      let invalid_rows = 0;
      this.duplicateIds = [];
      this.existedIds = [];
      this.allIds = [];
      this.foundIds = [];
      this.noValidIds = [];
      // this.results.slice(0, this.results.length);
      this.notFoundIds = [];
      if (rows.length > this.fileLimit) {
        this.exceedsLimit = true;
      }
      for (let i = 0; i < (rows.length); i++) {
        if (rows[i] && rows[i] != null && rows[i] != "") {
          try {
            const values = rows[i].split(',');

            let id = this.removeDoubleQuotes(values[0]);
            if (DOI.isValidDOI(id)) {
              id = Identifier.getRawDOIValue(id);
              // console.log(id, id.split("\r")[0]);
              id = id.split("\r")[0]
              if (this.allIds.indexOf(id) > -1) {
                this.duplicateIds.push(id);
                this.duplicateIdsRow.push(i + 1);
              } else {
                this.allIds.push(id);
              }
            } else {
              this.noValidIds.push(id);
              this.noValidIdsRow.push(i + 1);
            }
          }catch (e){
            invalid_rows++;
          }
        } else {
          invalid_rows++;
        }

      }
      this.enableUpload = true;
      if(properties.logServiceUrl) {
        this.subscriptions.push(this._logService.logUploadDOIs(properties, this.allIds.length).subscribe(res => { }));
      }
      if(this.allIds.length > 0) {
        this.fetchAllResults();
      } else {
        setTimeout(() => {
          this.loading = false;
        }, UploadDoisComponent.UPLOAD_TIMEOUT);
      }
    }, (error) => {
      this.enableUpload = true;
      this.errorMessage = "An error occured.";
      this.handleError("Error uploading file", error);
    });
  }

  private removeDoubleQuotes(value) {
    if (value.indexOf('"') == 0) {
      value = value.substring(1, value.length);
    }
    const index = +value.indexOf('"');
    if (index == (value.length - 1) || index == (value.length - 2)) {
      value = value.substring(0, index);
    }
    return value;
  }

  private validateAccessMode(value) {
    const accessModes = ["OPEN", "CLOSED", "EMBARGO"];
    return accessModes.indexOf(value) > -1;
  }

  fileChangeEvent(fileInput: any) {
    this.filesToUpload = <Array<File>>fileInput.target.files;
    this.upload();
  }

  makeFileRequest(url: string, params: Array<string>, files: Array<File>) {
    return new Promise<void>((resolve, reject) => {
      const formData: any = new FormData();
      const xhr = new XMLHttpRequest();
      for (let i = 0; i < files.length; i++) {
        formData.append("uploads[]", files[i], files[i].name);
      }
      xhr.onreadystatechange = function () {
        if (xhr.readyState == 4) {
          if (xhr.status == 200) {
            resolve(xhr.response);
          } else {
            reject(xhr.response);
          }
        }
      }
      xhr.open("POST", url, true);
      xhr.send(formData);
    });
  }

  fetchAllResults() {
    let page = 1;
    let timerSubscription = timer(0, UploadDoisComponent.UPLOAD_TIMEOUT).pipe(
      map(() => {
        if ((page - 1) * this.requestSize <= this.allIds.length) {
          this.fetchResultsByPage(page); // load data contains the http request
          page += 1;
        } else {
          this.stopFetching(timerSubscription);
        }
      })
    ).subscribe();
    this.subscriptions.push(timerSubscription);
  }

  stopFetching(timerSubscription) {
    timerSubscription.unsubscribe();
    this.loading = false;
  }

  fetchResultsByPage(page) {
    let dois = this.allIds.slice((page - 1) * this.requestSize, page * this.requestSize);
    if (dois.length == 0) {
      return;
    }

    this.subscriptions.push(this._searchResearchResultsService.fetchByDOIs(dois, "&fq=country=IE").subscribe(data => {
      for (let result of data[1]) {
        let matchingDOI = this.findMatchingDoi(result.DOIs, dois);
        this.foundIds.push(matchingDOI);
        let showRes = {
          doi: matchingDOI,
          title: result.title.name,
          accessMode: result.title.accessMode,
          accessRoute:result.oaRoutes?result.oaRoutes.oaColor:null,
          green: result.oaRoutes?result.oaRoutes.green:false ,
          diamond: result.oaRoutes?result.oaRoutes.isInDiamondJournal:false
        };
        this.results.push(showRes)
        this.addStatsPerResult(showRes)
        // console.log(showRes, this.stats)
      }
      if (data[0] < dois.length) {
        for (let doi of dois) {
          if (this.foundIds.indexOf(doi) == -1) {
            this.notFoundIds.push(doi);
            // this.results.push({doi: doi, title: null, accessMode: null, green: false, openAccessColor:null, diamond: false, accessRoute: null, found: false})
          }
        }
      }
      this.initFilters();
      this.updateView();

    }));
  }

  findMatchingDoi(resultDois, requestedDois) {
    for (let doi of resultDois) {
      if (requestedDois.indexOf(doi) != -1) {
        return doi;
      }

    }
    return null;
  }

  private handleError(message: string, error) {
    console.error("Upload error: " + message, error);
  }

  addStatsPerResult(result) {
    if(result.accessMode) {
      if (result.accessMode == "Open Access") {
        this.stats.open++;
      }
      if (result.accessMode == "Closed Access") {
        this.stats.closed++;
      }
      if (result.accessMode == "Embargo") {
        this.stats.embargo++;
      }
      if (result.accessMode == "Restricted") {
        this.stats.restricted++;
      }
    }
    if(result.green){
      this.stats.green++;
    }
    if(result.diamond){
      this.stats.diamond++;
    }
    if(result.accessRoute) {
      if (result.accessRoute == "gold") {
        this.stats.gold++;
      }
      if (result.accessRoute == "hybrid") {
        this.stats.hybrid++;
      }
      if (result.accessRoute == "bronze") {
        this.stats.bronze++;
      }
    }

  }

  updatePage($event) {
    this.page = $event.value;
  }

  updateKeyword(keyword){
    this.keyword = keyword;
    this.updateView();
  }

  updateView() {
    this.resultsToShow = [...this.results];
    this.resultsToShow = this.filterResultsByAccessMode();
    this.resultsToShow = this.filterResultsByAccessRoute();
    if(this.onlyGreen){
      this.resultsToShow = this.resultsToShow.filter(res =>  res.green);
    }
    if(this.onlyDiamond){
      this.resultsToShow = this.resultsToShow.filter(res =>  res.diamond);
    }
    if(this.keyword.length > 0){
      this.resultsToShow = this.filterResultsByKeyword();
    }
     this.resultsToShow= this.sortResults();
  }
  filterResultsByAccessMode() {
    if (this.accessModeFilter && this.accessModeFilter.countSelectedValues > 0) {
      for (let value of this.accessModeFilter.values) {
        if (value.selected == true) {
          return this.resultsToShow.filter(res => {
            return res.accessMode == value.id;
          });
        }
      }
    }
    return this.resultsToShow;
  }
  filterResultsByAccessRoute() {
    if (this.accessRouteFilter && this.accessRouteFilter.countSelectedValues > 0) {
      for (let value of this.accessRouteFilter.values) {
        if (value.selected == true) {
          return this.resultsToShow.filter(res => {
            return res.accessRoute == value.id;
          });
        }
      }
    }
    return this.resultsToShow;
  }
  /*filterResults() {
    return this.results.filter(res =>{
       if(this.filterBy == "route-green") {
        return res.green;
      }else if(this.filterBy == "route-diamond") {
        return res.diamond;
      }else{
        return true;
      }
    })
  }*/
  filterResultsByKeyword() {
    return this.resultsToShow.filter(res =>{

        return (res.title && res.title.toLowerCase().indexOf(this.keyword.toLowerCase()) !=-1);

    })
  }
  sortResults(changeSorting= false){
    if(changeSorting) {
      this.sortByTitleAsc = !this.sortByTitleAsc;
    }
     return this.resultsToShow.sort((n1, n2) => {

         if (n1.title && n2.title && ((!this.sortByTitleAsc  && n1.title > n2.title) || (this.sortByTitleAsc && n1.title < n2.title))) {
           return 1;
         }

       if (n1.title && n2.title && ((this.sortByTitleAsc  && n1.title > n2.title) || (!this.sortByTitleAsc && n1.title < n2.title))) {
           return -1;
         }

        return 0;
      })

  }

  getercentage(number, foundIds = true){
    if(!number){
      return "0";
    }
    let from = foundIds?this.foundIds.length:this.allIds.length
    var num = new Number((number/from)*100);
    return num == 100?100:num.toPrecision(2);
  }

  initFilters(){
    this.accessModeFilter = {
      countSelectedValues: 0,
      filterId: "access-mode",
      filterOperator: "or",
      filterType: "radio",
      originalFilterId: "",
      title: "Access Mode",
      valueIsExact: false,

      values:[
        {name: "Open Access", id: "Open Access", selected:false, number:this.stats["open"]},
        {name: "Embargo", id: "Embargo", selected:false, number:this.stats["embargo"]},
        {name: "Restricted", id: "Restricted", selected:false, number:this.stats["restricted"]},
        {name: "Closed Access", id: "Closed Access", selected:false, number:this.stats["closed"]}
      ]};
    this.accessRouteFilter = {
      countSelectedValues: 0,
      filterId: "access-route",
      filterOperator: "or",
      filterType: "radio",
      originalFilterId: "",
      title: "Access Route",
      valueIsExact: false,
      values:[
        {name: "Gold", id: "gold", selected:false, number:this.stats["gold"]},
        {name: "Hybrid", id: "hybrid", selected:false, number:this.stats["hybrid"]},
        {name: "Bronze", id: "bronze", selected:false, number:this.stats["bronze"]}
      ]};

  }

}
