/*
 * Copyright © 2016-2017 European Support Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
const needle = require('needle');
const fs = require('fs');
require('node-zip');
var btoa = require('btoa');
const md5 = require('md5');
const _ = require('lodash');

function getOptionsForRequest(context, method, path, type) {
	if (type == undefined || type == null) {
		type = context.defaultServerType
	}
	let server = context.getUrlForType(type);
	let options = {
		method: method,
		url: server + path,
		headers: _.clone(context.headers[type])
	};
//	options.headers["Content-Type"] = "application/json";
//	options.headers["accept"] = "application/json";
	return options;
}

function _requestBinaryFormData(context, method, path, fileName, formInputName, type) {
	let options = getOptionsForRequest(context, method, path, type);
	let formData = {};
	if (method === 'POST' || method === 'PUT') {
		//formData[formInputName] = fs.createReadStream(fileName);
		//options.formData = formData;
		let fileData =  {
			file: fileName
		};
		fileData['content_type'] = 'multipart/form-data';
		options.formData = {};
		options.formData[formInputName] = fileData;
	}
	return _request(context, method, path, options);
}
function _requestBinaryBody(context, method, path, fileName, type) {
	let options = getOptionsForRequest(context, method, path, type);
	if (method === 'POST' || method === 'PUT') {
		options.body =  fs.createReadStream(fileName);
		options.headers['Content-Type'] = 'application/octet-stream';

	}
	return _request(context, method, path, options);
}


function _requestPayload(context, method, path, filePath, type) {
	let options = getOptionsForRequest(context, method, path, type);
	options.json = _createPayload(filePath);
	options.headers['Content-MD5'] = addCheckSum(options.json);
	return _request(context, method, path, options);
}

function _requestRest(context, method, path, data, type) {
	let options = getOptionsForRequest(context, method, path, type);
	if (method === 'POST' || method === 'PUT') {
		options.json = data;
	}
	return _request(context, method, path, options);
}


function _request(context, method, path, options) {
	console.log('--> Calling REST ' + options.method +' url: ' + options.url);
	let inputData = options.json;
	let needleOptions = {headers: options.headers, rejectUnauthorized: false};
	if (inputData == undefined) {
		if (options.formData != undefined) {
			inputData = options.formData;
			needleOptions.multipart = true;
		}
		if (inputData && inputData.body != undefined) {
			inputData = options.body;
		}
	} else {
		needleOptions.json = true;
	}
	return needle(method, options.url, inputData, needleOptions)
		.then(function(result) {
			context.inputData = null;
			let successResult = result.statusCode >= 200 && result.statusCode < 300;
			let isExpected = context.shouldFail ? !successResult : successResult;
			data = result.body;
			if (!isExpected) {
				console.log('Did not get expected response code');
				throw  'Status Code was ' + result.statusCode ;
			}
			if (context.statusCode) {
				let expectedStatusCode = context.statusCode;
				context.statusCode = null;
				if (result.statusCode !== expectedStatusCode) {
					throw 'Response Status Code was ' + result.statusCode + ' instead of ' + expectedStatusCode;
				}
			}
			if (context.shouldFail) {
				context.shouldFail = false;
				if (context.errorCode) {
					if (typeof data === 'string' && data) {
						data = JSON.parse(data);
					}
					let contextErrorCode = context.errorCode;
					let errorCode = data.errorCode;
					context.errorCode = null;
					if (errorCode !== contextErrorCode) {
						throw 'Error Code was ' + errorCode + ' instead of ' + contextErrorCode;
					}
				}
				if (context.errorMessage) {
					if (typeof data === 'string' && data) {
						data = JSON.parse(data);
					}
					let errorMessage = data.message;
					let contextErrorMessage = context.errorMessage;
					context.errorMessage = null;
					if (errorMessage !== contextErrorMessage) {
						throw 'Error Message was ' + errorMessage + ' instead of ' + contextErrorMessage;
					}
				}
				return({statusCode: result.statusCode, data: {}});
			}

			if (typeof data === 'string' && data) {
				if (data.startsWith('[') || data.startsWith('{')) {
					data = JSON.parse(data);
				}
			}
			context.responseData = data;
			context.inputData = data;
			return({statusCode: result.statusCode, data: data});

		})
		.catch(function(err) {
			console.error('Request URL: ' + options.url);
			console.error('Request Method: ' + options.method);
			console.log(err);
			throw err;
		})
}

function download(context, path, filePath,  type) {
	if (type == undefined || type == null) {
		type = context.defaultServerType
	}
	let server = context.getUrlForType(type);
	let options = {
		method: 'GET',
		url: server + path,
		headers: context.headers[type]
	};

	console.log('--> Calling REST download url: ' + options.url);
	return needle('GET', options.url, {}, {
		headers: options.headers,
		rejectUnauthorized: false,
		output: filePath
	})
		.then(function (result) {
			let zipFile = fs.readFileSync(filePath, 'binary');
			let zip = new JSZip(zipFile, {base64: false, checkCRC32: true});
			if (zip.files['MANIFEST.json']) {
				let manifestData = zip.files['MANIFEST.json']._data;
				manifestData = manifestData.replace(/\\n/g, '');
				context.responseData = JSON.parse(manifestData);
			}
			return zip;
		})
		.catch(function (err) {
			console.error('Request URL: ' + options.url);
			console.error('Request Method: ' + options.method);
			throw err;
		})
}

function _random() {
	let d = new Date();
	return d.getTime().toString().split('').reverse().join('');
}

function _getJSONFromFile(file) {
	return JSON.parse(fs.readFileSync(file, 'utf8'));
}

function _createPayload(fileName) {
	var body = fs.readFileSync(fileName);
	let payload = {
		payloadData: body.toString('base64'),
		payloadName: fileName.substring(fileName.lastIndexOf("/") + 1 )
	};
	return payload;
}

function addCheckSum(payloadData) {
	let _md5 = md5(JSON.stringify(payloadData));
	return btoa(_md5.toLowerCase());
}

function _getFile(file, format) {
	if(format === '' ){
		return fs.readFileSync(file)
	}
	return fs.readFileSync(file, format);
}


module.exports = {
	getFile: _getFile,
	request: _requestRest,
	requestPayload: _requestPayload,
	requestBinaryFormData: _requestBinaryFormData,
	requestBinaryBody: _requestBinaryBody,
	random : _random,
	getJSONFromFile: _getJSONFromFile,
	download: download,
	payload: _createPayload
};