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

// Test support library for response payloads.

#include "chrome/browser/policy/messaging_layer/util/test_response_payload.h"

#include "base/base64.h"
#include "base/strings/strcat.h"
#include "base/values.h"

#include "chrome/browser/policy/messaging_layer/util/reporting_server_connector.h"
#include "components/reporting/util/encrypted_reporting_json_keys.h"
#include "components/reporting/util/status.h"
#include "components/reporting/util/statusor.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace reporting {
ResponseBuilder::ResponseBuilder(const base::DictValue& request)
    : request_(request.Clone()) {}

ResponseBuilder::ResponseBuilder(base::DictValue&& request)
    : request_(std::move(request)) {}

ResponseBuilder::ResponseBuilder(const ResponseBuilder& other)
    : request_(other.request_.Clone()), params_(other.params_) {}

ResponseBuilder& ResponseBuilder::SetForceConfirm(bool force_confirm) {
  params_.force_confirm = force_confirm;
  return *this;
}

ResponseBuilder& ResponseBuilder::SetNull(bool null) {
  params_.null = null;
  return *this;
}

ResponseBuilder& ResponseBuilder::SetRequest(const base::DictValue& request) {
  request_ = request.Clone();
  return *this;
}

ResponseBuilder& ResponseBuilder::SetRequest(base::DictValue&& request) {
  request_ = std::move(request);
  return *this;
}

ResponseBuilder& ResponseBuilder::SetSuccess(bool success) {
  params_.success = success;
  return *this;
}

StatusOr<base::DictValue> ResponseBuilder::Build() const {
  if (params_.null) {
    return base::unexpected(
        Status(error::FAILED_PRECONDITION, "No parameters set"));
  }

  base::DictValue response;

  // Attach sequenceInformation.
  if (const base::ListValue* const encrypted_record_list =
          request_.FindList(json_keys::kEncryptedRecordList);
      encrypted_record_list != nullptr) {
    EXPECT_FALSE(encrypted_record_list->empty());

    // Retrieve and process sequence information. The last record is the last
    // successfully uploaded record if the response is successful, or the first
    // failed record if the response is failure.
    const auto seq_info_it = std::prev(encrypted_record_list->cend());
    const auto* const seq_info =
        seq_info_it->GetDict().FindDict(json_keys::kSequenceInformation);
    EXPECT_NE(seq_info, nullptr);
    if (params_.success) {
      response.Set(json_keys::kLastSucceedUploadedRecord, seq_info->Clone());
    } else {
      response.Set(json_keys::kFirstFailedUploadedRecord,
                   base::DictValue().Set(json_keys::kFailedUploadedRecord,
                                         seq_info->Clone()));
      response.Set(json_keys::kFirstFailedUploadedRecord,
                   base::DictValue().Set(
                       json_keys::kFailureStatus,
                       base::DictValue().Set(json_keys::kErrorCode, 12345)));
      response.Set(json_keys::kFirstFailedUploadedRecord,
                   base::DictValue().Set(
                       json_keys::kFailureStatus,
                       base::DictValue().Set(json_keys::kErrorCode,
                                             "You've got a fake error.")));
      const auto* const last_success_seq_info =
          std::prev(seq_info_it)
              ->GetDict()
              .FindDict(json_keys::kSequenceInformation);
      EXPECT_NE(last_success_seq_info, nullptr);
      response.Set(json_keys::kLastSucceedUploadedRecord,
                   last_success_seq_info->Clone());
    }
  }

  // If forceConfirm confirm is expected, set it.
  if (params_.force_confirm) {
    response.Set(json_keys::kForceConfirm, true);
  }

  // If attach_encryption_settings is true, process that.
  const auto attach_encryption_settings =
      request_.FindBool(json_keys::kAttachEncryptionSettings);
  if (attach_encryption_settings.has_value() &&
      attach_encryption_settings.value()) {
    base::DictValue encryption_settings;
    std::string encoded = base::Base64Encode("PUBLIC KEY");
    encryption_settings.Set(json_keys::kPublicKey, std::move(encoded));
    encryption_settings.Set(json_keys::kPublicKeyId, 12345);
    encryption_settings.Set(json_keys::kPublicKeySignature,
                            base::Base64Encode("PUBLIC KEY SIG"));
    response.Set(json_keys::kEncryptionSettings,
                 std::move(encryption_settings));
  }

  // If configurationFileVersion is provided, attach the configuration file.
  const auto configuration_file_version =
      request_.FindInt(json_keys::kConfigurationFileVersion);
  if (configuration_file_version.has_value()) {
    base::DictValue configuration_file;
    base::ListValue event_configs;
    base::DictValue heartbeat;
    heartbeat.Set(json_keys::kConfigurationFileDestination, "HEARTBEAT_EVENTS");
    heartbeat.Set(json_keys::kConfigurationFileMinimumReleaseVersion, 11111);
    event_configs.Append(std::move(heartbeat));
    base::DictValue login;
    login.Set(json_keys::kConfigurationFileDestination, "LOGIN_LOGOUT_EVENTS");
    login.Set(json_keys::kConfigurationFileMinimumReleaseVersion, 22222);
    login.Set(json_keys::kConfigurationFileMaximumReleaseVersion, 33333);
    event_configs.Append(std::move(login));
    base::DictValue lock;
    lock.Set(json_keys::kConfigurationFileDestination, "LOCK_UNLOCK_EVENTS");
    event_configs.Append(std::move(lock));
    std::string encoded = base::Base64Encode("Fake signature");
    configuration_file.Set(json_keys::kConfigurationFileSignature,
                           base::Base64Encode("Fake signature"));
    configuration_file.Set(json_keys::kBlockedEventConfigs,
                           std::move(event_configs));
    configuration_file.Set(json_keys::kConfigurationFileVersionResponse,
                           123456);
    response.Set(json_keys::kConfigurationFile, std::move(configuration_file));
  }

  return response;
}

MakeUploadEncryptedReportAction::MakeUploadEncryptedReportAction(
    ResponseBuilder&& response_builder)
    : response_builder_(std::move(response_builder)) {}

void MakeUploadEncryptedReportAction::operator()(
    base::DictValue request,
    std::optional<base::DictValue> context,
    ReportingServerConnector::ResponseCallback callback) {
  response_builder_.SetRequest(std::move(request));
  auto response_result = response_builder_.Build();
  if (!response_result.has_value()) {
    std::move(callback).Run(base::unexpected(response_result.error()));
    return;
  }
  UploadResponseParser response_parser(
      EncryptedReportingClient::GenerationGuidIsRequired(),
      std::move(response_result.value()));
  std::move(callback).Run(std::move(response_parser));
}

}  // namespace reporting
