var auth_url = 'https://oauth2.googleapis.com/token',
	issued = Math.floor(new Date().getTime() / 1000),
	expires = issued + 3600,
	endpoint = 'https://www.googleapis.com/auth/',
	scopes = ['cloud-platform.read-only', 'monitoring', 'compute.readonly', 'sqlservice.admin'],
	scope = scopes.map(function (path) { return endpoint + path; }).join(' ');

function base64url(source) {
	encodedSource = btoa(source);
	encodedSource = encodedSource.replace(/=+$/, '');
	encodedSource = encodedSource.replace(/\\+/g, '-');
	encodedSource = encodedSource.replace(/\\//g, '_');

	return encodedSource;
}

function fromHex(hex) {
	var utf8array = [];

	if (hex.length % 2)
		throw 'Invalid length of a hex string!';
	for (var i = 0; i < hex.length; i += 2) {
		utf8array.push(parseInt(hex.substr(i, 2), 16));
	}
	return new Uint8Array(utf8array);
}

function encodeJWT(headers, payload, secret) {
	unsignedToken = base64url(JSON.stringify(headers)) + '.' + base64url(JSON.stringify(payload));
	jwt = unsignedToken + '.' + base64url(fromHex(sign('sha256', secret, unsignedToken)));
	return jwt;
}

function exchangeJwtForAccessToken(signed_jwt) {

	var request = new HttpRequest(),
		params = {
			grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
			assertion: signed_jwt
		}
	if (typeof script_params.proxy !== 'undefined' && script_params.proxy !== '') {
		request.setProxy(script_params.proxy);
	};
	var response = request.post(auth_url, JSON.stringify(params));

	Zabbix.log(4, '[ GCP API ] [ ' + auth_url + ' ] Received response with status code ' + request.getStatus() + ': ' + response);

	if (response !== null) {
		try {
			response = JSON.parse(response);
		}
		catch (error) {
			throw 'Failed to parse response received from GCP API. Check debug log for more information.';
		}
	}

	if (typeof response !== 'object' || response === null) {
		throw 'Cannot process response data: received data is not an object. Check debug log for more information.';
	}

	if (request.getStatus() !== 200) {
		if (typeof (response.error && response.error.message) !== 'undefined') {
			throw response.error.message;
		}
		else {
			throw 'Failed to receive data: invalid response status code. Check debug log for more information.';
		}
	}


	if (typeof response.access_token !== 'undefined') {
		return response.access_token;
	}
	else {
		throw 'No access token in the JWT exchange request.';
	}
}

function checkParams(params) {
	['pkey_id', 'private_key', 'email'].forEach(function (field) {
		if (typeof params !== 'object' || typeof params[field] === 'undefined' || params[field] === '') {
			throw 'Required param is not set: ' + field + '.';
		}
	});
	return params;
}

var script_params = checkParams(JSON.parse(value)),
	headers = {
		alg: 'RS256',
		kid: script_params.pkey_id,
		typ: 'JWT'
	},
	payload = {
		iss: script_params.email,
		sub: script_params.email,
		aud: auth_url,
		iat: issued,
		exp: expires,
		scope: scope
	};

try {
	jwt = encodeJWT(headers, payload, script_params.private_key);
	token = exchangeJwtForAccessToken(jwt);
	return token;
}
catch (err) {
	return JSON.stringify({ error: err });
}