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

#ifndef CHROME_BROWSER_SSL_DAILY_NAVIGATION_COUNTER_H_
#define CHROME_BROWSER_SSL_DAILY_NAVIGATION_COUNTER_H_

#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "base/values.h"

namespace base {
class Clock;
}

// This class records the number of navigations inside a rolling window. Each
// navigation is counted in its day's bucket. Counts are regularly saved to
// a dictionary which can be backed by a pref, so that the counts are persisted.
// Counts belonging to the days outside the rolling window are discarded.
// This class is only intended to count an approximate the number of
// navigations for HTTPS-First Mode related heuristics. Browser restarts or
// crashes may lose some counts which shouldn't affect the heuristics
// significantly.
class DailyNavigationCounter {
 public:
  // `dict` is the backing dictionary where counts are read from and saved to.
  // `clock` is the clock used to get time.
  // `rolling_window_duration_in_days` is the size of the rolling window during
  // which navigations are recorded. Counts outside this window will be
  // discarded. E.g. rolling_window_duration_in_days=1 means counts added two
  // days ago will be discarded today.
  // `save_interval` is the number of navigations after which the counts are
  // saved to `dict`.
  DailyNavigationCounter(base::DictValue* dict,
                         base::Clock* clock,
                         size_t rolling_window_duration_in_days,
                         size_t save_interval);
  virtual ~DailyNavigationCounter();

  // Returns the total number of navigations.
  size_t GetTotal() const;
  // Increments the number of navigations for this day. Returns true if the
  // counts are written to `dict`.
  bool Increment();

  // Returns the number of navigations that are in memory but not yet saved
  // to the dict.
  size_t unsaved_count_for_testing() const { return unsaved_count_; }

 private:
  raw_ptr<base::DictValue> dict_;
  raw_ptr<base::Clock> clock_;
  // Number of navigations that haven't been saved in the pref yet.
  int unsaved_count_ = 0;
  // Age of the oldest navigation to be counted. Anything older than this will
  // be discarded.
  size_t rolling_window_duration_in_days_;
  // If the number of unsaved navigations is larger than this, counts will be
  // saved to the dict.
  int save_interval_ = 10;
  // The map that counts the navigations. Keys are dates formatted as
  // yyyy-MM-DD, values are the number of navigations on that day. At any point,
  // there will be at most (save_interval_ + rolling_window_duration_in_days)
  // different keys in this map. After that, entries will be written into
  // dict_, discarding old entries.
  //
  // This could be a simple integer, but that would would prevent us from
  // discarding very old counts when the profile wasn't used for a long time.
  // E.g. 99 navigations made 20 days ago, then zero for 20 days, then one
  // navigation next day would return 100 navigations in total instead of one,
  // if save_interval_=100.
  base::flat_map<std::string, int> counts_map_;
};

#endif  // CHROME_BROWSER_SSL_DAILY_NAVIGATION_COUNTER_H_
