import { IStudent, StudentField } from '@studyportals/studentdomain';
import { Actor } from '../../../interfaces';
import { WriteHistoryRecord } from '../dto/write-history-record';
import { OfflineStudentRepositoryState } from '../states/offline-student-repository-state';
import { OnlineStudentRepositoryState } from '../states/online-student-repository-state';

export class OfflineToOnlineSynchronizationService {
	private overwriteProtectionFields: StudentField[] = [
		StudentField.NATIONALITY_COUNTRY_ISO,
		StudentField.NATIONALITY_COUNTRY_ID,
		StudentField.CURRENCY
	];

	constructor(
		private offline: OfflineStudentRepositoryState,
		private online: OnlineStudentRepositoryState
	) {

	}

	public async syncData(): Promise<void> {
		const offlineData = await this.getOfflineData();
		const overrideProtectedData = await this.enforceOverrideProtection(offlineData);

		if (Object.keys(overrideProtectedData).length > 0) {
			await this.online.setStudentData(overrideProtectedData, Actor.USER);
		}
	}

	private async enforceOverrideProtection(offlineData: IStudent): Promise<IStudent> {
		const offlineFields = Object.keys(offlineData) as StudentField[];
		const onlineData = await this.online.getStudentData(offlineFields);
		const onlineFields = this.getFieldsThatAreActuallySet(onlineData);

		const writeHistoryRecords: WriteHistoryRecord[] = await this.offline.getWriteHistory();

		for (const field of offlineFields) {
			if (!this.overwriteProtectionFields.includes(field)) {
				continue;
			}

			if (!onlineFields.includes(field)) {
				continue;
			}

			const writeHistoryRecord = writeHistoryRecords.find((record) => record.field === field);
			if (writeHistoryRecord && writeHistoryRecord.actor === Actor.AUTOMATION) {
				delete offlineData[field];
			}
		}

		return offlineData;
	}

	private async getOfflineData(): Promise<IStudent> {
		const fields = this.getAllStudentFields();
		const offlineData = await this.offline.getStudentData(fields);

		const fieldsToSync = this.getFieldsThatAreActuallySet(offlineData);

		if (fieldsToSync.length === 0) {
			return {};
		}

		const eligibleData = {};
		fieldsToSync.forEach((field) => {
			eligibleData[field] = offlineData[field];
		});

		return eligibleData;
	}

	private getFieldsThatAreActuallySet(data: IStudent): StudentField[] {
		return Object.keys(data).filter((field) => {
			return data[field] !== undefined;
		}) as StudentField[];
	}

	private getAllStudentFields(): StudentField[] {
		const studentFields: StudentField[] = [];

		const allKeyFields = Object.keys(StudentField);
		allKeyFields.forEach((keyField) => {
			studentFields.push(StudentField[keyField]);
		});

		return studentFields;
	}
}
