var AwsRds = {
    params: {},
    request_period: 600,

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

        AwsRds.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;
    },

    renderMetricQuery: function (period, db_instance_id) {
        var metrics_list = [
          'AbortedClients:Count',
          'ActiveTransactions:Count/Second',
          'AuroraBinlogReplicaLag:Seconds',
          'AuroraReplicaLag:Milliseconds',
          'AuroraReplicaLagMaximum:Milliseconds',
          'AuroraReplicaLagMinimum:Milliseconds',
          'BacktrackWindowActual:Count',
          'BacktrackWindowAlert:Count',
          'BinLogDiskUsage:Bytes',
          'BlockedTransactions:Count/Second',
          'BufferCacheHitRatio:Percent',
          'CheckpointLag:Seconds',
          'CommitLatency:Milliseconds',
          'CommitThroughput:Count/Second',
          'CPUCreditBalance:Count',
          'CPUCreditUsage:Count',
          'CPUUtilization:Percent',
          'DatabaseConnections:Count',
          'DDLLatency:Milliseconds',
          'DDLThroughput:Count/Second',
          'Deadlocks:Count/Second',
          'DeleteLatency:Milliseconds',
          'DeleteThroughput:Count/Second',
          'DiskQueueDepth:Count',
          'DMLLatency:Milliseconds',
          'DMLThroughput:Count/Second',
          'EBSByteBalance%:Percent',
          'EBSIOBalance%:Percent',
          'EngineUptime:Seconds',
          'FailedSQLServerAgentJobsCount:Count/Second',
          'FreeableMemory:Bytes',
          'FreeLocalStorage:Bytes',
          'InsertLatency:Milliseconds',
          'InsertThroughput:Count/Second',
          'LoginFailures:Count/Second',
          'MaximumUsedTransactionIDs:Count',
          'NetworkReceiveThroughput:Bytes/Second',
          'NetworkThroughput:Bytes/Second',
          'NetworkTransmitThroughput:Bytes/Second',
          'NumBinaryLogFiles:Count',
          'Queries:Count/Second',
          'RDSToAuroraPostgreSQLReplicaLag:Seconds',
          'ReadIOPS:Count/Second',
          'ReadLatency:Seconds',
          'ReadLatencyLocalStorage:Seconds',
          'ReadThroughput:Bytes/Second',
          'ReadThroughputLocalStorage:Bytes/Second',
          'ReplicationSlotDiskUsage:Bytes',
          'ResultSetCacheHitRatio:Percent',
          'RollbackSegmentHistoryListLength:Count',
          'RowLockTime:Milliseconds',
          'SelectLatency:Milliseconds',
          'SelectThroughput:Count/Second',
          'StorageNetworkReceiveThroughput:Bytes/Second',
          'StorageNetworkThroughput:Bytes/Second',
          'StorageNetworkTransmitThroughput:Bytes/Second',
          'SumBinaryLogSize:Bytes',
          'SwapUsage:Bytes',
          'TransactionLogsDiskUsage:Bytes',
          'UpdateLatency:Milliseconds',
          'UpdateThroughput:Count/Second',
          'WriteIOPS:Count/Second',
          'WriteLatency:Seconds',
          'WriteThroughput:Bytes/Second',
          'BurstBalance:Percent',
          'FreeStorageSpace:Percent',
          'OldestReplicationSlotLag:Bytes',
          'ReplicaLag:Seconds',
          'TransactionLogsGeneration:Bytes/Second'
        ];

        var metric_payload = [];
        metrics_list.forEach(function (metric) {
            var parts = metric.split(':', 2);
            var name = parts[0].replace(/[^a-zA-Z0-9]/g, '');
            metric_payload.push({
                'Id': name.charAt(0).toLowerCase() + name.slice(1),
                'MetricStat': {
                    'Metric': {
                        'MetricName': parts[0],
                        'Namespace': 'AWS/RDS',
                        'Dimensions': [
                            {
                                'Name': 'DBInstanceIdentifier',
                                'Value': db_instance_id
                            }
                        ]
                    },
                    'Period': period,
                    'Stat': 'Average',
                    'Unit': parts[1]
                }
            });
        });

        return metric_payload;

    },
    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, request_data) {
        if (typeof request_data === 'undefined' || request_data === null) {
            request_data = '';
        }
        else {
            request_data=  JSON.stringify(request_data)
        }

        var amzdate = (new Date()).toISOString().replace(/\\.\\d+Z/, 'Z').replace(/[-:]/g, ''),
            date = amzdate.replace(/T\\d+Z/, ''),
            host = service + '.' + region + '.amazonaws.com',
            canonical_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' + '\\n' + canonical_headers + '\\n' + signed_headers + '\\n' + sha256(request_data),
            credential_scope = date + '/' + region + '/' + service + '/' + 'aws4_request',
            request_string = 'AWS4-HMAC-SHA256' + '\\n' + amzdate + '\\n' + credential_scope + '\\n' + sha256(canonical_request),
            key = AwsRds.sign('AWS4' + AwsRds.params.secret_key, date);

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

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

        if (typeof AwsRds.params.proxy !== 'undefined' && AwsRds.params.proxy !== '') {
            request.setProxy(AwsRds.params.proxy);
        }
        request.addHeader('x-amz-date: ' + amzdate);
        request.addHeader('X-Amz-Target: GraniteServiceVersion20100801.GetMetricData')
        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=' + AwsRds.params.access_key + '/' + credential_scope + ', ' + 'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + hmac('sha256', key, request_string));

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

        response = request.post(url,request_data);
        Zabbix.log(4, '[ AWS RDS ] 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 = JSON.parse(response);
            }
            catch (error) {
                throw 'Failed to parse response received from AWS CloudWatch API. Check debug log for more information.';
            }
        }

        return response;
    },

    getMetricsData: function () {
        var payload={},
            end_time = Math.floor((new Date().getTime())/1000),
            start_time = end_time - AwsRds.request_period;

        payload['StartTime'] = start_time;
        payload['EndTime'] = end_time;
        payload['ScanBy'] = 'TimestampDescending';
        payload['MetricDataQueries'] = AwsRds.renderMetricQuery(AwsRds.request_period, AwsRds.params.db_instance_id);
        result = AwsRds.request('POST', AwsRds.params.region, 'monitoring', payload);
        if (typeof result !== 'object' || typeof result.MetricDataResults !== 'object') {
            throw 'Cannot get metrics data from AWS CloudWatch API. Check debug log for more information.';
        }

        return result.MetricDataResults;
    }
};

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

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

    return JSON.stringify({'error': error});
}