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

#include "components/services/app_service/public/cpp/permission.h"

#include <sstream>
#include <variant>

#include "base/containers/to_value_list.h"
#include "base/strings/to_string.h"

namespace apps {

namespace {

const char kPermissionTypeKey[] = "permission_type";
const char kValueKey[] = "value";
const char kIsManagedKey[] = "is_managed";
const char kDetailsKey[] = "details";

}  // namespace

APP_ENUM_TO_STRING(PermissionType,
                   kUnknown,
                   kCamera,
                   kLocation,
                   kMicrophone,
                   kNotifications,
                   kContacts,
                   kStorage,
                   kPrinting,
                   kFileHandling)
APP_ENUM_TO_STRING(TriState, kAllow, kBlock, kAsk)

Permission::Permission(PermissionType permission_type,
                       PermissionValue value,
                       bool is_managed,
                       std::optional<std::string> details)
    : permission_type(permission_type),
      value(std::move(value)),
      is_managed(is_managed),
      details(std::move(details)) {}

Permission::~Permission() = default;

PermissionPtr Permission::Clone() const {
  return std::make_unique<Permission>(permission_type, value, is_managed,
                                      details);
}

bool Permission::IsPermissionEnabled() const {
  if (std::holds_alternative<bool>(value)) {
    return std::get<bool>(value);
  }
  if (std::holds_alternative<TriState>(value)) {
    return std::get<TriState>(value) == TriState::kAllow;
  }
  return false;
}

std::string Permission::ToString() const {
  std::stringstream out;
  out << " permission type: " << EnumToString(permission_type) << std::endl;
  if (std::holds_alternative<bool>(value)) {
    out << " bool_value: " << base::ToString(std::get<bool>(value));
  } else if (std::holds_alternative<TriState>(value)) {
    out << " tristate_value: " << EnumToString(std::get<TriState>(value));
  }
  out << std::endl;
  if (details.has_value()) {
    out << " details: " << details.value() << std::endl;
  }
  out << " is_managed: " << base::ToString(is_managed) << std::endl;
  return out.str();
}

Permissions ClonePermissions(const Permissions& source_permissions) {
  Permissions permissions;
  for (const auto& permission : source_permissions) {
    permissions.push_back(permission->Clone());
  }
  return permissions;
}

bool IsEqual(const Permissions& source, const Permissions& target) {
  if (source.size() != target.size()) {
    return false;
  }

  for (int i = 0; i < static_cast<int>(source.size()); i++) {
    if (*source[i] != *target[i]) {
      return false;
    }
  }
  return true;
}

base::DictValue ConvertPermissionToDict(const PermissionPtr& permission) {
  base::DictValue dict;

  if (!permission) {
    return dict;
  }

  dict.Set(kPermissionTypeKey, static_cast<int>(permission->permission_type));

  if (std::holds_alternative<bool>(permission->value)) {
    dict.Set(kValueKey, std::get<bool>(permission->value));
  } else if (std::holds_alternative<TriState>(permission->value)) {
    dict.Set(kValueKey,
             static_cast<int>(std::get<TriState>(permission->value)));
  }

  dict.Set(kIsManagedKey, permission->is_managed);

  if (permission->details.has_value()) {
    dict.Set(kDetailsKey, permission->details.value());
  }

  return dict;
}

PermissionPtr ConvertDictToPermission(const base::DictValue& dict) {
  std::optional<int> permission_type = dict.FindInt(kPermissionTypeKey);
  if (!permission_type.has_value() ||
      permission_type.value() < static_cast<int>(PermissionType::kUnknown) ||
      permission_type.value() > static_cast<int>(PermissionType::kMaxValue)) {
    return nullptr;
  }

  Permission::PermissionValue permission_value;
  std::optional<bool> value = dict.FindBool(kValueKey);
  if (value.has_value()) {
    permission_value = value.value();
  } else {
    std::optional<int> tri_state = dict.FindInt(kValueKey);
    if (tri_state.has_value() &&
        tri_state.value() >= static_cast<int>(TriState::kAllow) &&
        tri_state.value() <= static_cast<int>(TriState::kMaxValue)) {
      permission_value = static_cast<TriState>(tri_state.value());
    } else {
      return nullptr;
    }
  }

  std::optional<bool> is_managed = dict.FindBool(kIsManagedKey);
  if (!is_managed.has_value()) {
    return nullptr;
  }

  const std::string* details = dict.FindString(kDetailsKey);

  return std::make_unique<Permission>(
      static_cast<PermissionType>(permission_type.value()), permission_value,
      is_managed.value(),
      details ? std::optional<std::string>(*details) : std::nullopt);
}

base::ListValue ConvertPermissionsToList(const Permissions& permissions) {
  return base::ToValueList(permissions, &ConvertPermissionToDict);
}

Permissions ConvertListToPermissions(const base::ListValue* list) {
  Permissions permissions;

  if (!list) {
    return permissions;
  }

  for (const base::Value& permission : *list) {
    PermissionPtr parsed_permission =
        ConvertDictToPermission(permission.GetDict());
    if (parsed_permission) {
      permissions.push_back(std::move(parsed_permission));
    }
  }
  return permissions;
}

}  // namespace apps
