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

#include "chrome/browser/extensions/component_migration_helper.h"

#include "base/stl_util.h"
#include "base/values.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_system_impl.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "content/public/browser/browser_context.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/feature_switch.h"

namespace extensions {

ComponentMigrationHelper::ComponentMigrationHelper(
    Profile* profile,
    ComponentActionDelegate* delegate)
    : profile_(profile),
      delegate_(delegate),
      extension_registry_(ExtensionRegistry::Get(profile)),
      pref_service_(profile->GetPrefs()),
      extension_system_(ExtensionSystem::Get(profile)),
      extension_registry_observer_(this) {
  DCHECK(delegate_);
  extension_registry_observer_.Add(extension_registry_);
}

ComponentMigrationHelper::~ComponentMigrationHelper() {}

// static
void ComponentMigrationHelper::RegisterPrefs(PrefRegistrySimple* registry) {
  registry->RegisterDictionaryPref(
      ::prefs::kToolbarMigratedComponentActionStatus);
}

void ComponentMigrationHelper::Register(const std::string& component_action_id,
                                        const ExtensionId& extension_id) {
  DCHECK(GetActionIdForExtensionId(extension_id).empty());
  migrated_actions_.push_back(
      std::make_pair(component_action_id, extension_id));
}

void ComponentMigrationHelper::Unregister(
    const std::string& component_action_id,
    const ExtensionId& extension_id) {
  for (auto it = migrated_actions_.begin(); it != migrated_actions_.end();
       it++) {
    if (it->first == component_action_id && it->second == extension_id) {
      migrated_actions_.erase(it);
      return;
    }
  }
}

void ComponentMigrationHelper::OnFeatureEnabled(
    const std::string& component_action_id) {
  DCHECK(FeatureSwitch::extension_action_redesign()->IsEnabled());
  std::vector<ExtensionId> extension_ids =
      GetExtensionIdsForActionId(component_action_id);
  DCHECK(!extension_ids.empty());

  enabled_actions_.insert(component_action_id);
  // Unload any extensions that we are migrating from.
  bool extension_was_installed = false;
  for (const auto& id : extension_ids) {
    if (IsExtensionInstalledAndEnabled(id)) {
      extension_was_installed = true;
      UnloadExtension(id);
    }
  }

  // Read the pref to determine component action status.  If not set, set to
  // true if we unloaded an extension.
  const base::DictionaryValue* migration_pref = pref_service_->GetDictionary(
      ::prefs::kToolbarMigratedComponentActionStatus);
  bool has_component_action_pref = migration_pref->HasKey(component_action_id);
  bool component_action_pref = false;
  if (has_component_action_pref) {
    bool success =
        migration_pref->GetBoolean(component_action_id, &component_action_pref);
    DCHECK(success);  // Shouldn't fail, but can in case of pref corruption.
  }

  if (!has_component_action_pref && extension_was_installed) {
    SetComponentActionPref(component_action_id, true);
    component_action_pref = true;
  }

  if (component_action_pref &&
      !delegate_->HasComponentAction(component_action_id)) {
    delegate_->AddComponentAction(component_action_id);
  }
}

void ComponentMigrationHelper::OnFeatureDisabled(
    const std::string& component_action_id) {
  std::vector<ExtensionId> extension_ids =
      GetExtensionIdsForActionId(component_action_id);
  DCHECK(!extension_ids.empty());

  enabled_actions_.erase(component_action_id);
  RemoveComponentActionPref(component_action_id);

  if (FeatureSwitch::extension_action_redesign()->IsEnabled() &&
      delegate_->HasComponentAction(component_action_id))
    delegate_->RemoveComponentAction(component_action_id);
}

void ComponentMigrationHelper::OnActionRemoved(
    const std::string& component_action_id) {
  // Record preference for the future.
  SetComponentActionPref(component_action_id, false);

  // Remove the action.
  if (delegate_->HasComponentAction(component_action_id))
    delegate_->RemoveComponentAction(component_action_id);
}

void ComponentMigrationHelper::OnExtensionReady(
    content::BrowserContext* browser_context,
    const Extension* extension) {
  const ExtensionId& extension_id = extension->id();
  const std::string& component_action_id =
      GetActionIdForExtensionId(extension_id);
  if (component_action_id.empty())
    return;
  if (ContainsKey(enabled_actions_, component_action_id)) {
    UnloadExtension(extension_id);
    SetComponentActionPref(component_action_id, true);

    if (!delegate_->HasComponentAction(component_action_id))
      delegate_->AddComponentAction(component_action_id);
  }
}

void ComponentMigrationHelper::SetComponentActionPref(
    const std::string& component_action_id,
    bool enabled) {
  DictionaryPrefUpdate update(pref_service_,
                              ::prefs::kToolbarMigratedComponentActionStatus);
  update->SetBoolean(component_action_id, enabled);
}

bool ComponentMigrationHelper::IsExtensionInstalledAndEnabled(
    const ExtensionId& extension_id) const {
  return extension_registry_->enabled_extensions().Contains(extension_id) ||
         extension_registry_->terminated_extensions().Contains(extension_id);
}

void ComponentMigrationHelper::UnloadExtension(
    const ExtensionId& extension_id) {
  extension_system_->extension_service()->UnloadExtension(
      extension_id, UnloadedExtensionInfo::REASON_MIGRATED_TO_COMPONENT);
}

void ComponentMigrationHelper::RemoveComponentActionPref(
    const std::string& component_action_id) {
  DictionaryPrefUpdate update(pref_service_,
                              ::prefs::kToolbarMigratedComponentActionStatus);
  update->Remove(component_action_id, nullptr);
}

std::vector<std::string> ComponentMigrationHelper::GetExtensionIdsForActionId(
    const std::string& component_action_id) const {
  std::vector<ExtensionId> extension_ids;
  for (const auto& i : migrated_actions_) {
    if (i.first == component_action_id)
      extension_ids.push_back(i.second);
  }
  return extension_ids;
}

std::string ComponentMigrationHelper::GetActionIdForExtensionId(
    const ExtensionId& extension_id) const {
  for (const auto& i : migrated_actions_) {
    if (i.second == extension_id)
      return i.first;
  }
  return "";
}

}  // namespace extensions