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

#include "chrome/browser/ui/ash/login/kiosk_app_menu_controller.h"

#include <string>
#include <vector>

#include "ash/constants/ash_features.h"
#include "ash/public/cpp/kiosk_app_menu.h"
#include "ash/public/cpp/login_screen.h"
#include "base/check.h"
#include "base/check_deref.h"
#include "base/functional/bind.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ash/app_mode/arcvm_app/kiosk_arcvm_app_manager.h"
#include "chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_manager.h"
#include "chrome/browser/ash/app_mode/kiosk_app.h"
#include "chrome/browser/ash/app_mode/kiosk_app_launch_error.h"
#include "chrome/browser/ash/app_mode/kiosk_app_types.h"
#include "chrome/browser/ash/app_mode/kiosk_chrome_app_manager.h"
#include "chrome/browser/ash/app_mode/kiosk_controller.h"
#include "chrome/browser/ash/app_mode/web_app/kiosk_web_app_manager.h"
#include "chrome/browser/ui/ash/login/login_display_host.h"
#include "chrome/browser/ui/ash/login/login_screen_client_impl.h"
#include "extensions/grit/extensions_browser_resources.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/image/image_skia.h"

namespace ash {

namespace {

KioskAppId ToKioskAppId(const KioskAppMenuEntry& menu_entry) {
  switch (menu_entry.type) {
    case KioskAppMenuEntry::AppType::kWebApp:
      return KioskAppId::ForWebApp(menu_entry.account_id);
    case KioskAppMenuEntry::AppType::kChromeApp:
      CHECK(menu_entry.chrome_app_id.has_value());
      return KioskAppId::ForChromeApp(menu_entry.chrome_app_id.value(),
                                      menu_entry.account_id);
    case KioskAppMenuEntry::AppType::kIsolatedWebApp:
      return KioskAppId::ForIsolatedWebApp(menu_entry.account_id);
    case KioskAppMenuEntry::AppType::kArcvmApp:
      return KioskAppId::ForArcvmApp(menu_entry.account_id);
  }
}

KioskAppMenuEntry::AppType ToMenuEntryType(KioskAppType type) {
  switch (type) {
    case KioskAppType::kWebApp:
      return KioskAppMenuEntry::AppType::kWebApp;
    case KioskAppType::kChromeApp:
      return KioskAppMenuEntry::AppType::kChromeApp;
    case KioskAppType::kIsolatedWebApp:
      return KioskAppMenuEntry::AppType::kIsolatedWebApp;
    case KioskAppType::kArcvmApp:
      return KioskAppMenuEntry::AppType::kArcvmApp;
  }
}

gfx::ImageSkia IconOrDefault(gfx::ImageSkia icon) {
  return icon.isNull() ? *ui::ResourceBundle::GetSharedInstance()
                              .GetImageNamed(IDR_APP_DEFAULT_ICON)
                              .ToImageSkia()
                       : icon;
}

std::string GetMenuItemName(const KioskApp& app) {
  if (!app.name().empty()) {
    return std::string(app.name());
  }
  // The app name can be missing for an app that's received via policy
  // before the CRX is loaded. Display the ChromeApp ID or URL in that case to
  // prevent a blank menu item.
  switch (app.id().type) {
    case KioskAppType::kChromeApp:
    case KioskAppType::kIsolatedWebApp:
    case KioskAppType::kArcvmApp:
      return app.id().app_id.value();
    case KioskAppType::kWebApp:
      return app.url()->spec();
  }
}

KioskAppMenuEntry ToMenuEntry(const KioskApp& app) {
  return KioskAppMenuEntry(
      ToMenuEntryType(app.id().type), app.id().account_id, app.id().app_id,
      base::UTF8ToUTF16(GetMenuItemName(app)), IconOrDefault(app.icon()));
}

std::vector<KioskAppMenuEntry> BuildKioskAppMenuEntries() {
  std::vector<KioskAppMenuEntry> menu_entries;
  for (const KioskApp& app : KioskController::Get().GetApps()) {
    menu_entries.push_back(ToMenuEntry(app));
  }
  return menu_entries;
}

}  // namespace

KioskAppMenuController::KioskAppMenuController(PrefService* local_state)
    : local_state_(CHECK_DEREF(local_state)) {
  kiosk_observations_.AddObservation(KioskChromeAppManager::Get());
  kiosk_observations_.AddObservation(KioskWebAppManager::Get());
  kiosk_observations_.AddObservation(KioskIwaManager::Get());
  if (ash::features::IsHeliumArcvmKioskEnabled()) {
    kiosk_observations_.AddObservation(KioskArcvmAppManager::Get());
  }
}

KioskAppMenuController::~KioskAppMenuController() = default;

void KioskAppMenuController::OnKioskAppDataChanged(const std::string& app_id) {
  SendKioskApps();
  ConfigureKioskCallbacks();
}

void KioskAppMenuController::OnKioskAppDataLoadFailure(
    const std::string& app_id) {
  SendKioskApps();
}

void KioskAppMenuController::OnKioskAppsSettingsChanged() {
  SendKioskApps();
}

void KioskAppMenuController::SendKioskApps() {
  if (!LoginScreenClientImpl::HasInstance()) {
    return;
  }

  KioskAppMenu::Get()->SetKioskApps(BuildKioskAppMenuEntries());
  KioskAppLaunchError::Error error =
      KioskAppLaunchError::Get(local_state_.get());
  if (error == KioskAppLaunchError::Error::kNone) {
    return;
  }

  // Clear any old pending Kiosk launch errors
  KioskAppLaunchError::RecordMetricAndClear(local_state_.get());

  LoginScreen::Get()->ShowKioskAppError(
      KioskAppLaunchError::GetErrorMessage(error));
}

void KioskAppMenuController::ConfigureKioskCallbacks() {
  KioskAppMenu::Get()->ConfigureKioskCallbacks(
      base::BindRepeating(&KioskAppMenuController::LaunchApp,
                          weak_factory_.GetWeakPtr()),
      base::BindRepeating(&KioskAppMenuController::OnMenuWillShow,
                          weak_factory_.GetWeakPtr()));
}

void KioskAppMenuController::LaunchApp(const KioskAppMenuEntry& menu_entry) {
  LoginDisplayHost::default_host()->StartKiosk(ToKioskAppId(menu_entry),
                                               /*is_auto_launch=*/false);
}

void KioskAppMenuController::OnMenuWillShow() {
  // Web app based kiosk app will want to load their icons.
  KioskWebAppManager::Get()->LoadIcons();
}

}  // namespace ash
