//
// Copyright (C) 2022 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.

#include "cuttlefish/host/commands/metrics/events.h"

#include <sys/utsname.h>

#include "absl/log/log.h"
#include "google/protobuf/timestamp.pb.h"

#include "cuttlefish/host/commands/metrics/clearcut_protos.h"
#include "cuttlefish/host/commands/metrics/send.h"
#include "cuttlefish/host/commands/metrics/utils.h"
#include "cuttlefish/host/libs/config/vmm_mode.h"
#include "external_proto/cf_log.pb.h"
#include "external_proto/cf_metrics_event.pb.h"
#include "external_proto/clientanalytics.pb.h"

namespace cuttlefish::metrics {

namespace {

using google::protobuf::Timestamp;
using logs::proto::wireless::android::cuttlefish::CuttlefishLogEvent;
using logs::proto::wireless::android::cuttlefish::events::MetricsEvent;
using wireless_android_play_playlog::LogEvent;
using wireless_android_play_playlog::LogRequest;

// TODO: 403646742 - this value previously came from the build, need to revisit
static constexpr int PRODUCT_SHIPPING_API_LEVEL = 37;

Timestamp MillisToTimestamp(uint64_t millis) {
  Timestamp timestamp;
  timestamp.set_nanos((millis % 1000) * 1000000);
  timestamp.set_seconds(millis / 1000);

  return timestamp;
}

MetricsEvent::OsType GetOsType() {
  struct utsname buf;
  if (uname(&buf) != 0) {
    LOG(ERROR) << "failed to retrieve system information";
    return MetricsEvent::CUTTLEFISH_OS_TYPE_UNSPECIFIED;
  }
  std::string sysname(buf.sysname);
  std::string machine(buf.machine);

  if (sysname != "Linux") {
    return MetricsEvent::CUTTLEFISH_OS_TYPE_UNSPECIFIED;
  }
  if (machine == "x86_64") {
    return MetricsEvent::CUTTLEFISH_OS_TYPE_LINUX_X86_64;
  }
  if (machine == "x86") {
    return MetricsEvent::CUTTLEFISH_OS_TYPE_LINUX_X86;
  }
  if (machine == "aarch64" || machine == "arm64") {
    return MetricsEvent::CUTTLEFISH_OS_TYPE_LINUX_AARCH64;
  }
  if (machine[0] == 'a') {
    return MetricsEvent::CUTTLEFISH_OS_TYPE_LINUX_AARCH32;
  }
  return MetricsEvent::CUTTLEFISH_OS_TYPE_UNSPECIFIED;
}

MetricsEvent::VmmType GetVmm(VmmMode vmm_mode) {
  switch (vmm_mode) {
    case VmmMode::kCrosvm:
      return MetricsEvent::CUTTLEFISH_VMM_TYPE_CROSVM;
    case VmmMode::kQemu:
      return MetricsEvent::CUTTLEFISH_VMM_TYPE_QEMU;
    default:
      return MetricsEvent::CUTTLEFISH_VMM_TYPE_UNSPECIFIED;
  }
}

// Builds the 2nd level MetricsEvent.
MetricsEvent BuildMetricsEvent(uint64_t now_ms, VmmMode vmm_mode,
                               MetricsEvent::EventType event_type) {
  // "metrics_event" is the 2nd level MetricsEvent
  MetricsEvent metrics_event;
  metrics_event.set_event_type(event_type);
  metrics_event.set_os_type(GetOsType());
  metrics_event.set_os_version(GetOsVersion());
  metrics_event.set_vmm_type(GetVmm(vmm_mode));

  if (!GetVmmVersion().empty()) {
    metrics_event.set_vmm_version(GetVmmVersion());
  }

  metrics_event.set_company(GetCompany());
  metrics_event.set_api_level(PRODUCT_SHIPPING_API_LEVEL);

  *metrics_event.mutable_event_time() = MillisToTimestamp(now_ms);

  return metrics_event;
}

int SendEvent(MetricsEvent::EventType event_type, VmmMode vmm_mode) {
  uint64_t now_ms = GetEpochTimeMs();

  CuttlefishLogEvent cf_event = BuildCfLogEvent(now_ms);
  *cf_event.mutable_metrics_event() =
      BuildMetricsEvent(now_ms, vmm_mode, event_type);

  LogEvent log_event = BuildLogEvent(now_ms, cf_event);

  LogRequest log_request = BuildLogRequest(now_ms, log_event);
  std::string log_request_str = log_request.SerializeAsString();

  return PostRequest(log_request_str, ClearcutServer::kProd);
}

}  // namespace

CuttlefishLogEvent BuildCfLogEvent(uint64_t now_ms) {
  CuttlefishLogEvent cf_event;
  cf_event.set_device_type(CuttlefishLogEvent::CUTTLEFISH_DEVICE_TYPE_HOST);
  cf_event.set_session_id(GenerateSessionId(now_ms));

  if (!GetCfVersion().empty()) {
    cf_event.set_cuttlefish_version(GetCfVersion());
  }

  *cf_event.mutable_timestamp_ms() = MillisToTimestamp(now_ms);

  return cf_event;
}

int SendVMStart(VmmMode vmm_mode) {
  return SendEvent(MetricsEvent::CUTTLEFISH_EVENT_TYPE_VM_INSTANTIATION,
                   vmm_mode);
}

int SendVMStop(VmmMode vmm_mode) {
  return SendEvent(MetricsEvent::CUTTLEFISH_EVENT_TYPE_VM_STOP, vmm_mode);
}

int SendDeviceBoot(VmmMode vmm_mode) {
  return SendEvent(MetricsEvent::CUTTLEFISH_EVENT_TYPE_DEVICE_BOOT, vmm_mode);
}

int SendLockScreen(VmmMode vmm_mode) {
  return SendEvent(MetricsEvent::CUTTLEFISH_EVENT_TYPE_LOCK_SCREEN_AVAILABLE,
                   vmm_mode);
}

}  // namespace cuttlefish::metrics
