#!/usr/bin/env python3
# Copyright (C) 2019 The Android Open Source Project
#
# 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.

import argparse
import json
import hashlib
import sys

from config import DB, PROJECT
from common_utils import req, SCOPES
'''
Uploads the performance metrics of the Perfetto tests to StackDriver and
Firebase.

The expected format of the JSON is as follows:
{
  metrics: [
    {
      'metric': *metric name*,
      'value': *metric value*,
      'unit': *either s (seconds) or b (bytes)*,
      'tags': {
        *tag name*: *tag value*,
        ...
      },
      'labels': {
        *label name*: *label value*,
        ...
      }
    },
    ...
  ]
}
'''

STACKDRIVER_API = 'https://monitoring.googleapis.com/v3/projects/%s' % PROJECT
SCOPES.append('https://www.googleapis.com/auth/firebase.database')
SCOPES.append('https://www.googleapis.com/auth/userinfo.email')
SCOPES.append('https://www.googleapis.com/auth/monitoring.write')


def sha1(obj):
  hasher = hashlib.sha1()
  hasher.update(
      json.dumps(obj, sort_keys=True, separators=(',', ':')).encode('utf-8'))
  return hasher.hexdigest()


def metric_list_to_hash_dict(raw_metrics):
  metrics = {}
  for metric in raw_metrics:
    key = '%s-%s' % (metric['metric'], sha1(metric['tags']))
    metrics[key] = metric
  return metrics


def create_stackdriver_metrics(ts, metrics):
  # Chunk up metrics into 100 element chunks to comply with Stackdriver's
  # restrictions on the number of metrics in a request.
  metrics_list = list(metrics.values())
  metric_chunks = [metrics_list[x:x + 100] for x in range(0, len(metrics), 100)]
  desc_chunks = []

  for chunk in metric_chunks:
    desc = {'timeSeries': []}
    for metric in chunk:
      metric_name = metric['metric']
      desc['timeSeries'] += [{
          'metric': {
              'type':
                  'custom.googleapis.com/perfetto-ci/perf/%s' % metric_name,
              'labels':
                  dict(
                      list(metric.get('tags', {}).items()) +
                      list(metric.get('labels', {}).items())),
          },
          'resource': {
              'type': 'global'
          },
          'points': [{
              'interval': {
                  'endTime': ts
              },
              'value': {
                  'doubleValue': str(metric['value'])
              }
          }]
      }]
    desc_chunks.append(desc)
  return desc_chunks


def main():
  parser = argparse.ArgumentParser()
  parser.add_argument(
      '--job-id',
      type=str,
      required=True,
      help='The Perfetto CI job ID to tie this upload to')
  parser.add_argument(
      'metrics_file', type=str, help='File containing the metrics to upload')
  args = parser.parse_args()

  with open(args.metrics_file, 'r') as metrics_file:
    raw_metrics = json.loads(metrics_file.read())

  job = req('GET', '%s/jobs/%s.json' % (DB, args.job_id))
  ts = job['time_started']

  metrics = metric_list_to_hash_dict(raw_metrics['metrics'])
  req('PUT', '%s/perf/%s.json' % (DB, args.job_id), body=metrics)

  # Only upload Stackdriver metrics for post-submit runs.
  git_ref = job['env'].get('PERFETTO_TEST_GIT_REF')
  if git_ref == 'refs/heads/main':
    sd_metrics_chunks = create_stackdriver_metrics(ts, metrics)
    for sd_metrics in sd_metrics_chunks:
      req('POST', STACKDRIVER_API + '/timeSeries', body=sd_metrics)

  return 0


if __name__ == '__main__':
  sys.exit(main())
