// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/tracing/common/background_tracing_state_manager.h"

#include <cstddef>
#include <cstdint>
#include <memory>

#include "base/json/values_util.h"
#include "base/memory/ptr_util.h"
#include "base/task/single_thread_task_runner.h"
#include "base/values.h"
#include "components/tracing/common/pref_names.h"

namespace tracing {
namespace {

constexpr char kTracingEnabledScenariosKey[] = "enabled_scenarios";
constexpr char kTracingPrivacyFilterKey[] = "privacy_filter";

BackgroundTracingStateManager* g_background_tracing_state_manager = nullptr;

}  // namespace

BackgroundTracingStateManager::BackgroundTracingStateManager(
    PrefService* local_state)
    : local_state_(local_state) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK_EQ(g_background_tracing_state_manager, nullptr);
  g_background_tracing_state_manager = this;

  Initialize();
}

BackgroundTracingStateManager::~BackgroundTracingStateManager() {
  DCHECK_EQ(g_background_tracing_state_manager, this);
  g_background_tracing_state_manager = nullptr;
}

std::unique_ptr<BackgroundTracingStateManager>
BackgroundTracingStateManager::CreateInstance(PrefService* local_state) {
  if (local_state == nullptr) {
    return nullptr;
  }
  return base::WrapUnique(new BackgroundTracingStateManager(local_state));
}

BackgroundTracingStateManager& BackgroundTracingStateManager::GetInstance() {
  CHECK_NE(nullptr, g_background_tracing_state_manager);
  return *g_background_tracing_state_manager;
}

void BackgroundTracingStateManager::Initialize() {
  DCHECK(local_state_);

  const base::DictValue& dict =
      local_state_->GetDict(kBackgroundTracingSessionState);

  auto* scenarios = dict.FindList(kTracingEnabledScenariosKey);
  if (scenarios) {
    for (const auto& item : *scenarios) {
      auto* scenario_hash = item.GetIfString();
      if (scenario_hash) {
        enabled_scenarios_.push_back(*scenario_hash);
      }
    }
  }

  std::optional<bool> privacy_filter_enabled =
      dict.FindBool(kTracingPrivacyFilterKey);
  if (privacy_filter_enabled) {
    privacy_filter_enabled_ = *privacy_filter_enabled;
  }
}

void BackgroundTracingStateManager::SaveState() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(local_state_);

  base::DictValue dict;

  if (!enabled_scenarios_.empty()) {
    base::ListValue scenarios;
    for (const auto& scenario_name : enabled_scenarios_) {
      scenarios.Append(scenario_name);
    }
    dict.Set(kTracingEnabledScenariosKey, std::move(scenarios));
  }
  dict.Set(kTracingPrivacyFilterKey, privacy_filter_enabled_);

  local_state_->SetDict(kBackgroundTracingSessionState, std::move(dict));
  local_state_->CommitPendingWrite();
}

void BackgroundTracingStateManager::UpdateEnabledScenarios(
    std::vector<std::string> enabled_scenarios) {
  enabled_scenarios_ = std::move(enabled_scenarios);
  SaveState();
}

void BackgroundTracingStateManager::UpdatePrivacyFilter(bool enabled) {
  privacy_filter_enabled_ = enabled;
  SaveState();
}

void BackgroundTracingStateManager::ResetForTesting() {
  enabled_scenarios_ = {};
  privacy_filter_enabled_ = false;
  Initialize();
}

}  // namespace tracing
