// 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.

#import "ios/chrome/browser/policy/model/cloud/user_policy_signin_service.h"

#import "base/feature_list.h"
#import "base/logging.h"
#import "base/time/time.h"
#import "components/enterprise/browser/groups/enterprise_groups_handler.h"
#import "components/enterprise/browser/identifiers/profile_id_service.h"
#import "components/enterprise/browser/reporting/common_pref_names.h"
#import "components/policy/core/browser/cloud/user_policy_signin_service_util.h"
#import "components/policy/core/common/cloud/cloud_policy_client_registration_helper.h"
#import "components/policy/core/common/policy_logger.h"
#import "components/policy/core/common/policy_pref_names.h"
#import "components/prefs/pref_service.h"
#import "components/signin/public/base/consent_level.h"
#import "components/signin/public/identity_manager/identity_manager.h"
#import "components/signin/public/identity_manager/primary_account_change_event.h"
#import "google_apis/gaia/core_account_id.h"
#import "google_apis/gaia/gaia_auth_util.h"
#import "ios/chrome/browser/policy/model/reporting/features.h"
#import "ios/chrome/browser/shared/model/profile/features.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "services/network/public/cpp/shared_url_loader_factory.h"

namespace {

// TODO(crbug.com/40831740): Move
// chrome/browser/signin/account_id_from_account_info.h to components/ to be
// able to reuse the helper here.
//
// Gets the AccountId from the provided `account_info`.
AccountId AccountIdFromAccountInfo(const CoreAccountInfo& account_info) {
  if (account_info.email.empty() || account_info.gaia.empty()) {
    return EmptyAccountId();
  }

  return AccountId::FromUserEmailGaiaId(
      gaia::CanonicalizeEmail(account_info.email), account_info.gaia);
}

}  // namespace

namespace policy {

UserPolicySigninService::UserPolicySigninService(
    PrefService* pref_service,
    PrefService* local_state,
    enterprise::ProfileIdService* profile_id_service,
    DeviceManagementService* device_management_service,
    UserCloudPolicyManager* policy_manager,
    signin::IdentityManager* identity_manager,
    EnterpriseGroupsProfileHandler* enterprise_groups_profile_handler,
    scoped_refptr<network::SharedURLLoaderFactory> system_url_loader_factory)
    : UserPolicySigninServiceBase(local_state,
                                  device_management_service,
                                  policy_manager,
                                  identity_manager,
                                  system_url_loader_factory),
      pref_service_(pref_service),
      profile_id_service_(profile_id_service),
      enterprise_groups_profile_handler_(enterprise_groups_profile_handler) {
  if (identity_manager) {
    scoped_identity_manager_observation_.Observe(identity_manager);
  }
  if (base::FeatureList::IsEnabled(
          enterprise_reporting::kCloudProfileReporting)) {
    CHECK(profile_id_service_);
  }

  TryInitialize();
}

UserPolicySigninService::~UserPolicySigninService() {}

void UserPolicySigninService::Shutdown() {
  scoped_identity_manager_observation_.Reset();
  CancelPendingRegistration();
  UserPolicySigninServiceBase::Shutdown();
}

void UserPolicySigninService::OnPrimaryAccountChanged(
    const signin::PrimaryAccountChangeEvent& event) {
  if (IsSignoutEvent(event)) {
    ShutdownCloudPolicyManager();
  } else {
    TryInitialize();
  }
}

void UserPolicySigninService::OnPolicyFetched(CloudPolicyClient* client) {
  std::optional<std::string> profile_id = client->profile_id();
  if (profile_id.has_value() && !profile_id.value().empty()) {
    pref_service_->SetBoolean(
        enterprise_reporting::kPoliciesEverFetchedWithProfileId, true);
  }
}

void UserPolicySigninService::TryInitialize() {
  // If using a TestingProfile with no IdentityManager or
  // CloudPolicyManager, skip initialization.
  if (!policy_manager() || !identity_manager()) {
    DVLOG_POLICY(1, POLICY_AUTH)
        << "Skipping initialization for tests due to missing components.";
    return;
  }

  if (!CanApplyPolicies(/*check_for_refresh_token=*/false)) {
    // Clear existing user policies if the feature is disabled or if policies
    // can no longer be applied.
    DVLOG_POLICY(3, POLICY_PROCESSING)
        << "Clearing existing user policies as the feature is disabled";
    ShutdownCloudPolicyManager();
    return;
  }
  AccountId account_id = AccountIdFromAccountInfo(
      identity_manager()->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin));
  InitializeForSignedInUser(account_id, system_url_loader_factory());
}

bool UserPolicySigninService::CanApplyPolicies(bool check_for_refresh_token) {
  return CanApplyPoliciesForSignedInUser(check_for_refresh_token,
                                         signin::ConsentLevel::kSignin,
                                         identity_manager());
}

std::string UserPolicySigninService::GetProfileId() {
  if (base::FeatureList::IsEnabled(
          enterprise_reporting::kCloudProfileReporting)) {
    return profile_id_service_->GetProfileId().value_or(std::string());
  }
  // Profile ID hasn't been implemented on iOS yet.
  return std::string();
}

base::TimeDelta UserPolicySigninService::GetTryRegistrationDelay() {
  return GetTryRegistrationDelayFromPrefs(pref_service_);
}

void UserPolicySigninService::UpdateLastPolicyCheckTime() {
  UpdateLastPolicyCheckTimeInPrefs(pref_service_);
}

void UserPolicySigninService::OnUserPolicyNotificationSeen() {
  TryInitialize();
}

void UserPolicySigninService::ShutdownCloudPolicyManager() {
  if (enterprise_groups_profile_handler_) {
    enterprise_groups_profile_handler_->ResetAndClearGroups();
  }
  UserPolicySigninServiceBase::ShutdownCloudPolicyManager();
}

void UserPolicySigninService::InitializeCloudPolicyManager(
    const AccountId& account_id,
    std::unique_ptr<CloudPolicyClient> client) {
  UserPolicySigninServiceBase::InitializeCloudPolicyManager(account_id,
                                                            std::move(client));
  if (enterprise_groups_profile_handler_) {
    enterprise_groups_profile_handler_->Init();
  }
}

}  // namespace policy
