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

#include "components/user_manager/user_manager.h"

#include "base/logging.h"
#include "components/account_id/account_id.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/user_manager/known_user.h"
#include "components/user_manager/multi_user/multi_user_sign_in_policy.h"
#include "components/user_manager/user_directory_integrity_manager.h"
#include "components/user_manager/user_manager_pref_names.h"
#include "components/user_manager/user_names.h"

namespace user_manager {

UserManager* UserManager::instance = nullptr;

UserManager::UserAccountData::UserAccountData(
    const std::u16string& display_name,
    const std::u16string& given_name,
    const std::string& locale)
    : display_name_(display_name), given_name_(given_name), locale_(locale) {}

UserManager::UserAccountData::~UserAccountData() = default;

UserManager::DeviceLocalAccountInfo::DeviceLocalAccountInfo(std::string user_id,
                                                            UserType type)
    : user_id(std::move(user_id)), type(type) {}

UserManager::DeviceLocalAccountInfo::DeviceLocalAccountInfo(
    const UserManager::DeviceLocalAccountInfo&) = default;

UserManager::DeviceLocalAccountInfo&
UserManager::DeviceLocalAccountInfo::operator=(
    const UserManager::DeviceLocalAccountInfo&) = default;

UserManager::DeviceLocalAccountInfo::~DeviceLocalAccountInfo() = default;

// static
void UserManager::RegisterPrefs(PrefRegistrySimple* registry) {
  registry->RegisterListPref(prefs::kRegularUsersPref);
  registry->RegisterStringPref(prefs::kLastLoggedInGaiaUser, "");
  registry->RegisterDictionaryPref(prefs::kUserDisplayName);
  registry->RegisterDictionaryPref(prefs::kUserGivenName);
  registry->RegisterDictionaryPref(prefs::kUserDisplayEmail);
  registry->RegisterDictionaryPref(prefs::kUserOAuthTokenStatus);
  registry->RegisterDictionaryPref(prefs::kUserForceOnlineSignin);
  registry->RegisterDictionaryPref(prefs::kUserType);
  registry->RegisterStringPref(prefs::kLastActiveUser, "");
  registry->RegisterDictionaryPref(prefs::kOwnerAccount);

  registry->RegisterListPref(prefs::kDeviceLocalAccountsWithSavedData);
  registry->RegisterStringPref(prefs::kDeviceLocalAccountPendingDataRemoval,
                               "");

  UserDirectoryIntegrityManager::RegisterLocalStatePrefs(registry);
  KnownUser::RegisterPrefs(registry);
}

// static
void UserManager::RegisterProfilePrefs(PrefRegistrySimple* registry) {
  registry->RegisterStringPref(
      prefs::kMultiProfileUserBehaviorPref,
      MultiUserSignInPolicyToPrefValue(MultiUserSignInPolicy::kUnrestricted));
  registry->RegisterBooleanPref(
      prefs::kMultiProfileNeverShowIntro, false,
      user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
  registry->RegisterBooleanPref(
      prefs::kMultiProfileWarningShowDismissed, false,
      user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
}

void UserManager::Initialize() {
  DCHECK(!UserManager::instance);
  UserManager::SetInstance(this);
}

// static
bool UserManager::IsInitialized() {
  return UserManager::instance;
}

void UserManager::Destroy() {
  DCHECK(UserManager::instance == this);
  UserManager::SetInstance(nullptr);
}

// static
UserManager* user_manager::UserManager::Get() {
  CHECK(UserManager::instance);
  return UserManager::instance;
}

UserManager::~UserManager() = default;

// static
void UserManager::SetInstance(UserManager* user_manager) {
  UserManager::instance = user_manager;
}

// static
UserManager* user_manager::UserManager::GetForTesting() {
  return UserManager::instance;
}

// static
UserManager* UserManager::SetForTesting(UserManager* user_manager) {
  UserManager* previous_instance = UserManager::instance;
  UserManager::instance = user_manager;
  return previous_instance;
}

UserType UserManager::CalculateUserType(const AccountId& account_id,
                                        const User* user,
                                        const bool browser_restart,
                                        const bool is_child) const {
  if (account_id == GuestAccountId()) {
    return UserType::kGuest;
  }

  // This may happen after browser crash after device account was marked for
  // removal, but before clean exit.
  if (browser_restart && IsDeviceLocalAccountMarkedForRemoval(account_id)) {
    return UserType::kPublicAccount;
  }

  // If user already exists
  if (user) {
    // This branch works for any other user type, including PUBLIC_ACCOUNT.
    const UserType user_type = user->GetType();
    if (user_type == UserType::kChild || user_type == UserType::kRegular) {
      const UserType new_user_type =
          is_child ? UserType::kChild : UserType::kRegular;
      if (new_user_type != user_type) {
        LOG(WARNING) << "Child user type has changed: " << user_type << " => "
                     << new_user_type;
      }
      return new_user_type;
    } else if (is_child) {
      LOG(FATAL) << "Incorrect child user type " << user_type;
    }

    return user_type;
  }

  // User is new
  if (is_child) {
    return UserType::kChild;
  }

  return UserType::kRegular;
}

bool UserManager::IsUserAllowed(const user_manager::User& user,
                                bool is_guest_allowed,
                                bool is_user_allowlisted) {
  DCHECK(user.GetType() == UserType::kRegular ||
         user.GetType() == UserType::kGuest ||
         user.GetType() == UserType::kChild);

  if (user.GetType() == UserType::kGuest && !is_guest_allowed) {
    return false;
  }
  if (user.HasGaiaAccount() && !is_user_allowlisted) {
    return false;
  }
  return true;
}

}  // namespace user_manager
