// 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 COMPONENTS_SERVER_CERTIFICATE_DATABASE_SERVER_CERTIFICATE_DATABASE_SERVICE_H_
#define COMPONENTS_SERVER_CERTIFICATE_DATABASE_SERVER_CERTIFICATE_DATABASE_SERVICE_H_

#include <vector>

#include "base/callback_list.h"
#include "base/component_export.h"
#include "base/feature_list.h"
#include "base/functional/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/sequence_bound.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/server_certificate_database/server_certificate_database.h"

#if BUILDFLAG(IS_CHROMEOS)
#include "components/server_certificate_database/server_certificate_database_nss_migrator.h"
#endif

class PrefRegistrySimple;
class PrefService;

namespace net {

#if BUILDFLAG(IS_CHROMEOS)
namespace prefs {
// Integer that indicates whether the user's NSS certificates have been
// migrated to ServerCertificateDatabase. The value is a
// ServerCertificateDatabaseService::NSSMigrationResultPref enum.
inline constexpr char kNSSCertsMigratedToServerCertDb[] =
    "certificates.nss_certs_migrated_to_server_cert_db";
}  //  namespace prefs
#endif

#if BUILDFLAG(IS_CHROMEOS)
COMPONENT_EXPORT(SERVER_CERTIFICATE_DATABASE)
BASE_DECLARE_FEATURE(kEnableNSSCertMigration);
#endif

// KeyedService that loads and provides policies around usage of Certificates
// for TLS.
class COMPONENT_EXPORT(SERVER_CERTIFICATE_DATABASE)
    ServerCertificateDatabaseService : public KeyedService {
 public:
#if BUILDFLAG(IS_CHROMEOS)
  // These values are persisted to logs. Entries should not be renumbered and
  // numeric values should never be reused.
  enum class NSSMigrationResultHistogram {
    kNssDbEmpty = 0,
    kSuccess = 1,
    kPartialSuccess = 2,
    kFailed = 3,
    kMaxValue = kFailed,
  };

  // Enum that will record migration state in user's preferences. In the
  // current implementation, migration is only attempted once, but saving state
  // about whether there were any errors with the migration might be useful in
  // case there are issues during the rollout and we need to add new code that
  // can try again for anyone that had errors.
  // These values are persisted to prefs. Entries should not be renumbered and
  // numeric values should never be reused.
  enum class NSSMigrationResultPref : int {
    kNotMigrated = 0,
    kMigratedSuccessfully = 1,
    kMigrationHadErrors = 2,
  };
#endif

  using GetCertificatesCallback = base::OnceCallback<void(
      std::vector<net::ServerCertificateDatabase::CertInformation>)>;

#if BUILDFLAG(IS_CHROMEOS)
  explicit ServerCertificateDatabaseService(
      base::FilePath profile_path,
      PrefService* prefs,
      ServerCertificateDatabaseNSSMigrator::NssSlotGetter nss_slot_getter);
#else
  explicit ServerCertificateDatabaseService(base::FilePath profile_path);
#endif

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

  ~ServerCertificateDatabaseService() override;

  // Register a callback to be run every time the database is changed.
  base::CallbackListSubscription AddObserver(base::RepeatingClosure callback);

  // Add or update user settings with the included certificates.
  void AddOrUpdateUserCertificates(
      std::vector<net::ServerCertificateDatabase::CertInformation> cert_infos,
      base::OnceCallback<void(bool)> callback);

  // Read all certificates from the database.
  void GetAllCertificates(GetCertificatesCallback callback);

  // Run callback with `server_cert_database_`. The callback will be run on a
  // thread pool sequence where it is allowed to call methods on the database
  // object. This can be used to do multiple operations on the database without
  // repeated thread hops.
  //
  // TODO(https://crbug.com/40928765): This does NOT notify the observer if any
  // changes were made. For the current use case (only used by the NSS
  // migrator) this does not matter, but if anything else wants to use this to
  // change the database a solution would be needed.
  void PostTaskWithDatabase(
      base::OnceCallback<void(net::ServerCertificateDatabase*)> callback);

  void GetCertificatesCount(base::OnceCallback<void(uint32_t)> callback);

  void DeleteCertificate(const std::string& sha256hash_hex,
                         base::OnceCallback<void(bool)> callback);

#if BUILDFLAG(IS_CHROMEOS)
  static void RegisterProfilePrefs(PrefRegistrySimple* registry);

  static void DisableNSSCertMigrationForTesting();
#endif

  base::WeakPtr<ServerCertificateDatabaseService> GetWeakPtr() {
    return weak_factory_.GetWeakPtr();
  }

 private:
  void HandleModificationResult(base::OnceCallback<void(bool)> callback,
                                bool success);

#if BUILDFLAG(IS_CHROMEOS)
  void NSSMigrationComplete(
      ServerCertificateDatabaseNSSMigrator::MigrationResult result);
#endif

  const base::FilePath profile_path_;

  base::SequenceBound<net::ServerCertificateDatabase> server_cert_database_;
#if BUILDFLAG(IS_CHROMEOS)
  raw_ptr<PrefService> prefs_;
  ServerCertificateDatabaseNSSMigrator::NssSlotGetter nss_slot_getter_;
  std::unique_ptr<ServerCertificateDatabaseNSSMigrator> nss_migrator_;
  std::vector<GetCertificatesCallback> get_certificates_pending_migration_;
#endif

  base::RepeatingClosureList observers_;

  base::WeakPtrFactory<ServerCertificateDatabaseService> weak_factory_{this};
};

}  // namespace net

#endif  // COMPONENTS_SERVER_CERTIFICATE_DATABASE_SERVER_CERTIFICATE_DATABASE_SERVICE_H_
