import Connection from "../domain/ConnectionCode";
import WebSite from "../domain/Website";
import Exploration from "../domain/Exploration";
import AifexService from "../domain/AifexService";
import { CheckList, StepObjective } from "../domain/CheckList";
import Session from "../domain/Session";
import AifexPluginInfo from "../domain/AifexPluginInfo";
import ChecklistEvaluation from "../domain/ChecklistEvaluation";

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

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.toJSON(),
		};
		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.toJSON(),
		};
		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.toJSON()
		};
		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> {

		let 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);
				})
	}

	getObjectiveCheckList(connection: Connection): Promise<CheckList | null> {
		const option = {
			method: "GET",
			headers: { "Content-Type": "application/json" },
		};
		return fetch(`${CONSTRAINT_URL}/objective/checklist/${connection.sessionId}`, option)
		.then((response) => {
			if (!response.ok) {
				throw new Error(response.statusText);
			}
			return response.text();
		})
		.then(result => {
			if (!result) {
				return new CheckList([]);
			}
			const dataJSON = JSON.parse(result) 
			const objectives = dataJSON.objectiveList.map(objectiveData => {
				switch (objectiveData.type) {
					case "step":
						return new StepObjective(objectiveData.id, objectiveData.name, false, objectiveData.text)
					default:
						return Promise.reject("Invalid objective " + objectiveData);
				}
			});
			return new CheckList(objectives);
		})
	}

	evaluateSequence(connection: Connection, exploration: Exploration): Promise<ChecklistEvaluation> {
		return new Promise((resolve, reject) => {
			const eventList = exploration.actions.map(action => action.toString())
			const option = {
				method: "POST",
				body: JSON.stringify({
					sessionId: connection.sessionId,
					eventList: eventList
				}),
				headers: { "Content-Type": "application/json" },
			};
			fetch(`${CONSTRAINT_URL}/objective/evaluateSequence`, option).then((response) => {
				if (!response.ok) {
					throw new Error(response.statusText);
				}
				return response.json();
			})
			.then(result => {
				console.log(result);
				resolve(result.evaluation)
			})
			.catch((error) => {
				reject(error);
			})
		})
	}
}
