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";
import Screenshot from "../domain/Screenshot";
import CommentDistribution from "../domain/CommentDistribution";

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

const OK_STATUS = 200;
const INVALID_PARAMETERS_STATUS = 400;
const NOT_FOUND_STATUS = 404;
const INTERNAL_SERVER_ERROR_STATUS = 500;


export default class AifexServiceHTTP implements AifexService {

	getSession(sessionId : string): Promise<Session | undefined> {
		return fetch(`${SESSION_URL}/session/${sessionId}`)
		.then((response) => {
			if (response.status === OK_STATUS) {
				return response
				.json()
				.then((session) => {
					return new Session(session.id , session.webSite.id , session.baseURL);
				});
			}
			if (response.status === INVALID_PARAMETERS_STATUS) {
				return Promise.reject(`sessionId is malformed`);
			}
			if (response.status === NOT_FOUND_STATUS) {
				return;
			}
			if (response.status === INTERNAL_SERVER_ERROR_STATUS) {
				return Promise.reject(`server error`);
			}
		})
	}

	getWebSite(webSiteId: string): Promise<WebSite | undefined> {
		return fetch(`${WEBSITE_URL}/website/${webSiteId}`)
		.then((response) => {
			if (response.status === OK_STATUS) {
				return response
				.json()
				.then(websiteData => {
					return new WebSite(websiteData.id, websiteData.name, websiteData.mappingList);
				})
			}
			if (response.status === INVALID_PARAMETERS_STATUS) {
				return Promise.reject(`sessionId is malformed`);
			}
			if (response.status === NOT_FOUND_STATUS) {
				return;
			}
			if (response.status === INTERNAL_SERVER_ERROR_STATUS) {
				return Promise.reject(`server error`);
			}
		})		
	}

	hasModel(modelId: string): Promise<boolean>  {
		return fetch(`${MODEL_URL}/model/${modelId}`)
		.then((response) => {
			if (response.status === OK_STATUS) {
				return true;
			}
			if (response.status === INVALID_PARAMETERS_STATUS) {
				return Promise.reject(`modelId is malformed`);
			}
			if (response.status === NOT_FOUND_STATUS) {
				return false;
			}
			if (response.status === INTERNAL_SERVER_ERROR_STATUS) {
				return Promise.reject(`server error`);
			}
			return false;
		})	
	}

	getProbabilityMap(
		connection: Connection,
		exploration: Exploration
	): Promise<Map<string, number>> {
		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}/getprobabilitymap`,
			option
		)
		.then((response) => {
			if (response.status === OK_STATUS) {
				return response
				.json()
				.then((jsonMap) => {
					return new Map(jsonMap);
				});
			}
			if (response.status === INVALID_PARAMETERS_STATUS) {
				return Promise.reject(`modelId and/or interaction list is malformed`);
			}
			if (response.status === INTERNAL_SERVER_ERROR_STATUS) {
				return Promise.reject(`server error`);
			}
			return new Map();
		})
	}

	addExploration(
		connection: Connection,
		testerName: string,
		exploration: Exploration
	): Promise<number> {
		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.status === OK_STATUS) {
				return response.json();
			}
			if (response.status === NOT_FOUND_STATUS) {
				return Promise.reject(`sessionId not found`);
			}
			if (response.status === INVALID_PARAMETERS_STATUS) {
				return Promise.reject(`sessionId and/or exploration is malformed`);
			}
			if (response.status === INTERNAL_SERVER_ERROR_STATUS) {
				return Promise.reject(`server error`);
			}
		});
	}

	getCommentDistributions(connection: Connection, exploration: Exploration): Promise<CommentDistribution[] | undefined> {
		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}/getcommentdistributions`,
			option)
		.then((response) => {
			if (response.status === OK_STATUS) {
				return response
				.json()
				.then((commentDistributionsData : { 
					note : string, 
					distributions : { 
						commentOccurence: number,
						contextOccurece: number,
						context: string[] 
					}[]
				}[]) => {
					return commentDistributionsData.map(commentDistributionData => {
						return new CommentDistribution(commentDistributionData.note, commentDistributionData.distributions)

					})
				})
			}
			if (response.status === NOT_FOUND_STATUS) {
				return Promise.reject(`sessionId not found`);
			}
			if (response.status === INVALID_PARAMETERS_STATUS) {
				return Promise.reject(`sessionId and/or exploration is malformed`);
			}
			if (response.status === INTERNAL_SERVER_ERROR_STATUS) {
				return Promise.reject(`server error`);
			}
		});
	}

	addScreenshotList(connection: Connection, explorationNumber : number, list : Screenshot[]): Promise<void> {
		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.status === OK_STATUS) {
				return;
			}
			if (response.status === INVALID_PARAMETERS_STATUS) {
				return Promise.reject(`screenshotList is malformed`);
			}
			if (response.status === INTERNAL_SERVER_ERROR_STATUS) {
				return Promise.reject(`server error`);
			}
		});
	}

	addVideo(connection: Connection, explorationNumber:number, video:Blob): Promise<void> {
		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.status === OK_STATUS) {
				return;
			}
			if (response.status === INVALID_PARAMETERS_STATUS) {
				return Promise.reject(`sessionId and/or explorationNumber and/or video is malformed`);
			}
			if (response.status === INTERNAL_SERVER_ERROR_STATUS) {
				return Promise.reject(`server error`);
			}
		});
	}

	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> {
		const interactionList = exploration.evaluableInteractions.map(action => action.toString());
		const option = {
			method: "POST",
			body: JSON.stringify({
				webSiteId: webSite.id,
				interactionList
			}),
			headers: { "Content-Type": "application/json" },
		};
		return fetch(`${CONSTRAINT_URL}/evaluator/evaluateSequence`, option)
		.then((response) => {
			if (response.status === OK_STATUS) {
				return response.json();
			} else {
				return Promise.reject(`Evaluator error`);
			}
		})
		.then((evaluationData : {
			evaluation: {
				continuingActionList: {prefix: string, suffix?: string}[],
				enteringInteractionList: {prefix: string, suffix?: string}[],
				finishingInteractionList: {prefix: string, suffix?: string}[],
				isAccepted: boolean,
			}}) => {
			const evaluation = evaluationData.evaluation;
			if (!evaluation) {
				return Promise.reject(`Evaluator error`);
			} else {
				return new ExplorationEvaluation(
					evaluation.isAccepted,
					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)),
				);
			}
		})
		
	}

	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.status === OK_STATUS) {
				return response.json();
			}
			if (response.ok) {
				return;
			}
			if (response.status === INVALID_PARAMETERS_STATUS) {
				return Promise.reject(`webSiteId is malformed`);
			}
			if (response.status === INTERNAL_SERVER_ERROR_STATUS) {
				return Promise.reject(`server error`);
			}
		})
		.then((evaluatorJSON: {
			webSiteId: string,
			description: string,
			expression: String,
			id: string,
		}) => {
			if (evaluatorJSON){
				return new Evaluator(evaluatorJSON.description);
			} else {
				return;
			}
		})
	}
}
