// Copyright 2025 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_SAFETY_HUB_UNUSED_SITE_PERMISSIONS_MANAGER_H_
#define CHROME_BROWSER_UI_SAFETY_HUB_UNUSED_SITE_PERMISSIONS_MANAGER_H_

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

#include "base/containers/flat_map.h"
#include "base/gtest_prod_util.h"
#include "base/memory/scoped_refptr.h"
#include "base/time/clock.h"
#include "base/values.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/ui/safety_hub/revoked_permissions_result.h"
#include "chrome/browser/ui/safety_hub/safety_hub_result.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings_constraints.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"

class PrefChangeRegistrar;
class PrefService;

namespace url {
class Origin;
}

// This class keeps track of unused site permissions by updating
// their last_visit date on navigations and clearing them periodically.
class UnusedSitePermissionsManager {
 public:
  explicit UnusedSitePermissionsManager(
      content::BrowserContext* browser_context,
      PrefService* prefs);

  UnusedSitePermissionsManager(const UnusedSitePermissionsManager&) = delete;
  UnusedSitePermissionsManager& operator=(const UnusedSitePermissionsManager&) =
      delete;

  ~UnusedSitePermissionsManager();

  // Does most of the heavy lifting of the update process: for each permission,
  // it determines whether it should be considered as recently unused (i.e. one
  // week). This list will be further filtered in the UI task to determine which
  // permissions should be revoked.
  static std::unique_ptr<SafetyHubResult> UpdateOnBackgroundThread(
      base::Clock* clock,
      const scoped_refptr<HostContentSettingsMap> hcsm,
      bool revocation_backfill_completed);

  // Helper to convert content settings type into its string representation.
  static std::string ConvertContentSettingsTypeToKey(ContentSettingsType type);

  // Helper to get content settings type from its string representation.
  static ContentSettingsType ConvertKeyToContentSettingsType(
      const std::string& key);

  // Helper to convert single origin primary pattern to an origin.
  // Converting a primary pattern to an origin is normally an anti-pattern, and
  // this method should only be used for single origin primary patterns.
  // They have fully defined URL+scheme+port which makes converting
  // a primary pattern to an origin successful.
  static url::Origin ConvertPrimaryPatternToOrigin(
      const ContentSettingsPattern& primary_pattern);

  // Revokes permissions from sites whose last visit is older than a defined
  // threshold (e.g. currently 60 days).
  void RevokeUnusedPermissions(std::unique_ptr<SafetyHubResult> result);

  // Returns true if settings are being changed due to auto revocation of
  // unused site permissions.
  bool IsRevocationRunning();

  // Called by TabHelper when a URL was visited.
  void OnPageVisited(const url::Origin& origin);

  // Re-grants permissions that are auto-revoked ones and removes the origin
  // from revoked permissions list.
  void RegrantPermissionsForOrigin(const url::Origin& origin);

  // Reverse changes made by |RegrantPermissionsForOrigin|. Adds this origin to
  // the removed permissions list and resets its permissions.
  void UndoRegrantPermissionsForOrigin(const PermissionsData& permission);

  // Removes a pattern from the list of revoked permissions so that the entry is
  // no longer shown to the user. Does not affect permissions themselves.
  void DeletePatternFromRevokedUnusedSitePermissionList(
      const ContentSettingsPattern& primary_pattern,
      const ContentSettingsPattern& secondary_pattern);

  // Stores revoked permissions data on HCSM.
  void StorePermissionInUnusedSitePermissionSetting(
      base::flat_map<ContentSettingsType, base::Value> permissions,
      const std::optional<content_settings::ContentSettingConstraints>
          constraint,
      const ContentSettingsPattern& primary_pattern,
      const ContentSettingsPattern& secondary_pattern);

  // Helper function to convert the stored_value in website settings to a map
  // from revoked permission type to revoked permission value, which can be
  // stored in PermissionData.
  base::flat_map<ContentSettingsType, base::Value> ExtractRevokedPermissions(
      base::Value stored_value);

  // Test support:
  void SetClockForTesting(base::Clock* clock);
  std::vector<ContentSettingEntry> GetTrackedUnusedPermissionsForTesting();
  using UntimestampedPermissionList =
      RevokedPermissionsResult::UntimestampedPermissionList;
  UntimestampedPermissionList GetUntimestampedPermissionsForTesting();

  using UnusedPermissionMap = RevokedPermissionsResult::UnusedPermissionMap;

 private:
  FRIEND_TEST_ALL_PREFIXES(UnusedSitePermissionsManagerTest,
                           UpdateIntegerValuesToGroupName_AllContentSettings);
  FRIEND_TEST_ALL_PREFIXES(
      UnusedSitePermissionsManagerTest,
      UpdateIntegerValuesToGroupName_SubsetOfContentSettings);
  FRIEND_TEST_ALL_PREFIXES(
      UnusedSitePermissionsManagerTest,
      UpdateIntegerValuesToGroupName_UnknownContentSettings);

  // If the user clicked "Allow again" for an auto-revoked origin, the
  // permissions for that site should not be auto-revoked again by the service.
  void IgnoreOriginForAutoRevocation(const url::Origin& origin);

  // Convert all integer permission values to string, if there is any
  // permission represented by integer.
  void UpdateIntegerValuesToGroupName();

  // Backfill untimestamped permissions' `last_visited` with the current date.
  //
  // The `last_visited` is coarsed by `GetCoarseVisitedTime` [1] due to privacy.
  // It rounds given timestamp down to the nearest multiple of 7 in the past.
  // [1] components/content_settings/core/browser/content_settings_utils.cc
  void MaybePerformLastVisitedBackfill(
      RevokedPermissionsResult* interim_result);

  // Pointer to an object that allows us to manage site permissions.
  HostContentSettingsMap* hcsm() {
    return HostContentSettingsMapFactory::GetForProfile(browser_context_.get());
  }

  // Pointer to a browser context whose permissions are being updated.
  raw_ptr<content::BrowserContext> browser_context_;

  // Observes user profile prefs.
  std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;

  // Set of permissions that haven't been used for at least a week.
  UnusedPermissionMap recently_unused_permissions_;

  UntimestampedPermissionList untimestamped_permissions_;

  // Returns true if automatic check and revocation of unused site permissions
  // is occurring. This value is used in `OnContentSettingChanged` to help
  // decide whether to clean up revoked permission data.
  bool is_unused_site_revocation_running_ = false;

  // Clock to implement revocation based on last visited time.
  raw_ptr<base::Clock> clock_;
};

#endif  // CHROME_BROWSER_UI_SAFETY_HUB_UNUSED_SITE_PERMISSIONS_MANAGER_H_
