var AwsS3 = {
    params: {},

    setParams: function (params) {
        ['access_key', 'secret_key'].forEach(function (field) {
            if (typeof params !== 'object' || typeof params[field] === 'undefined' || params[field] === '') {
                throw 'Required param is not set: "' + field + '".';
            }
        });

        AwsS3.params = params;
    },

    sign: function (key, message) {
        var hex = hmac('sha256', key, message);

        if ((hex.length % 2) === 1) {
            throw 'Invalid length of a hex string!';
        }

        var result = new Int8Array(hex.length / 2);
        for (var i = 0, b = 0; i < hex.length; i += 2, b++) {
            result[b] = parseInt(hex.substring(i, i + 2), 16);
        }

        return result;
    },

    prepareParams: function (params) {
        var result = [];

        Object.keys(params).sort().forEach(function (key) {
            if (typeof params[key] !== 'object') {
                result.push(key + '=' + encodeURIComponent(params[key]));
            }
            else {
                result.push(prepareObject(key, params[key]));
            }
        });

        return result.join('&');
    },

    request: function (method, region, service, host, params, data, uri) {
        if (typeof data === 'undefined' || data === null) {
            data = '';
        }

        var amzdate = (new Date()).toISOString().replace(/\\.\\d+Z/, 'Z').replace(/[-:]/g, ''),
            date = amzdate.replace(/T\\d+Z/, ''),

            canonical_uri = '/' + uri,
            canonical_headers = 'content-encoding:amz-1.0\\n' + 'host:' + host + '\\n' + 'x-amz-date:' + amzdate + '\\n',
            signed_headers = 'content-encoding;host;x-amz-date',
            canonical_request = method + '\\n' + canonical_uri + '\\n' + params + '\\n' + canonical_headers + '\\n' + signed_headers + '\\n' + sha256(data),
            credential_scope = date + '/' + region + '/' + service + '/' + 'aws4_request',
            request_string = 'AWS4-HMAC-SHA256' + '\\n' + amzdate + '\\n' + credential_scope + '\\n' + sha256(canonical_request),
            key = AwsS3.sign('AWS4' + AwsS3.params.secret_key, date);

        key = AwsS3.sign(key, region);
        key = AwsS3.sign(key, service);
        key = AwsS3.sign(key, 'aws4_request');

        var request = new HttpRequest(),
            url = 'https://' + host + canonical_uri + '?' + params;

        if (typeof AwsS3.params.proxy !== 'undefined' && AwsS3.params.proxy !== '') {
            request.setProxy(AwsS3.params.proxy);
        }
        request.addHeader('x-amz-date: ' + amzdate);
        request.addHeader('x-amz-content-sha256:' + sha256(data));
        request.addHeader('Accept: application/json');
        request.addHeader('Content-Type: application/json');
        request.addHeader('Content-Encoding: amz-1.0');
        request.addHeader('Authorization: ' + 'AWS4-HMAC-SHA256 Credential=' + AwsS3.params.access_key + '/' + credential_scope + ', ' + 'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + hmac('sha256', key, request_string));

        Zabbix.log(4, '[ AWS S3 ] Sending request: ' + url);

        response = request.get(url);

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

        if (request.getStatus() !== 200) {
            throw 'Request failed with status code ' + request.getStatus() + ': ' + response;
        }
        if (response !== null) {
            try {
                response = XML.toJson(response);
            }
            catch (error) {
                throw 'Failed to parse response received from AWS CloudWatch API. Check debug log for more information.';
            }
        }

        return JSON.parse(response);
    },

    getBucketLocation: function (bucket_name) {
        var payload = {};

        payload['location'] = '',

            result = AwsS3.request('GET', 'us-east-1', 's3', 's3.amazonaws.com', AwsS3.prepareParams(payload), '', bucket_name);

        if (typeof result !== 'object'
            || result.hasOwnProperty('LocationConstraint') === false
        ) {
            throw 'Cannot get buckets region location data from AWS S3 API. Check debug log for more information.';
        }

        return result.LocationConstraint !== null ? result.LocationConstraint : 'us-east-1';
    },

    listBuckets: function () {
        var payload = {};

        payload['Action'] = 'ListBuckets',
            payload['Version'] = '2006-03-01',

            result = AwsS3.request('GET', 'us-east-1', 's3', 's3.us-east-1.amazonaws.com', AwsS3.prepareParams(payload), '', '');
        if (typeof result !== 'object'
            || typeof result.ListAllMyBucketsResult !== 'object'
            || typeof result.ListAllMyBucketsResult.Buckets !== 'object') {
            throw 'Cannot get buckets list from AWS S3 API. Check debug log for more information.';
        }
        if (result.ListAllMyBucketsResult.Buckets === null)
            return []
        if (typeof result.ListAllMyBucketsResult.Buckets.Bucket !== 'object') {
            throw 'Cannot get buckets list from AWS S3 API. Check debug log for more information.';
        }

        var buckets = [];
        if (!Array.isArray(result.ListAllMyBucketsResult.Buckets.Bucket))
            buckets = [result.ListAllMyBucketsResult.Buckets.Bucket]
        else
            buckets = result.ListAllMyBucketsResult.Buckets.Bucket
        buckets.forEach(
            function (bucket, index, buckets_array) {
                if (!bucket.hasOwnProperty('Name'))
                    throw 'Cannot get location for bucket. Check debug log for more information.';
                buckets_array[index]['location'] = AwsS3.getBucketLocation(bucket['Name']);
            });

        return buckets;
    }
};

try {
    AwsS3.setParams(JSON.parse(value));

    return JSON.stringify(AwsS3.listBuckets());
}
catch (error) {
    error += (String(error).endsWith('.')) ? '' : '.';
    Zabbix.log(3, '[ AWS S3 ] ERROR: ' + error);
    return JSON.stringify({ 'error': error });
}