import { IEventAggregationService } from '@studyportals/event-aggregation-service-interface';
import { AttendanceType, StudentField } from '@studyportals/studentdomain';
import { suite, test } from '@testdeck/mocha';
import { assert } from 'chai';
import * as Moq from 'typemoq';
import { Actor, InterestType } from '../../../interfaces/enumerations';
import { OnlineStudentRepositoryState } from '../../../src/domain/states/online-student-repository-state';
import { StudentRepository } from '../../../src/domain/student-repository';
import { AnonymousStudentEventBroadcaster } from '../../../src/infrastructure/anonymous-student-event-broadcaster';
import { CachedStudentClient } from '../../../src/infrastructure/clients/cached-student-client';
import { LocalStudentClient } from '../../../src/infrastructure/clients/local-student-client';
import { StudentAPIClient } from '../../../src/infrastructure/clients/student-api-client';

@suite()
class OnlineStudentRepositoryStateTest {
	private testInstanceMock: Moq.IMock<OnlineStudentRepositoryState>;

	private eventAggregationServiceMock: Moq.IMock<any>;

	private sessionServiceMock: Moq.IMock<any>;

	private studentRepositoryMock: Moq.IMock<StudentRepository>;

	private studentClientMock: Moq.IMock<CachedStudentClient>;

	private get studentRepository(): StudentRepository {
		return this.studentRepositoryMock.object;
	}

	private get studentClient(): CachedStudentClient {
		return this.studentClientMock.object;
	}

	private get sessionService(): any {
		return this.sessionServiceMock.object;
	}

	private get eventAggregationService(): IEventAggregationService {
		return this.eventAggregationServiceMock.object;
	}

	private get testInstance(): OnlineStudentRepositoryState {
		return this.testInstanceMock.object;
	}

	public before(): void {
		this.testInstanceMock = Moq.Mock.ofType(OnlineStudentRepositoryState);
		this.testInstanceMock.callBase = true;

		this.eventAggregationServiceMock = Moq.Mock.ofType<IEventAggregationService>();
		this.sessionServiceMock = Moq.Mock.ofType<any>();
		this.studentRepositoryMock = Moq.Mock.ofType<StudentRepository>();
		this.studentClientMock = Moq.Mock.ofType<CachedStudentClient>();

		this.testInstanceMock
			.setup((x) => x['studentRepository'])
			.returns(() => this.studentRepository);

		this.testInstanceMock
			.setup((x) => x['studentClient'])
			.returns(() => this.studentClient);

		this.testInstanceMock
			.setup((x) => x['sessionService'])
			.returns(() => this.sessionService);

		this.testInstanceMock
			.setup((x) => x['eventAggregationService'])
			.returns(() => this.eventAggregationService);
	}

	@test
	public async constructStudentClient_Should_Return_CachedStudentClient_When_Called(): Promise<void> {
		// given

		// when
		const client = this.testInstance['constructStudentClient']();

		// then
		assert.instanceOf(client, CachedStudentClient);
	}

	@test
	public async constructStudentClient_Should_Return_CachedStudentClient_With_CorrectParameters_When_Called(): Promise<void> {
		// given

		// when
		const client = this.testInstance['constructStudentClient']();

		// then
		assert.instanceOf(client['studentAPIClient'], StudentAPIClient);
		assert.instanceOf(client['localStudentClient'], LocalStudentClient);
		assert.equal(client['eventAggregationService'], this.eventAggregationService);
		assert.instanceOf(client['anonymousStudentEventBroadcaster'], AnonymousStudentEventBroadcaster);
	}

	@test
	public async setStudentData_Should_Call_SetData_StudentClient_When_Called(): Promise<void> {
		// given
		const givenData = {first_name: 'henk'};

		// when
		await this.testInstance.setStudentData(givenData, Actor.AUTOMATION);

		// then
		this.studentClientMock.verify((x) => x.setData(givenData), Moq.Times.once());
	}

	@test
	public async getStudentData_Should_Call_GetData_StudentClient_When_Called(): Promise<void> {
		// given
		const fields = [StudentField.DISCIPLINES];

		// when
		await this.testInstance.getStudentData(fields);

		// then
		this.studentClientMock.verify((x) => x.getData(fields), Moq.Times.once());
	}

	@test
	public async addToCollection_Should_Call_StudentRepository_addToCollection_When_Called(): Promise<void> {
		// given
		this.studentClientMock.setup((x) => x.addToCollection(Moq.It.isAny(), Moq.It.isAny()));

		// when
		await this.testInstance.addToCollection(StudentField.ATTENDANCE, [AttendanceType.ONLINE]);

		// then
		this.studentClientMock.verify((x) => x.addToCollection(Moq.It.isAny(), Moq.It.isAny()), Moq.Times.once());
	}

	@test
	public async removeFromCollection_Should_Call_StudentRepository_addToCollection_When_Called(): Promise<void> {
		// given
		this.studentClientMock.setup((x) => x.removeFromCollection(Moq.It.isAny(), Moq.It.isAny()));

		// when
		await this.testInstance.removeFromCollection(StudentField.INTERESTS_COUNTRIES, [1, 4, 76]);

		// then
		this.studentClientMock.verify((x) => x.removeFromCollection(Moq.It.isAny(), Moq.It.isAny()), Moq.Times.once());
	}

}
