// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
///////////////////////////////////////////////////////////////////////////////
#ifndef TINK_AEAD_KMS_AEAD_KEY_MANAGER_H_
#define TINK_AEAD_KMS_AEAD_KEY_MANAGER_H_

#include <algorithm>
#include <vector>

#include "absl/strings/string_view.h"
#include "tink/aead.h"
#include "tink/core/key_type_manager.h"
#include "tink/kms_clients.h"
#include "tink/util/constants.h"
#include "tink/util/status.h"
#include "tink/util/statusor.h"
#include "tink/util/validation.h"
#include "proto/kms_aead.pb.h"

namespace crypto {
namespace tink {

class KmsAeadKeyManager
    : public KeyTypeManager<google::crypto::tink::KmsAeadKey,
                            google::crypto::tink::KmsAeadKeyFormat,
                            List<Aead>> {
 public:
  class AeadFactory : public PrimitiveFactory<Aead> {
    crypto::tink::util::StatusOr<std::unique_ptr<Aead>> Create(
        const google::crypto::tink::KmsAeadKey& kms_aead_key) const override {
      const auto& key_uri = kms_aead_key.params().key_uri();
      auto kms_client_result = KmsClients::Get(key_uri);
      if (!kms_client_result.ok()) return kms_client_result.status();
      return kms_client_result.ValueOrDie()->GetAead(key_uri);
    }
  };

  KmsAeadKeyManager() : KeyTypeManager(absl::make_unique<AeadFactory>()) {}

  uint32_t get_version() const override { return 0; }

  google::crypto::tink::KeyData::KeyMaterialType key_material_type()
      const override {
    return google::crypto::tink::KeyData::REMOTE;
  }

  const std::string& get_key_type() const override { return key_type_; }

  crypto::tink::util::Status ValidateKey(
      const google::crypto::tink::KmsAeadKey& key) const override {
    crypto::tink::util::Status status =
        ValidateVersion(key.version(), get_version());
    if (!status.ok()) return status;
    return ValidateKeyFormat(key.params());
  }

  crypto::tink::util::Status ValidateKeyFormat(
      const google::crypto::tink::KmsAeadKeyFormat& key_format) const override {
    if (key_format.key_uri().empty()) {
      return crypto::tink::util::Status(
          crypto::tink::util::error::INVALID_ARGUMENT, "Missing key_uri.");
    }
    return util::OkStatus();
  }

  crypto::tink::util::StatusOr<google::crypto::tink::KmsAeadKey> CreateKey(
      const google::crypto::tink::KmsAeadKeyFormat& key_format) const override {
    google::crypto::tink::KmsAeadKey kms_aead_key;
    kms_aead_key.set_version(get_version());
    *(kms_aead_key.mutable_params()) = key_format;
    return kms_aead_key;
  }

 private:
  const std::string key_type_ = absl::StrCat(
      kTypeGoogleapisCom, google::crypto::tink::KmsAeadKey().GetTypeName());
};

}  // namespace tink
}  // namespace crypto

#endif  // TINK_AEAD_KMS_AEAD_KEY_MANAGER_H_
