import { DataStorage } from '@studyportals/data-storage';
import { IStudent, StudentField } from '@studyportals/studentdomain';
import merge from 'deepmerge';
import { Actor, AnonymousStudentProfileUpdated, InterestType, StudentRepositoryStateType } from '../../../interfaces';
import { WriteHistoryRecord } from '../../domain/dto/write-history-record';
import { AnonymousStudentEventBroadcaster } from '../anonymous-student-event-broadcaster';
import { StudentClient } from '../interfaces/student-client';

export class LocalStudentClient implements StudentClient {

	private storageKeyWriteHistory: string;

	// tslint:disable: typedef
	constructor(
		private storageKey: string,
		private storageTTL: number,
		private anonymousStudentEventBroadcaster: AnonymousStudentEventBroadcaster | null = null
	) {
		this.storageKeyWriteHistory = `${storageKey}/WriteHistory`;
	}

	private localStorage = DataStorage;

	public cleanUp(): void {
		this.localStorage.remove(this.storageKey);
		this.localStorage.remove(this.storageKeyWriteHistory);
	}

	public async getData(studentFields: StudentField[]): Promise<IStudent> {
		const dataFromLocalStorage = this.retrieveDataFromLocalStorage();

		const studentData = {};
		studentFields.forEach((studentField) => {
			const dataField = dataFromLocalStorage[studentField];
			if (dataField === undefined) {
				return;
			}

			if (dataField === null) {
				studentData[studentField] = undefined;
				return;
			}

			studentData[studentField] = dataField;
		});

		return studentData;
	}

	public async setData(studentData: IStudent): Promise<void> {
		const dataForLocalStorage = this.retrieveDataFromLocalStorage();

		const newStudentData = merge(dataForLocalStorage, studentData, { arrayMerge: this.mergeArraysWithoutDuplicates });

		Object.keys(newStudentData).forEach((key) => {
			if (newStudentData[key] === null) {
				newStudentData[key] = undefined;
			}
		});

		this.setDataInLocalStorage(newStudentData);
		this.broadcastChanges(newStudentData);
	}

	private mergeArraysWithoutDuplicates(destinationArray: number[], sourceArray: number[], options: any): number[] {
		return [...new Set([...destinationArray, ...sourceArray])];
	}

	public async addToCollection(type: StudentField, items: any[]): Promise<void> {
		this.addToIdListInLocalStorage(type, items);
	}

	public async removeFromCollection(type: StudentField, items: any[]): Promise<void> {
		this.removeFromIdListInLocalStorage(type, items);
	}

	public async addToWriteHistory(studentFields: StudentField[], actor: Actor): Promise<void> {
		let records: WriteHistoryRecord[] = [];
		const jsonRecords = this.localStorage.retrieve(this.storageKeyWriteHistory);
		if (jsonRecords) {
			records = JSON.parse(jsonRecords);
		}

		for (const studentField of studentFields) {
			const existingRecord = records.find((record) => record.field === studentField);

			if (existingRecord) {
				existingRecord.actor = actor;
			} else {
				records.push({
					field: studentField,
					actor
				});
			}
		}

		this.localStorage.store(this.storageKeyWriteHistory, JSON.stringify(records), this.storageTTL);
	}

	public async getWriteHistory(): Promise<WriteHistoryRecord[]> {
		const jsonRecords = this.localStorage.retrieve(this.storageKeyWriteHistory);
		if (!jsonRecords) {
			return [];
		}

		return JSON.parse(jsonRecords);
	}

	private retrieveDataFromLocalStorage() {
		const rawData = this.localStorage.retrieve(this.storageKey);
		const data = rawData ? JSON.parse(rawData) : {};

		return data;
	}

	private setDataInLocalStorage(data: object): any {
		Object.keys(data).forEach((key) => {
			if (data[key] === undefined) {
				data[key] = null;
			}
		});

		this.localStorage.store(this.storageKey, JSON.stringify(data), this.storageTTL);
	}

	private addToIdListInLocalStorage(key: StudentField | InterestType, ids: number[]): void {
		const dataForLocalStorage = this.retrieveDataFromLocalStorage();
		const savedItems = new Set(dataForLocalStorage[key]);
		ids.forEach((id) => {
			savedItems.add(id);
		});

		dataForLocalStorage[key] = Array.from(savedItems);
		this.setDataInLocalStorage(dataForLocalStorage);
		this.broadcastChanges({ [key]: dataForLocalStorage[key] });
	}

	private removeFromIdListInLocalStorage(key: StudentField | InterestType, ids: number[]): void {
		const dataForLocalStorage = this.retrieveDataFromLocalStorage();
		const savedItems = new Set(dataForLocalStorage[key]);
		ids.forEach((id) => {
			savedItems.delete(id);
		});

		dataForLocalStorage[key] = Array.from(savedItems);
		this.setDataInLocalStorage(dataForLocalStorage);
		this.broadcastChanges({ [key]: dataForLocalStorage[key] });
	}

	private broadcastChanges(changes: IStudent) {
		if (!this.anonymousStudentEventBroadcaster) {
			return;
		}

		const event = new AnonymousStudentProfileUpdated(new Date(), StudentRepositoryStateType.OFFLINE, changes, true);
		this.anonymousStudentEventBroadcaster.broadcastProfileUpdatedEvent(event);
	}

}
