text: import { action, computed, configure as configureMobx, decorate, observable, runInAction } from 'mobx';
  import { fetchEntity } from '../../state/fetch-entity.js';
  import { performSirenAction } from './SirenActionHelper.js';
  import { Rels } from 'd2l-hypermedia-constants';
  
  configureMobx({ enforceActions: 'observed' });
  
  const
  	evaluatorClass = 'evaluator-candidate',
  	selectedClass = 'selected',
  	updateAction = 'update',
  	clearStateAction = 'clear-state',
  	toggleAction = 'toggle-multiple',
  	toggleActionFieldName = 'value',
  	searchAction = 'search',
  	searchActionFieldName = 'search',
  	selfLink = 'self',
  	nexPageLink = 'next-page',
  	toggleAllAction = 'toggle-all-evaluators';
  
  const
  	searchParameter = 'search',
  	pageParameter = 'page',
  	pageSizeParameter = 'pageSize';
  
  export class ActivityEvaluators {
  
  	constructor(href, token) {
  		this._activityHref = href;
  		this._token = token;
  		this.evaluators = [];
  		this.selectedEvaluators = [];
  		this._entity = null;
  		this._toggled = new Set();
  		this._hasPendingChanges = false;
  		this._selectedHref = null;
  		this._candidatesHref = null;
  		this.hasNextPage = false;
  		this._total = 0;
  	}
  
  	async clearSelection() {
  		this._toggled.clear();
  
  		const href = this._resetParameters(this._candidatesHref, [[searchParameter, '']]);
  		if (this._candidatesHref === href) {
  			return;
  		}
  
  		this._candidatesHref = this._resetParameters(this._candidatesHref, this._getDefUrlSearchPrms());
  		const entity = await fetchEntity(this._candidatesHref, this._token, true);
  		await this._load(entity);
  	}
  
  	async clearState() {
  
  		if (!this._hasPendingChanges) {
  			return;
  		}
  
  		const action = this._entity.getActionByName(clearStateAction);
  		action.href = this._resetParameters(action.href, this._getDefUrlSearchPrms());
  		const resp = await performSirenAction(this._token, action, undefined, true);
  
  		this._entity = null;
  		this._candidatesHref = resp.getLinkByRel(selfLink).href;
  		this._hasPendingChanges = false;
  		this._toggled.clear();
  
  		await this._loadSelectedEvaluators();
  	}
  
  	get count() {
  		return this._total;
  	}
  
  	get countSelected() {
  		return this.selectedEvaluators.length;
  	}
  
  	countToggled() {
  
  		let count = 0;
  		this._toggled.forEach((href) => {
  
  			const isSelected = this.selectedEvaluators.some(i => i.userHref === href);
  			count += isSelected ? -1 : 1;
  		});
  
  		return count;
  	}
  
  	async fetch(bypassCache) {
  
  		const activitySirenEntity = await fetchEntity(this._activityHref, this._token, bypassCache);
  
  		if (activitySirenEntity
  			&& activitySirenEntity.hasLinkByRel(Rels.Activities.evaluatorCandidates)
  			&& activitySirenEntity.hasLinkByRel(Rels.Activities.selectedEvaluators)) {
  
  			this._candidatesHref = activitySirenEntity.getLinkByRel(Rels.Activities.evaluatorCandidates).href;
  			this._selectedHref = activitySirenEntity.getLinkByRel(Rels.Activities.selectedEvaluators).href;
  
  			await this._loadSelectedEvaluators();
  		}
  
  		return this;
  	}
  
  	get hasPendingChanges() {
  		return this._hasPendingChanges;
  	}
  
  	get isEnabled() {
  
  		return this._selectedHref !== null && this._candidatesHref !== null;
  	}
  
  	isEvaluatorToggled(userHref) {
  		return this._toggled.has(userHref);
  	}
  
  	async load() {
  		if (!this._entity) {
  
  			const candidatesSirenEntity = await fetchEntity(this._candidatesHref, this._token, true);
  
  			await this._load(candidatesSirenEntity);
  		}
  	}
  
  	async loadMore() {
  
  		if (!this.hasNextPage) {
  			return;
  		}
  
  		const nextHref =  this._entity.getLinkByRel(nexPageLink).href;
  		const entity = await fetchEntity(nextHref, this._token, true);
  
  		await this._load(entity, true);
  	}
  
  	async search(keyword) {
  
  		const action = this._entity.getActionByName(searchAction);
  		action.href = this._resetParameters(action.href, [[pageParameter, 1]]);
  		const field = action.getFieldByName(searchActionFieldName);
  		field.value = keyword;
  
  		const resp = await performSirenAction(this._token, action, undefined, true, true);
  
  		await this._load(resp);
  	}
  
  	async toggleAll(isChecked) {
  		const action = this._entity.getActionByName(toggleAllAction);
  		action.href = this._resetParameters(action.href, this._getDefUrlSearchPrms());
  		const field = action.getFieldByName('value');
  		field.value =  isChecked ? 'select-all-evaluators' : 'deselect-all-evaluators';
  
  		const resp = await performSirenAction(this._token, action, [field], true, true);
  
  		this._toggled.clear();
  		this._hasPendingChanges = true;
  
  		await this._load(resp);
  		await this._loadSelectedEvaluators();//TODO: remove when the transient within transient state is supported.
  	}
  
  	toggleSelection(userHref) {
  
  		if (this._toggled.has(userHref)) {
  			this._toggled.delete(userHref);
  		} else {
  			this._toggled.add(userHref);
  		}
  	}
  
  	async updateSelection() {
  
  		if (this._toggled.size === 0) {
  			await this.clearSelection();
  			return;
  		}
  
  		const action = this._entity.getActionByName(toggleAction);
  		const field = action.getFieldByName(toggleActionFieldName);
  		field.value = JSON.stringify(Array.from(this._toggled));
  		action.href = this._resetParameters(action.href, this._getDefUrlSearchPrms());
  
  		const resp = await performSirenAction(this._token, action, undefined, true, true);
  
  		this._toggled.clear();
  		this._hasPendingChanges = true;
  
  		await this._load(resp);
  		await this._loadSelectedEvaluators();
  	}
  
  	async updateState() {
  
  		if (!this._hasPendingChanges) {
  			return;
  		}
  
  		const action = this._entity.getActionByName(updateAction);
  		action.href = this._resetParameters(action.href, this._getDefUrlSearchPrms());
  		const resp = await performSirenAction(this._token, action, undefined, true);
  
  		this._entity = null;
  		this._candidatesHref = resp.getLinkByRel(selfLink).href;
  		this._hasPendingChanges = false;
  	}
  
  	_getDefUrlSearchPrms() {
  		return [[searchParameter, ''], [pageParameter, 1]];
  	}
  
  	async _getSelectedEvaluatorsEntity() {
  
  		const parameters = this._getDefUrlSearchPrms();
  		let href = this._resetParameters(this._selectedHref, parameters);
  		let sirenEntity = await fetchEntity(href, this._token, true);
  
  		if (!sirenEntity) {
  			return sirenEntity;
  		}
  		this._total = sirenEntity.properties['total-evaluators'];
  
  		if (sirenEntity.hasLinkByRel(nexPageLink)) {
  			this._selectedHref = sirenEntity.getLinkByRel(selfLink).href;
  
  			parameters.push([pageSizeParameter, this._total]);
  			href = this._resetParameters(this._selectedHref, parameters);
  
  			sirenEntity = await fetchEntity(href, this._token, true);
  		}
  
  		return sirenEntity;
  	}
  
  	async _load(entity, loadMore = false) {
  
  		if (entity) {
  			this._entity = entity;
  			this._candidatesHref = entity.getLinkByRel(selfLink).href;
  			this._selectedHref = entity.getLinkByRel(Rels.Activities.selectedEvaluators).href;
  			runInAction(() => this.hasNextPage = entity.hasLinkByRel(nexPageLink));
  		}
  
  		await this._loadEvaluators(loadMore);
  	}
  
  	async _loadEvaluator(data) {
  		const userSirenEntity = await fetchEntity(data.userHref, this._token);
  
  		data.displayName = userSirenEntity.hasSubEntityByRel(Rels.displayName)
  			? userSirenEntity.getSubEntityByRel(Rels.displayName).properties.name
  			: null;
  		data.orgDefinedId = userSirenEntity.hasSubEntityByRel(Rels.orgDefinedId)
  			? userSirenEntity.getSubEntityByRel(Rels.orgDefinedId).properties.orgDefinedId
  			: null;
  	}
  
  	async _loadEvaluators(loadMore = false) {
  
  		const evaluators = [];
  		if (this._entity) {
  			const entities = this._entity.getSubEntitiesByClass(evaluatorClass);
  			const promises = entities.map((entity) => {
  
  				const evaluatorData = {
  					userHref: entity.getSubEntityByRel(Rels.Activities.user).href,
  					displayName: null,
  					orgDefinedId: null,
  					isSelected: entity.hasClass(selectedClass),
  				};
  				evaluators.push(evaluatorData);
  
  				return this._loadEvaluator(evaluatorData);
  			});
  
  			await Promise.all(promises);
  		}
  		runInAction(() => {
  			if (loadMore) {
  				evaluators.forEach((evaluator) => this.evaluators.push(evaluator));
  			}
  			else {
  				this.evaluators.replace(evaluators);
  			}
  		});
  	}
  
  	async _loadSelectedEvaluator(evaluatorData) {
  		const userSirenEntity = await fetchEntity(evaluatorData.userHref, this._token);
  
  		evaluatorData.displayName = userSirenEntity.hasSubEntityByRel(Rels.displayName)
  			? userSirenEntity.getSubEntityByRel(Rels.displayName).properties.name
  			: null;
  	}
  
  	async _loadSelectedEvaluators() {
  
  		const selectedEvaluators = [];
  		const sirenEntity = await this._getSelectedEvaluatorsEntity();
  
  		if (sirenEntity) {
  			const entities = sirenEntity.getSubEntitiesByClass(evaluatorClass);
  			const promises = entities.map((entity) => {
  
  				const evaluatorData = {
  					userHref: entity.getSubEntityByRel(Rels.Activities.user).href,
  					displayName: null
  				};
  				selectedEvaluators.push(evaluatorData);
  
  				return this._loadSelectedEvaluator(evaluatorData);
  			});
  
  			await Promise.all(promises);
  		}
  		runInAction(() => this.selectedEvaluators.replace(selectedEvaluators));
  	}
  
  	_resetParameters(href, parameters) {
  		const url = new URL(href, window.location.origin);
  		const prms =  new Map(parameters);
  
  		prms.forEach((value, key) => {
  			if (url.searchParams.has(key)) {
  				url.searchParams.set(key, value);
  			}
  		});
  
  		return url.href;
  	}
  }
  
  decorate(ActivityEvaluators, {
  	// props
  	evaluators: observable,
  	selectedEvaluators: observable,
  	hasNextPage: observable,
  	_toggled: observable,
  	// actions
  	clearSelection: action,
  	clearState: action,
  	load: action,
  	loadMore: action,
  	toggleSelection: action,
  	updateSelection: action,
  	updateState: action,
  	search: action,
  	toggleAll: action,
  	_loadEvaluators: action,
  	_loadSelectedEvaluators: action,
  	//computed
  	count: computed,
  	countSelected: computed,
  	hasPendingChanges: computed,
  	isEnabled: computed
  });
  