import {ChangeDetectorRef, Directive, HostListener, OnInit, ViewRef} from "@angular/core";
import {IndicatorStakeholderBaseComponent} from "../monitor-admin/utils/stakeholder-base.component";
import {DomSanitizer} from "@angular/platform-browser";
import {
  Category,
  Indicator, IndicatorPath,
  IndicatorSize, Overlay,
  Section,
  Stakeholder,
  SubCategory,
  Topic,
  Visibility
} from "./entities/stakeholder";
import {LayoutService} from "../dashboard/sharedComponents/sidebar/layout.service";
import {ClickEvent} from "../utils/click/click-outside-or-esc.directive";
import {Session, User} from "../login/utils/helper.class";
import {Filter, Value} from "../searchPages/searchUtils/searchHelperClasses.class";
import {RangeFilter} from "../utils/rangeFilter/rangeFilterHelperClasses.class";
import {RangeFilterComponent} from "../utils/rangeFilter/rangeFilter.component";
import {Dates, StringUtils} from "../utils/string-utils.class";
import {Params} from "@angular/router";
import {StatisticsService} from "../monitor-admin/utils/services/statistics.service";
import {SearchResearchResultsService} from "../services/searchResearchResults.service";
import {CustomFilterService} from "../shared/customFilter.service";

@Directive()
export abstract class MonitorIndicatorStakeholderBaseComponent extends IndicatorStakeholderBaseComponent implements OnInit {
  /** Status */
  public loading: boolean = true;
  public isMobile: boolean = false;
  public isFullscreen: boolean = false;

  /** Variables */
  public user: User;
  public requireLogin: boolean = true;
  public view: Visibility;
  public stakeholder: Stakeholder;
  public activeTopic: Topic = null;
  public activeCategory: Category = null;
  public activeSubCategory: SubCategory = null;
  public filters: Filter[] = [];
  public queryParams: any = {};
  public periodFilter: RangeFilter = {
    title: "Time range",
    filterId: "year",
    originalFilterIdFrom: null,
    originalFilterIdTo: null,
    selectedFromValue: null,
    selectedToValue: null,
    selectedFromAndToValues: ""
  };
  rangeFilter: RangeFilterComponent;
  minYear = Dates.currentYear - 20;
  maxYear =  Dates.currentYear;
  public numberResults: Map<string, number> = new Map<string, number>();
  public clipboard;

  /** Services */
  protected sanitizer: DomSanitizer;
  protected cdr: ChangeDetectorRef;
  protected layoutService: LayoutService;
  protected statisticsService: StatisticsService;
  protected searchResearchResultsService: SearchResearchResultsService;
  protected customFilterService: CustomFilterService;

  @HostListener('fullscreenchange', ['$event'])
  @HostListener('webkitfullscreenchange', ['$event'])
  @HostListener('mozfullscreenchange', ['$event'])
  @HostListener('MSFullscreenChange', ['$event'])
  screenChange() {
    this.isFullscreen = !this.isFullscreen;
  }

  ngOnInit() {
    this.layoutService.isMobile.subscribe(isMobile => {
      this.isMobile = isMobile;
      this.cdr.detectChanges();
    });
    this.createClipboard();
  }

  protected setView(params: Params) {
    this.setSelectedFilters();
    this.loading = false;
    if (params['topic']) {
      this.activeTopic = this.stakeholder.topics.find(topic => topic.alias === decodeURIComponent(params['topic']) && this.hasPermission(topic.visibility));
      if (this.activeTopic) {
        if (params['category']) {
          this.activeCategory = this.activeTopic.categories.find(category =>
              (category.alias === params['category']) && this.hasPermission(category.visibility));
          if (!this.activeCategory) {
            this.navigateToError();
            return;
          }
        } else {
          this.activeCategory = this.activeTopic.categories.find(category => this.hasPermission(category.visibility));
          if (this.activeCategory) {
            this.activeSubCategory = this.activeCategory.subCategories.find(subCategory =>
                this.hasPermission(subCategory.visibility));
            if (this.activeSubCategory) {
              this.setIndicators();
            }
          }
          return;
        }
        if (this.activeCategory) {
          if (params['subCategory']) {
            this.activeSubCategory = this.activeCategory.subCategories.find(subCategory =>
                (subCategory.alias === params['subCategory'] && this.hasPermission(subCategory.visibility)));
            if (!this.activeSubCategory) {
              this.navigateToError();
              return;
            }
          } else {
            this.activeSubCategory = this.activeCategory.subCategories.find(subCategory =>
                this.hasPermission(subCategory.visibility));
          }
          if (this.activeSubCategory) {
            this.setIndicators();
          } else {
            this.navigateToError();
          }
          return;
        } else {
          this.activeSubCategory = null;
        }
      } else {
        this.navigateToError();
        return;
      }
    } else {
      this.activeTopic = this.stakeholder.topics.find(topic => this.hasPermission(topic.visibility));
      if (this.activeTopic) {
        this.activeCategory = this.activeTopic.categories.find(category => this.hasPermission(category.visibility));
        if (this.activeCategory) {
          this.activeSubCategory = this.activeCategory.subCategories.find(subCategory => this.hasPermission(subCategory.visibility));
          if (this.activeSubCategory) {
            this.setIndicators();
          }
        }
      }
    }
  }

