import Connection from "../domain/ConnectionCode";
import WebSite from "../domain/Website";
import Exploration from "../domain/Exploration";
import AifexService from "../domain/AifexService";
import Session from "../domain/Session";
import AifexPluginInfo from "../domain/AifexPluginInfo";
import ExplorationEvaluation from "../domain/ExplorationEvaluation";
import Action from "../domain/Action";
import Question from "../domain/Question";
import Step from "../domain/Step";
import Evaluator from "../domain/Evaluator";

declare const MODEL_URL;
declare const SESSION_URL;
declare const WEBSITE_URL;
declare const DASHBOARD_URL;
declare const CONSTRAINT_URL;

const STATUS__SUCCESS_RESPONSE_NULL = 204

export default class AifexServiceHTTP implements AifexService {

	getSession(sessionId : string): Promise<Session> {
		return fetch(`${SESSION_URL}/session/${sessionId}`)
			.then((response) => {
				if (!response.ok) {
					throw new Error(response.statusText);
				}
				return response.json();
			})
			.then((session) => {
				return new Session(session.id , session.webSite.id , session.baseURL);
			});
	}

	getWebSite(webSiteId: string): Promise<WebSite> {
		return fetch(`${WEBSITE_URL}/website/${webSiteId}`)
			.then((response) => {
				if (!response.ok) {
					throw new Error(response.statusText);
				}
				return response.json();
			})
			.then(websiteData => {
				return new WebSite(websiteData.id, websiteData.name, websiteData.mappingList);
			})
	}

	getProbabilityMap(
		connection: Connection,
		exploration: Exploration
	): Promise<Map<string, number>> {
		return new Promise((resolve, reject) => {
		const body = {
			interactionList: exploration.interactionsToJSON(),
		};
		const option = {
			method: "POST",
			body: JSON.stringify(body),
			headers: { "Content-Type": "application/json" },
		};
		fetch(
			`${MODEL_URL}/model/${connection.modelId}/getprobabilitymap`,
			option
			).then((response) => {
				if (!response.ok) {
					reject(response);
				}
				return response.json();
			})
			.then((jsonMap) => {
				return resolve(new Map(jsonMap));
			})
			.catch((error) => {
				reject(error);
			});
		});
	}

	addExploration(
		connection: Connection,
		testerName: string,
		exploration: Exploration
	): Promise<any> {
		const body = {
			testerName,
			interactionList: exploration.interactionsToJSON(),
			startDate: exploration.startDate,
			stopDate: exploration.stopDate
		};
		const option = {
			method: "POST",
			body: JSON.stringify(body),
			headers: { "Content-Type": "application/json" },
		};
		return fetch(
			`${SESSION_URL}/session/${connection.sessionId}/exploration/add`,
			option
		).then((response) => {
			if (!response.ok) {
				throw new Error(response.statusText);
			}
			return response.json();
		});
	}

	getCommentList(connection: Connection, exploration: Exploration): Promise<any> {
		const body = {
			interactionList: exploration.interactionsToJSON()
		};
		const option = {
			method: "POST",
			body: JSON.stringify(body),
			headers: { "Content-Type": "application/json" },
		};
		return fetch(
			`${MODEL_URL}/model/${connection.modelId}/getcommentlist`,
			option
		).then((response) => {
			if (!response.ok) {
				throw new Error(response.statusText);
			}
			return response.json();
		});
	}

	addScreenshotList(connection: Connection, explorationNumber, list): Promise<any> {
		const screenshotList = list.map((screenshot) => {
			screenshot.sessionId = connection.sessionId;
			screenshot.explorationNumber = explorationNumber;
			return screenshot;
		});
		const body = {
			screenshotList,
		};
		const option = {
			method: "POST",
			body: JSON.stringify(body),
			headers: { "Content-Type": "application/json" },
		};
		return fetch(`${SESSION_URL}/session/addscreenshotlist`, option).then(
			(response) => {
				if (!response.ok) {
					throw new Error(response.statusText);
				}
				return response.json();
			}
		);
	}

	addVideo(connection: Connection, explorationNumber:number, video:Blob): Promise<any> {

		const fd = new FormData();
		fd.append('video', video, 'video.webm');

		const option = {
			method: "POST",
			body: fd
		};
		return fetch(`${SESSION_URL}/session/addVideo/${connection.sessionId}/${explorationNumber}`, option).then(
			(response) => {
				if (!response.ok) {
					throw new Error(response.statusText);
				}
				return response.json();
			}
		);

	}

	getPluginInfo(): Promise<AifexPluginInfo> {
		const option = {
			method: "GET",
			headers: { "Content-Type": "application/json" },
		};
		return fetch(`${DASHBOARD_URL}/plugin-info`, option)
				.then(response => {
					if (!response.ok) {
						throw new Error(response.statusText);
					}
					return response.json();
				})
				.then(details => {
					details.url = `${DASHBOARD_URL}/download`
					return new AifexPluginInfo(details.version, details.name, details.description, details.url);
				})
	}

	evaluateSequence(webSite: WebSite, exploration: Exploration): Promise<ExplorationEvaluation|null> {
		return new Promise((resolve, reject) => {
			const interactionList = exploration.evaluableInteractions.map(action => action.toString());
			const option = {
				method: "POST",
				body: JSON.stringify({
					webSiteId: webSite.id,
					interactionList
				}),
				headers: { "Content-Type": "application/json" },
			};
			fetch(`${CONSTRAINT_URL}/evaluator/evaluateSequence`, option).then((response) => {
				if (!response.ok) {
					throw new Error(response.statusText);
				}
				return response.json();
			})
			.then((evaluationData : {
				evaluation: {
					continuingActionList: {prefix: string, suffix?: string}[],
					enteringInteractionList: {prefix: string, suffix?: string}[],
					finishingInteractionList: {prefix: string, suffix?: string}[],
					notValidatedStepList: {name: string, expression: string}[],
					validatedStepList: {name: string, expression: string}[],
					questionList: {text: string}[]
				}
			}) => {
				const evaluation = evaluationData.evaluation;
				if (!evaluation) {
					resolve(null)
				}
				resolve(new ExplorationEvaluation(
					evaluation.validatedStepList.map(step => new Step(step.name, step.expression)),
					evaluation.notValidatedStepList.map(step => new Step(step.name, step.expression)),
					evaluation.enteringInteractionList.map(interaction => new Action(interaction.prefix, interaction.suffix)),
					evaluation.continuingActionList.map(interaction => new Action(interaction.prefix, interaction.suffix)),
					evaluation.finishingInteractionList.map(interaction => new Action(interaction.prefix, interaction.suffix)),
					evaluation.questionList.map(questionData => new Question(questionData.text))
				))
			})
			.catch((error) => {
				reject(error);
			})
		})
	}

	getEvaluator(webSiteId : string) : Promise<Evaluator | undefined> {
		const option = {
			method: "GET",
			headers: { "Content-Type": "application/json" },
		};
		return fetch(`${CONSTRAINT_URL}/evaluator/getEvaluator/${webSiteId}`, option).then((response) => {
			if (!response.ok) {
				return Promise.resolve(undefined)
			}
			if (response.status === STATUS__SUCCESS_RESPONSE_NULL) {
				return Promise.resolve(undefined);
			} else {
				return response.json()
			}
		}).then(evaluatorJSON => {
			if (evaluatorJSON){
				return new Evaluator(evaluatorJSON.description);
			} else {
				return undefined;
			}
		})
	}
}
