// 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/performance_manager/public/user_tuning/prefs.h"

#include <iterator>
#include <optional>
#include <string>
#include <utility>
#include <vector>

#include "base/json/values_util.h"
#include "base/values.h"
#include "components/performance_manager/public/features.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"

namespace performance_manager::user_tuning::prefs {

bool IsForceForegroundPriorityForAllTabsEnabled(PrefService* pref_service) {
  return base::FeatureList::IsEnabled(
             features::kForceForegroundPriorityForAllTabs) ||
         (pref_service &&
          pref_service->GetBoolean(kForceForegroundPriorityForAllTabs));
}

void RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
  registry->RegisterBooleanPref(kMemorySaverModeEnabled, false);
  registry->RegisterIntegerPref(
      kMemorySaverModeTimeBeforeDiscardInMinutes,
      kDefaultMemorySaverModeTimeBeforeDiscardInMinutes);
  registry->RegisterIntegerPref(
      kMemorySaverModeState, static_cast<int>(MemorySaverModeState::kDisabled));
  registry->RegisterIntegerPref(
      kMemorySaverModeAggressiveness,
      static_cast<int>(MemorySaverModeAggressiveness::kMedium));
  registry->RegisterIntegerPref(
      kBatterySaverModeState,
      static_cast<int>(BatterySaverModeState::kEnabledBelowThreshold));
  registry->RegisterTimePref(kLastBatteryUseTimestamp, base::Time());
  registry->RegisterBooleanPref(kDiscardRingTreatmentEnabled, true);
  registry->RegisterBooleanPref(kTabFreezingEnabled, true);
  registry->RegisterBooleanPref(kPerformanceInterventionNotificationEnabled,
                                true);
  registry->RegisterTimePref(kPerformanceInterventionNotificationLastShown,
                             base::Time());
  registry->RegisterListPref(kPerformanceInterventionNotificationAcceptHistory);
  registry->RegisterBooleanPref(kForceForegroundPriorityForAllTabs, false);
}

