var base_path = 'cloudsql.googleapis.com/database/',
	pgsql_path = 'cloudsql.googleapis.com/database/postgresql/',
	obj = {},
	value_types = ['int64Value', 'doubleValue', 'stringValue', 'boolValue'];

function getField(data, path) {
	var steps = path.split('.');

	for (var i = 0; i < steps.length; i++) {
		var step = steps[i];

		if (typeof data !== 'object' || typeof data[step] === 'undefined') {
			throw 'Required field "' + path + '" is not present in data received.';
		}
		data = data[step];
	}
	return data;
}

function checkParams(params) {
	['project_id', 'time_window', 'database_id', 'token'].forEach(function (field) {
		if (typeof params !== 'object' || typeof params[field] === 'undefined' || params[field] === '') {
			throw 'Required param is not set: ' + field + '.';
		}
	});
	if (!params.time_window.match(/^[0-9]+(m|h)$/)) {
		throw 'Incorrect update interval for parameter  time_window. Valid ranges 1m-23h.';
	}
	return params;
}

function getHttpData(url, token, body) {
	var request = new HttpRequest();
	if (typeof params.proxy !== 'undefined' && params.proxy !== '') {
		request.setProxy(params.proxy);
	};
	request.addHeader('Authorization: Bearer ' + token);
	request.addHeader('Content-Type: application/json');

	var response = request.post(url, JSON.stringify(body));

	Zabbix.log(4, '[ GCP API ] [ ' + 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.';
		}
	}
	return response;
};

function rawMap(raw, prefix) {
	var keys = getField(raw, 'timeSeriesDescriptor.pointDescriptors'),
		values = getField(raw, 'timeSeriesData.0.pointData.0.values'),
		re = /(^t_\\d+\\.value(_|\\.)|_sum$)/g;

	for (i in keys) {
		var key = getField(keys[i], 'key').replace(re, '');

		for (v in value_types) {
			if (typeof values[i][value_types[v]] !== 'undefined') {
				if (prefix) {
					obj[prefix + key] = values[i][value_types[v]];
				} else {
					obj[key] = values[i][value_types[v]];
				}

			}
		}
	}
	return obj;
}

function singleMetrics(tsd) {
	result = {};
	for (t in tsd) {
		for (v in value_types) {
			if (typeof tsd[t].pointData[0].values[0][value_types[v]] !== 'undefined') {
				result['pgsql_' + getField(tsd[t], 'labelValues.3.stringValue').toLowerCase()] = tsd[t].pointData[0].values[0][value_types[v]];
			}
		}
	}
	return result;
}

var params = checkParams(JSON.parse(value)),
	filter = ' | within ' + params.time_window + " | filter database_id = '" + params.database_id + "'",
	j_filter = filter + ' | join',
	url = 'https://monitoring.googleapis.com/v3/projects/' + params.project_id + '/timeSeries:query',
	base_paths = [
		'cpu/reserved_cores',
		'cpu/usage_time',
		'cpu/utilization',
		'disk/read_ops_count',
		'disk/write_ops_count',
		'memory/quota',
		'memory/total_usage',
		'memory/usage',
		'network/received_bytes_count',
		'network/sent_bytes_count | map ignore [destination]'
	].map(function (path) { return base_path + path; }),
	pgsql_paths = [
		'transaction_id_utilization ' + filter,
		'temp_bytes_written_count' + filter + ' | sum',
		'temp_files_written_count' + filter + ' | sum',
		'num_backends' + filter + ' | sum'
	].map(function (path) { return pgsql_path + path; }).concat(
		['disk/bytes_used', 'disk/utilization', 'disk/quota'].map(function (path) { return base_path + path; })),
	queries = [
		{ name: 'base_', data: { query: '{' + base_paths.join(';') + '}' + j_filter } },
		{ name: 'pgsql_', data: { query: '{' + pgsql_paths.join(';') + '}' + '| within ' + params.time_window + '| join' } }
	];

for (q in queries) {
	raw = getHttpData(url, params.token, queries[q].data);
	obj = Object.assign(obj, rawMap(raw, queries[q].name));
}

obj.base_ram_pused = Math.floor((getField(obj, 'base_usage') / getField(obj, 'base_quota')) * 100);

var single_paths = ['transaction_id_count', 'vacuum/oldest_transaction_age'],
	single_queries = single_paths.map(function (path) { return { query: pgsql_path + path + filter }; });

for (q in single_queries) {
	raw = getHttpData(url, params.token, single_queries[q]);
	Object.assign(obj, singleMetrics(getField(raw, 'timeSeriesData')));
}

return JSON.stringify(obj);