// Copyright 2024 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/web_applications/model/isolation_data.h"

#include "base/containers/to_value_list.h"
#include "base/values.h"
#include "components/webapps/isolated_web_apps/types/update_channel.h"

namespace web_app {

namespace {

void PersistFieldsForUpdateImpl(IsolationData::Builder& builder,
                                const IsolationData& isolation_data) {
  builder.SetControlledFramePartitions(
      isolation_data.controlled_frame_partitions());
  if (isolation_data.update_manifest_url()) {
    builder.SetUpdateManifestUrl(*isolation_data.update_manifest_url());
  }
  if (isolation_data.update_channel()) {
    builder.SetUpdateChannel(*isolation_data.update_channel());
  }
}

}  // namespace

IsolationData::IsolationData(
    IsolatedWebAppStorageLocation location,
    IwaVersion version,
    std::set<std::string> controlled_frame_partitions,
    std::optional<PendingUpdateInfo> pending_update_info,
    std::optional<IntegrityBlockData> integrity_block_data,
    std::optional<GURL> update_manifest_url,
    std::optional<UpdateChannel> update_channel,
    std::optional<OpenedTabsCounterNotificationState>
        opened_tabs_counter_notification_state)
    : location_(std::move(location)),
      version_(std::move(version)),
      controlled_frame_partitions_(std::move(controlled_frame_partitions)),
      pending_update_info_(std::move(pending_update_info)),
      integrity_block_data_(std::move(integrity_block_data)),
      update_manifest_url_(std::move(update_manifest_url)),
      opened_tabs_counter_notification_state_(
          std::move(opened_tabs_counter_notification_state)),
      update_channel_(std::move(update_channel)) {
  CHECK(!update_manifest_url_.has_value() || update_manifest_url_->is_valid(),
        base::NotFatalUntil::M138);
}

IsolationData::~IsolationData() = default;
IsolationData::IsolationData(const IsolationData&) = default;
IsolationData& IsolationData::operator=(const IsolationData&) = default;
IsolationData::IsolationData(IsolationData&&) = default;
IsolationData& IsolationData::operator=(IsolationData&&) = default;

base::Value IsolationData::AsDebugValue() const {
  auto debug_dict =
      base::DictValue()
          .Set("isolated_web_app_location", location_.ToDebugValue())
          .Set("version", version_.GetString())
          .Set("controlled_frame_partitions (on-disk)",
               base::ToValueList(controlled_frame_partitions_))
          .Set("pending_update_info", pending_update_info_
                                          ? pending_update_info_->AsDebugValue()
                                          : base::Value())
          .Set("integrity_block_data",
               integrity_block_data_ ? integrity_block_data_->AsDebugValue()
                                     : base::Value())
          .Set("opened_tabs_counter_notification_state",
               opened_tabs_counter_notification_state_
                   ? base::Value(
                         opened_tabs_counter_notification_state_->GetState()
                             .DebugString())
                   : base::Value());
  if (update_manifest_url_) {
    debug_dict.Set("update_manifest_url", update_manifest_url_->spec());
  }
  if (update_channel_) {
    debug_dict.Set("update_channel", update_channel_->ToString());
  }

  return base::Value(std::move(debug_dict));
}

IsolationData::PendingUpdateInfo::PendingUpdateInfo(
    IsolatedWebAppStorageLocation location,
    IwaVersion version,
    std::optional<IntegrityBlockData> integrity_block_data)
    : location(std::move(location)),
      version(std::move(version)),
      integrity_block_data(std::move(integrity_block_data)) {}

IsolationData::PendingUpdateInfo::~PendingUpdateInfo() = default;

IsolationData::PendingUpdateInfo::PendingUpdateInfo(const PendingUpdateInfo&) =
    default;
IsolationData::PendingUpdateInfo& IsolationData::PendingUpdateInfo::operator=(
    const PendingUpdateInfo&) = default;

base::Value IsolationData::PendingUpdateInfo::AsDebugValue() const {
  return base::Value(
      base::DictValue()
          .Set("isolated_web_app_location", location.ToDebugValue())
          .Set("version", version.GetString())
          .Set("integrity_block_data",
               integrity_block_data ? integrity_block_data->AsDebugValue()
                                    : base::Value()));
}

IsolationData::OpenedTabsCounterNotificationState::
    OpenedTabsCounterNotificationState(
        proto::IsolationData::OpenedTabsCounterNotificationState state)
    : proto_state_(std::move(state)) {
  CHECK(proto_state_.has_acknowledged());
  CHECK(proto_state_.has_times_shown());
}

IsolationData::OpenedTabsCounterNotificationState::
    OpenedTabsCounterNotificationState(bool acknowledged,
                                       uint32_t times_shown) {
  proto_state_.set_acknowledged(acknowledged);
  proto_state_.set_times_shown(times_shown);
}

const proto::IsolationData::OpenedTabsCounterNotificationState&
IsolationData::OpenedTabsCounterNotificationState::GetState() const {
  return proto_state_;
}

IsolationData::Builder::Builder(IsolatedWebAppStorageLocation location,
                                IwaVersion version)
    : location_(std::move(location)), version_(std::move(version)) {}

IsolationData::Builder::Builder(const IsolationData& isolation_data)
    : location_(isolation_data.location()),
      version_(isolation_data.version()),
      controlled_frame_partitions_(
          isolation_data.controlled_frame_partitions()),
      pending_update_info_(isolation_data.pending_update_info()),
      integrity_block_data_(isolation_data.integrity_block_data()),
      update_manifest_url_(isolation_data.update_manifest_url()),
      update_channel_(isolation_data.update_channel()) {}

IsolationData::Builder::~Builder() = default;

IsolationData::Builder::Builder(const IsolationData::Builder&) = default;
IsolationData::Builder& IsolationData::Builder::operator=(
    const IsolationData::Builder&) = default;
IsolationData::Builder::Builder(IsolationData::Builder&&) = default;
IsolationData::Builder& IsolationData::Builder::operator=(
    IsolationData::Builder&&) = default;

IsolationData::Builder& IsolationData::Builder::SetControlledFramePartitions(
    std::set<std::string> controlled_frame_partitions) & {
  controlled_frame_partitions_ = std::move(controlled_frame_partitions);
  return *this;
}

IsolationData::Builder&& IsolationData::Builder::SetControlledFramePartitions(
    std::set<std::string> controlled_frame_partitions) && {
  controlled_frame_partitions_ = std::move(controlled_frame_partitions);
  return std::move(*this);
}

IsolationData::Builder& IsolationData::Builder::SetPendingUpdateInfo(
    IsolationData::PendingUpdateInfo pending_update_info) & {
  CHECK_EQ(pending_update_info.location.dev_mode(), location_.dev_mode());
  pending_update_info_ = std::move(pending_update_info);
  return *this;
}

IsolationData::Builder&& IsolationData::Builder::SetPendingUpdateInfo(
    IsolationData::PendingUpdateInfo pending_update_info) && {
  CHECK_EQ(pending_update_info.location.dev_mode(), location_.dev_mode());
  pending_update_info_ = std::move(pending_update_info);
  return std::move(*this);
}

IsolationData::Builder&
IsolationData::Builder::SetOpenedTabsCounterNotificationState(
    IsolationData::OpenedTabsCounterNotificationState notification_state) & {
  opened_tabs_counter_notification_state_ = std::move(notification_state);
  return *this;
}

IsolationData::Builder&&
IsolationData::Builder::SetOpenedTabsCounterNotificationState(
    IsolationData::OpenedTabsCounterNotificationState notification_state) && {
  opened_tabs_counter_notification_state_ = std::move(notification_state);
  return std::move(*this);
}

IsolationData::Builder& IsolationData::Builder::ClearPendingUpdateInfo() & {
  pending_update_info_ = std::nullopt;
  return *this;
}

IsolationData::Builder&& IsolationData::Builder::ClearPendingUpdateInfo() && {
  pending_update_info_ = std::nullopt;
  return std::move(*this);
}

IsolationData::Builder& IsolationData::Builder::SetIntegrityBlockData(
    IntegrityBlockData integrity_block_data) & {
  integrity_block_data_ = std::move(integrity_block_data);
  return *this;
}

IsolationData::Builder&& IsolationData::Builder::SetIntegrityBlockData(
    IntegrityBlockData integrity_block_data) && {
  integrity_block_data_ = std::move(integrity_block_data);
  return std::move(*this);
}

IsolationData::Builder& IsolationData::Builder::SetUpdateManifestUrl(
    GURL update_manifest_url) & {
  CHECK(update_manifest_url.is_valid(), base::NotFatalUntil::M138);
  update_manifest_url_ = std::move(update_manifest_url);
  return *this;
}

IsolationData::Builder&& IsolationData::Builder::SetUpdateManifestUrl(
    GURL update_manifest_url) && {
  CHECK(update_manifest_url.is_valid(), base::NotFatalUntil::M138);
  update_manifest_url_ = std::move(update_manifest_url);
  return std::move(*this);
}

IsolationData::Builder& IsolationData::Builder::SetUpdateChannel(
    UpdateChannel update_channel) & {
  update_channel_ = std::move(update_channel);
  return *this;
}

IsolationData::Builder&& IsolationData::Builder::SetUpdateChannel(
    UpdateChannel update_channel) && {
  update_channel_ = std::move(update_channel);
  return std::move(*this);
}

IsolationData::Builder& IsolationData::Builder::PersistFieldsForUpdate(
    const IsolationData& isolation_data) & {
  PersistFieldsForUpdateImpl(*this, isolation_data);
  return *this;
}

IsolationData::Builder&& IsolationData::Builder::PersistFieldsForUpdate(
    const IsolationData& isolation_data) && {
  PersistFieldsForUpdateImpl(*this, isolation_data);
  return std::move(*this);
}

IsolationData IsolationData::Builder::Build() && {
  return IsolationData(
      std::move(location_), std::move(version_),
      std::move(controlled_frame_partitions_), std::move(pending_update_info_),
      std::move(integrity_block_data_), std::move(update_manifest_url_),
      std::move(update_channel_),
      std::move(opened_tabs_counter_notification_state_));
}

}  // namespace web_app