void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
  registry->RegisterListPref(kTabDiscardingExceptions,
                             user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
  registry->RegisterDictionaryPref(
      kTabDiscardingExceptionsWithTime,
      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
  registry->RegisterListPref(kManagedTabDiscardingExceptions);
  registry->RegisterListPref(kForceForegroundPriorityForUrls);
}

MemorySaverModeState GetCurrentMemorySaverModeState(PrefService* pref_service) {
  int state = pref_service->GetInteger(kMemorySaverModeState);
  if (state < static_cast<int>(MemorySaverModeState::kDisabled) ||
      state > static_cast<int>(MemorySaverModeState::kEnabled)) {
    int disabled_state = static_cast<int>(MemorySaverModeState::kDisabled);
    pref_service->SetInteger(kMemorySaverModeState, disabled_state);
    state = disabled_state;
  }

  return static_cast<MemorySaverModeState>(state);
}

MemorySaverModeAggressiveness GetCurrentMemorySaverMode(
    PrefService* pref_service) {
  int mode = pref_service->GetInteger(kMemorySaverModeAggressiveness);
  if (mode < static_cast<int>(MemorySaverModeAggressiveness::kConservative) ||
      mode > static_cast<int>(MemorySaverModeAggressiveness::kAggressive)) {
    int medium_mode = static_cast<int>(MemorySaverModeAggressiveness::kMedium);
    pref_service->SetInteger(kMemorySaverModeAggressiveness, medium_mode);
    mode = medium_mode;
  }

  return static_cast<MemorySaverModeAggressiveness>(mode);
}

base::TimeDelta GetCurrentMemorySaverModeTimeBeforeDiscard(
    PrefService* pref_service) {
  int time_before_discard_in_minutes =
      pref_service->GetInteger(kMemorySaverModeTimeBeforeDiscardInMinutes);
  if (time_before_discard_in_minutes < 0) {
    pref_service->ClearPref(kMemorySaverModeTimeBeforeDiscardInMinutes);
    time_before_discard_in_minutes =
        pref_service->GetInteger(kMemorySaverModeTimeBeforeDiscardInMinutes);
  }

  return base::Minutes(time_before_discard_in_minutes);
}

BatterySaverModeState GetCurrentBatterySaverModeState(
    PrefService* pref_service) {
  int state = pref_service->GetInteger(kBatterySaverModeState);
  if (state < static_cast<int>(BatterySaverModeState::kDisabled) ||
      state > static_cast<int>(BatterySaverModeState::kEnabled)) {
    int disabled_state = static_cast<int>(BatterySaverModeState::kDisabled);
    pref_service->SetInteger(kBatterySaverModeState, disabled_state);
    state = disabled_state;
  }

  return static_cast<BatterySaverModeState>(state);
}

bool ShouldShowDiscardRingTreatment(PrefService* pref_service) {
#if BUILDFLAG(IS_ANDROID)
  return false;
#else
  return pref_service->GetBoolean(kDiscardRingTreatmentEnabled);
#endif
}

bool ShouldShowPerformanceInterventionNotification(PrefService* pref_service) {
#if BUILDFLAG(IS_ANDROID)
  return false;
#else
  return pref_service->GetBoolean(kPerformanceInterventionNotificationEnabled);
#endif
}

void MigrateMemorySaverModePref(PrefService* pref_service) {
  const PrefService::Preference* state_pref =
      pref_service->FindPreference(kMemorySaverModeState);
  if (!state_pref->IsDefaultValue()) {
    // The user has changed the new pref, no migration needed. Clear the old
    // pref because it won't be used anymore. Note that this case should not
    // occur.
    pref_service->ClearPref(kMemorySaverModeEnabled);
    return;
  }

  const PrefService::Preference* bool_pref =
      pref_service->FindPreference(kMemorySaverModeEnabled);

  bool enabled = bool_pref->GetValue()->GetBool();
  int equivalent_int_pref =
      enabled ? static_cast<int>(MemorySaverModeState::kEnabled)
              : static_cast<int>(MemorySaverModeState::kDisabled);
  if (!bool_pref->IsDefaultValue()) {
    // The user has changed the old pref, but the new pref is still set to the
    // default value. This means the old pref's state needs to be migrated into
    // the new pref.
    pref_service->SetInteger(kMemorySaverModeState, equivalent_int_pref);
    // Clear the old pref because it won't be used anymore.
    pref_service->ClearPref(kMemorySaverModeEnabled);
  }
}

void MigrateMultiStateMemorySaverModePref(PrefService* pref_service) {
  const PrefService::Preference* state_pref =
      pref_service->FindPreference(kMemorySaverModeState);
  if (!state_pref->IsDefaultValue() &&
      static_cast<MemorySaverModeState>(state_pref->GetValue()->GetInt()) ==
          MemorySaverModeState::kDeprecated) {
    pref_service->SetInteger(kMemorySaverModeState,
                             static_cast<int>(MemorySaverModeState::kEnabled));
  }

  pref_service->ClearPref(kMemorySaverModeTimeBeforeDiscardInMinutes);
}

bool IsSiteInTabDiscardExceptionsList(PrefService* pref_service,
                                      const std::string& site) {
  const base::DictValue& discard_exceptions_map =
      pref_service->GetDict(kTabDiscardingExceptionsWithTime);
  return discard_exceptions_map.contains(site);
}

void AddSiteToTabDiscardExceptionsList(PrefService* pref_service,
                                       const std::string& site) {
  const base::DictValue& discard_exceptions_original =
      pref_service->GetDict(kTabDiscardingExceptionsWithTime);
  if (!discard_exceptions_original.contains(site)) {
    base::DictValue discard_exceptions_map =
        discard_exceptions_original.Clone();
    discard_exceptions_map.Set(site, base::TimeToValue(base::Time::Now()));
    pref_service->SetDict(kTabDiscardingExceptionsWithTime,
                          std::move(discard_exceptions_map));
  }
}

std::vector<std::string> GetTabDiscardExceptionsBetween(
    PrefService* pref_service,
    base::Time period_start,
    base::Time period_end) {
  std::vector<std::string> discard_exceptions;

  const base::DictValue& discard_exceptions_map =
      pref_service->GetDict(performance_manager::user_tuning::prefs::
                                kTabDiscardingExceptionsWithTime);
  for (const std::pair<const std::string&, const base::Value&> it :
       discard_exceptions_map) {
    std::optional<base::Time> time = base::ValueToTime(it.second);
    if ((!time || (time > period_start && time < period_end))) {
      discard_exceptions.push_back(it.first);
    }
  }

  return discard_exceptions;
}

void ClearTabDiscardExceptions(PrefService* pref_service,
                               base::Time delete_begin,
                               base::Time delete_end) {
  const base::DictValue& discard_exceptions_map =
      pref_service->GetDict(kTabDiscardingExceptionsWithTime);

  std::vector<std::pair<std::string, base::Value>> saved_exceptions;
  saved_exceptions.reserve(discard_exceptions_map.size());

  for (std::pair<const std::string&, const base::Value&> it :
       discard_exceptions_map) {
    std::optional<base::Time> time = base::ValueToTime(it.second);
    if (time && (time.value() < delete_begin || time.value() > delete_end)) {
      saved_exceptions.emplace_back(it.first, it.second.Clone());
    }
  }
  pref_service->SetDict(
      kTabDiscardingExceptionsWithTime,
      base::DictValue(std::make_move_iterator(saved_exceptions.begin()),
                      std::make_move_iterator(saved_exceptions.end())));
}

}  // namespace performance_manager::user_tuning::prefs
