//
// Copyright 2021 gRPC authors.
//
// 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.
//

#include <grpc/support/port_platform.h>

#include "src/core/ext/xds/xds_http_filters.h"

#include "envoy/extensions/filters/http/router/v3/router.upb.h"
#include "envoy/extensions/filters/http/router/v3/router.upbdefs.h"

#include "src/core/ext/xds/xds_http_fault_filter.h"
#include "src/core/ext/xds/xds_http_rbac_filter.h"

namespace grpc_core {

const char* kXdsHttpRouterFilterConfigName =
    "envoy.extensions.filters.http.router.v3.Router";

namespace {

class XdsHttpRouterFilter : public XdsHttpFilterImpl {
 public:
  void PopulateSymtab(upb_symtab* symtab) const override {
    envoy_extensions_filters_http_router_v3_Router_getmsgdef(symtab);
  }

  absl::StatusOr<FilterConfig> GenerateFilterConfig(
      upb_strview serialized_filter_config, upb_arena* arena) const override {
    if (envoy_extensions_filters_http_router_v3_Router_parse(
            serialized_filter_config.data, serialized_filter_config.size,
            arena) == nullptr) {
      return absl::InvalidArgumentError("could not parse router filter config");
    }
    return FilterConfig{kXdsHttpRouterFilterConfigName, Json()};
  }

  absl::StatusOr<FilterConfig> GenerateFilterConfigOverride(
      upb_strview /*serialized_filter_config*/,
      upb_arena* /*arena*/) const override {
    return absl::InvalidArgumentError(
        "router filter does not support config override");
  }

  const grpc_channel_filter* channel_filter() const override { return nullptr; }

  // No-op.  This will never be called, since channel_filter() returns null.
  absl::StatusOr<ServiceConfigJsonEntry> GenerateServiceConfig(
      const FilterConfig& /*hcm_filter_config*/,
      const FilterConfig* /*filter_config_override*/) const override {
    return absl::UnimplementedError("router filter should never be called");
  }

  bool IsSupportedOnClients() const override { return true; }

  bool IsSupportedOnServers() const override { return true; }

  bool IsTerminalFilter() const override { return true; }
};

using FilterOwnerList = std::vector<std::unique_ptr<XdsHttpFilterImpl>>;
using FilterRegistryMap = std::map<absl::string_view, XdsHttpFilterImpl*>;

FilterOwnerList* g_filters = nullptr;
FilterRegistryMap* g_filter_registry = nullptr;

}  // namespace

void XdsHttpFilterRegistry::RegisterFilter(
    std::unique_ptr<XdsHttpFilterImpl> filter,
    const std::set<absl::string_view>& config_proto_type_names) {
  for (auto config_proto_type_name : config_proto_type_names) {
    (*g_filter_registry)[config_proto_type_name] = filter.get();
  }
  g_filters->push_back(std::move(filter));
}

const XdsHttpFilterImpl* XdsHttpFilterRegistry::GetFilterForType(
    absl::string_view proto_type_name) {
  auto it = g_filter_registry->find(proto_type_name);
  if (it == g_filter_registry->end()) return nullptr;
  return it->second;
}

void XdsHttpFilterRegistry::PopulateSymtab(upb_symtab* symtab) {
  for (const auto& filter : *g_filters) {
    filter->PopulateSymtab(symtab);
  }
}

void XdsHttpFilterRegistry::Init() {
  g_filters = new FilterOwnerList;
  g_filter_registry = new FilterRegistryMap;
  RegisterFilter(absl::make_unique<XdsHttpRouterFilter>(),
                 {kXdsHttpRouterFilterConfigName});
  RegisterFilter(absl::make_unique<XdsHttpFaultFilter>(),
                 {kXdsHttpFaultFilterConfigName});
  RegisterFilter(absl::make_unique<XdsHttpRbacFilter>(),
                 {kXdsHttpRbacFilterConfigName});
  RegisterFilter(absl::make_unique<XdsHttpRbacFilter>(),
                 {kXdsHttpRbacFilterConfigOverrideName});
}

void XdsHttpFilterRegistry::Shutdown() {
  delete g_filter_registry;
  delete g_filters;
}

}  // namespace grpc_core
