// Copyright 2024 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_UI_USER_EDUCATION_RECENT_SESSION_POLICY_H_
#define CHROME_BROWSER_UI_USER_EDUCATION_RECENT_SESSION_POLICY_H_

#include <optional>
#include <utility>
#include <vector>

#include "base/time/time.h"
#include "chrome/browser/user_education/browser_user_education_storage_service.h"

// Class that defines policy for handling recent sessions.
// Allows recording of metrics and possibly modifying promo behavior.
class RecentSessionPolicy {
 public:
  virtual ~RecentSessionPolicy() = default;

  // Records any metrics associated with tracking recent usage.
  virtual void RecordRecentUsageMetrics(
      const RecentSessionData& recent_sessions) = 0;

  // Determines whether low usage should be taken into account when choosing
  // whether to show promos.
  virtual bool ShouldEnableLowUsagePromoMode(
      const RecentSessionData& recent_sessions) const = 0;
};

// Uses a series of thresholds to determine if this is a high- or low-usage
// profile/installation. Default is to follow guidance from UX research, but the
// parameters are adjustable via trade study parameters.
class RecentSessionPolicyImpl : public RecentSessionPolicy {
 public:
  // Represents one element of a logging and/or low-usage-detection policy.
  class Constraint {
   public:
    virtual ~Constraint() = default;

    // Analyzes `recent_sessions` and returns a count.
    // If there is insufficient data to produce a count, returns std::nullopt.
    virtual std::optional<int> GetCount(
        const RecentSessionData& recent_sessions) const = 0;

    // Returns whether recording of metrics and sessions should be skipped
    // because e.g. the metric would have been recorded already during the
    // current calendar day. Default is false.
    virtual bool ShouldSkipRecording(
        const RecentSessionData& recent_sessions) const;
  };

  // Counts the number of sessions in the given number of `days`. Does not
  // care about calendar days; this is just 24-hour periods.
  class SessionCountConstraint : public Constraint {
   public:
    explicit SessionCountConstraint(int days) : days_(days) {}
    ~SessionCountConstraint() override = default;
    std::optional<int> GetCount(
        const RecentSessionData& recent_sessions) const override;

   private:
    const int days_;
  };

  // Represents a constraint that should only be recorded daily.
  class DailyConstraint : public Constraint {
   public:
    bool ShouldSkipRecording(
        const RecentSessionData& recent_sessions) const override;
  };

  // Counts the number of active weeks in the past number of `weeks`. Uses the
  // last seven calendar days (including today).
  class ActiveWeeksConstraint : public DailyConstraint {
   public:
    explicit ActiveWeeksConstraint(int weeks, int active_days)
        : weeks_(weeks), active_days_(active_days) {}
    ~ActiveWeeksConstraint() override = default;
    std::optional<int> GetCount(
        const RecentSessionData& recent_sessions) const override;

   private:
    const int weeks_;
    const int active_days_;
  };

  // Counts the number of active days in the past number of `days`. Uses
  // calendar days, including today.
  class ActiveDaysConstraint : public DailyConstraint {
   public:
    explicit ActiveDaysConstraint(int days) : days_(days) {}
    ~ActiveDaysConstraint() override = default;
    std::optional<int> GetCount(
        const RecentSessionData& recent_sessions) const override;

   private:
    const int days_;
  };

  // Contains data about the various constraints.
  struct ConstraintInfo {
    ConstraintInfo();
    ConstraintInfo(std::unique_ptr<Constraint> constraint,
                   std::string histogram_name,
                   std::optional<int> histogram_max,
                   std::optional<int> low_usage_max);
    ConstraintInfo(ConstraintInfo&&) noexcept;
    ConstraintInfo& operator=(ConstraintInfo&&) noexcept;
    ~ConstraintInfo();

    // The constraint itself.
    std::unique_ptr<Constraint> constraint;

    // The histogram to log, if any.
    std::string histogram_name;

    // The max of the histogram; if zero, a default value is used.
    std::optional<int> histogram_max;

    // The threshold above which the current user is not eligible for low usage
    // promo mode. If not specified, only histograms will be emitted.
    std::optional<int> low_usage_max;
  };
  using ConstraintInfos = std::vector<ConstraintInfo>;

  // Creates a policy with the given constraints.
  explicit RecentSessionPolicyImpl(
      ConstraintInfos constraints = GetDefaultConstraints());
  RecentSessionPolicyImpl(const RecentSessionPolicyImpl&) = delete;
  void operator=(const RecentSessionPolicyImpl&) = delete;
  ~RecentSessionPolicyImpl() override;

  // RecentSessionPolicy:
  void RecordRecentUsageMetrics(
      const RecentSessionData& recent_sessions) override;
  bool ShouldEnableLowUsagePromoMode(
      const RecentSessionData& recent_sessions) const override;

  void set_constraints_for_testing(ConstraintInfos constraints) {
    CHECK(!constraints.empty());
    constraints_ = std::move(constraints);
  }

 private:
  // Gets a set of constraints with parameters read from the feature flag, or
  // sensible defaults.
  static ConstraintInfos GetDefaultConstraints();

  ConstraintInfos constraints_;
};

#endif  // CHROME_BROWSER_UI_USER_EDUCATION_RECENT_SESSION_POLICY_H_