  protected handleQueryParams(queryParams, params) {
    this.queryParams = Object.assign({}, queryParams);
    this.initializeFilters();
    this.setView(params);
    if(this.requireLogin && !this.user && (this.filters.filter(filter => this.queryParams[filter.filterId]).length > 0 || this.queryParams['year'])) {
      if(queryParams['view']) {
        this._router.navigate([], {queryParams: {view: queryParams['view']}});
      } else {
        this._router.navigate([], {queryParams: {}});
      }
    }
    this.view = queryParams['view'];
  }

  protected initializeFilters() {
    this.periodFilter.selectedFromValue = (this.queryParams['year'] && this.queryParams['year'].indexOf("range") == 0) ? this.queryParams['year'].split("range")[1].split(":")[0] : "";
    this.periodFilter.selectedToValue = (this.queryParams['year'] && this.queryParams['year'].indexOf("range") == 0) ? this.queryParams['year'].split("range")[1].split(":")[1] : "";
    this.validateYearRange(false);

    for (let filter of this.filters) {
      if (this.queryParams[filter.filterId]) {
        for (let value of filter.values) {
          if (value.id == StringUtils.URIDecode(StringUtils.unquote(this.queryParams[filter.filterId]))) {
            value.selected = true;
            filter.countSelectedValues = 1;
            break;
          }
        }
      } else {
        this.clearFilter(filter);
      }
    }
  }

  protected validateYearRange(navigateTo: boolean = false) {
    let validYears = true;
    if (this.periodFilter.selectedToValue && (this.periodFilter.selectedToValue.length == 0 || !Dates.isValidYear(this.periodFilter.selectedToValue, this.minYear, this.maxYear))) {
      this.periodFilter.selectedToValue = this.maxYear + "";
      validYears = false;
    }
    if (this.periodFilter.selectedFromValue && (this.periodFilter.selectedFromValue.length == 0 || !Dates.isValidYear(this.periodFilter.selectedFromValue, this.minYear, this.maxYear))) {
      this.periodFilter.selectedFromValue = this.minYear + "";
      validYears = false;
    }
    if (this.periodFilter.selectedFromValue && this.periodFilter.selectedFromValue.length && this.periodFilter.selectedToValue && this.periodFilter.selectedToValue.length > 0 && parseInt(this.periodFilter.selectedFromValue, 10) > parseInt(this.periodFilter.selectedToValue, 10)) {
      this.periodFilter.selectedFromValue = this.periodFilter.selectedToValue;
      validYears = false;
    }
    if (!validYears || navigateTo) {
      if (this.periodFilter.selectedFromValue || this.periodFilter.selectedToValue) {
        this.queryParams["year"] = 'range' + (this.periodFilter.selectedFromValue ? this.periodFilter.selectedFromValue : '') + ":" + (this.periodFilter.selectedToValue ? this.periodFilter.selectedToValue : "");
      } else {
        delete this.queryParams["year"];
      }
      this._router.navigate([], {queryParams: this.queryParams});
      this.setIndicators();
    }
  }

  protected getFullUrl(indicatorPath: IndicatorPath) {
    let fosValues = this.getSelectedFilterValues("fos");
    return this.indicatorUtils.getFullUrlWithFilters(this.stakeholder, indicatorPath,null, this.periodFilter.selectedFromValue, this.periodFilter.selectedToValue, false, fosValues?fosValues.lvl1:[],fosValues?fosValues.lvl2:[], this.getSelectedFilterValues("publiclyfunded"));
  }

  protected setIndicators() {
    this.activeSubCategory.numbers = this.activeSubCategory.numbers.filter(section => section.indicators.length > 0);
    this.activeSubCategory.charts = this.activeSubCategory.charts.filter(section => section.indicators.length > 0);
    this.periodFilter.selectedFromAndToValues = (this.periodFilter.selectedFromValue || this.periodFilter.selectedToValue ? ((this.periodFilter.selectedFromValue && !this.periodFilter.selectedToValue ? "From " : "") + (!this.periodFilter.selectedFromValue && this.periodFilter.selectedToValue ? "Until " : "") + (this.periodFilter.selectedFromValue ? this.periodFilter.selectedFromValue : "") +
        (this.periodFilter.selectedFromValue && this.periodFilter.selectedToValue ? " - " : "") + (this.periodFilter.selectedToValue ? this.periodFilter.selectedToValue : "")) : "");
    //clear numbers when filters change
    this.numberResults.clear();
    let urls: Map<string, [number, number, number][]> = new Map<string, [number, number, number][]>();
    this.activeSubCategory.numbers.forEach((section, i) => {
      section.indicators.forEach((number, j) => {
        if (this.hasPermission(number.visibility)) {
          number.indicatorPaths.forEach((indicatorPath, k) => {
            let url = this.getFullUrl(indicatorPath);
            const pair = JSON.stringify([indicatorPath.source, url]);
            const indexes = urls.get(pair) ? urls.get(pair) : [];
            indexes.push([i, j, k]);
            urls.set(pair, indexes);
          })
        }
      });
    });
    urls.forEach((indexes, pair) => {
      pair = JSON.parse(pair);
      let activeSubcategory = this.activeSubCategory._id;
      this.subscriptions.push(this.statisticsService.getNumbers(this.indicatorUtils.getSourceType(pair[0]), pair[1]).subscribe(response => {
        if(activeSubcategory === this.activeSubCategory._id) {
          indexes.forEach(([i, j, k]) => {
            if( this.activeSubCategory?.numbers[i]?.indicators[j]) {
              let result = JSON.parse(JSON.stringify(response));
              this.activeSubCategory.numbers[i].indicators[j].indicatorPaths[k].jsonPath.forEach(jsonPath => {
                if (result) {
                  result = result[jsonPath];
                }
              });
              if (typeof result === 'string' || typeof result === 'number') {
                result = Number(result);
                if (result === Number.NaN) {
                  result = 0;
                }
              } else {
                result = 0;
              }
              this.numberResults.set(i + '-' + j + '-' + k, result);
            }
          });
        }
      }));
    });
    this.activeSubCategory.charts.forEach((section, i) => {
      section.indicators.forEach((indicator, j) => {
        indicator.indicatorPaths.forEach((indicatorPath, k) => {
          indicator.indicatorPaths[k].safeResourceUrl = this.getUrlByStakeHolder(indicator.indicatorPaths[k]);
        });
      });
    });
    if (this.cdr && !(this.cdr as ViewRef).destroyed) {
      this.cdr.detectChanges();
    }
  }

  public getUrlByStakeHolder(indicatorPath: IndicatorPath) {
    return this.sanitizer.bypassSecurityTrustResourceUrl(
        this.indicatorUtils.getChartUrl(indicatorPath.source, this.getFullUrl(indicatorPath)));
  }

  public getActiveIndicatorPath(indicator: Indicator) {
    if(indicator.activePath) {
      return indicator.indicatorPaths[indicator.activePath];
    } else {
      return indicator.indicatorPaths[0];
    }
  }

  public filter() {
    this.validateYearRange(true);
  }

  public filterChanged($event, navigate: boolean = true) {
    let selected = [];
    for (let value of $event.value.values) {
      if (value.selected) {
        selected.push( StringUtils.quote(StringUtils.URIEncode(value.id)));
      }
    }
    if (selected.length > 0) {
      this.queryParams[$event.value.filterId] = selected.join(",");
    } else {
      delete this.queryParams[$event.value.filterId];
    }
    if (navigate) {
      this._router.navigate([], {queryParams: this.queryParams});
      this.setIndicators();
    }

  }

  public countSelectedFilters(): number {
    let count = 0;
    if (this.periodFilter.selectedFromAndToValues.length > 0) {
      count += 1;
    }
    for (let filter of this.filters) {
      count += filter.countSelectedValues;
    }
    return count;
  }

  public clearAll() {
    for (let filter of this.filters) {
      this.clearFilter(filter);
    }
    this.periodFilter.selectedFromValue = "";
    this.periodFilter.selectedToValue = "";
    this.validateYearRange(true)
  }

  public clearFilter(filter: Filter) {
    filter.countSelectedValues = 0;
    filter.radioValue = "";
    for (let value of filter.values) {
      if (value.selected) {
        value.selected = false;
      }
    }
    if (this.queryParams[filter.filterId]) {
      delete this.queryParams[filter.filterId];
    }
  }

  clearPeriodFilter() {
    if (this.periodFilter.selectedFromValue || this.periodFilter.selectedToValue) {
      this.periodFilter.selectedFromValue = "";
      this.periodFilter.selectedToValue = "";
      if(this.rangeFilter) {
        this.rangeFilter.clearFilter();
      }
      this.filter();
    }
  }

  clearFilterValue(filter: Filter, value: Value) {
    value.selected = false;
    filter.radioValue = '';
    filter.countSelectedValues = filter.countSelectedValues - 1;
    this.filterChanged({
      value:filter
    });
  }

  public hasPermission(visibility: Visibility): boolean {
    if(visibility === 'PUBLIC') {
      return true;
    } else if(visibility === 'RESTRICTED') {
      return (!this.view || this.view === 'RESTRICTED') && this.isMember(this.stakeholder);
    } else {
      return !this.view && this.isManager(this.stakeholder);
    }
  }

  public isMember(stakeholder: Stakeholder) {
    return this.user && (Session.isPortalAdministrator(this.user) || Session.isCurator(stakeholder.type, this.user)
        || Session.isManager(stakeholder.type, stakeholder.alias, this.user) || Session.isMember(stakeholder.type, stakeholder.alias, this.user));
  }

  public isManager(stakeholder: Stakeholder) {
    return this.user && (Session.isPortalAdministrator(this.user) || Session.isCurator(stakeholder.type, this.user) || Session.isManager(stakeholder.type, stakeholder.alias, this.user));
  }

  public countSubCategoriesToShow(category: Category): number {
    return category.subCategories.filter(subCategory => this.hasPermission(subCategory.visibility)).length;
  }

  public countSectionsWithIndicatorsToShow(sections: Section[]):number {
    return sections.map(section => this.countIndicatorsToShow(section.indicators)).reduce((sum, current) => sum + current, 0);
  }

  public countIndicatorsToShow(indicators: Indicator[]): number {
    return indicators.filter(indicator => this.hasPermission(indicator.visibility)).length;
  }

  public getNumberClassBySize(size: IndicatorSize) {
    if (size === 'small') {
      return 'uk-width-medium@m uk-width-1-1';
    } else if (size === 'medium') {
      return 'uk-width-1-4@l uk-width-1-2@m uk-width-1-1';
    } else {
      return 'uk-width-1-2@l uk-width-1-1@m uk-width-1-1';
    }
  }

  public getChartClassBySize(size: IndicatorSize): string {
    if (size === 'small') {
      return 'uk-width-1-3@xl uk-width-1-2@m uk-width-1-1';
    } else if (size === 'medium') {
      return 'uk-width-1-2@l uk-width-1-1';
    } else {
      return 'uk-width-1-1';
    }
  }

  public printReport() {
    window.print();
  }

  changeOverlay(event, indicator: Indicator, overlay: Overlay) {
    event.stopPropagation();
    indicator.overlay = overlay;
  }

  closeOverlay(event: ClickEvent, indicator: Indicator) {
    if(event.clicked && indicator.overlay) {
      indicator.overlay = false;
    }
  }

  private createClipboard() {
    if (typeof window !== 'undefined') {
      delete this.clipboard;
      let Clipboard;
      Clipboard = require('clipboard');
      this.clipboard = new Clipboard('.clipboard_btn');
    }
  }

  //Refine Type Filters

  setSelectedFilters(){
    for (var i = 0; i < this.filters.length; i++) {
      var filter: Filter = this.filters[i];
      filter.countSelectedValues = 0;
      let parameterNames = Object.keys(this.queryParams);
      if (parameterNames.indexOf(filter.filterId) != -1) {
        let values = (decodeURIComponent(this.queryParams[filter.filterId])).split(/,(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/, -1);
        for (let filterValue of filter.values) {
          if (values.indexOf(StringUtils.quote(filterValue.id)) > -1) {
            filterValue.selected = true;
            filter.countSelectedValues++;
          } else {
            filterValue.selected = false;

          }
        }

      } else {
        for (let filterValue of filter.values) {
          filterValue.selected = false;
        }
      }

    }
  }
  getSelectedFilterValues(filterId){
    let values = null;
    for (let filter of this.filters) {
      if(filterId == filter.filterId && filter.countSelectedValues > 0) {
        values =filterId == "fos"?{lvl1:[],lvl2:[]}:[];
          for (let filterValue of filter.values) {
            if (filterValue.selected) {
              if(filterId == "fos"){
                let code = filterValue.id.split(" ")[0];
                if(code.length == 2){
                  values.lvl1.push(filterValue.id)
                }else{
                  values.lvl2.push(filterValue.id)
                }
              }else if(filterId == "publiclyfunded"){
                // console.log("publiclyFunded", filterValue)
                return filterValue.id;
              }else{
                values.push(filterValue.id);
              }
            }
          }
      }

    }
    return values;
  }

}
