// Copyright 2025 The Pigweed 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
//
//     https://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 "pw_rpc/internal/channel_list.h"

#include <array>
#include <cstddef>
#include <cstdint>

#include "pw_rpc/channel.h"
#include "pw_rpc/internal/lock.h"
#include "pw_rpc/internal/packet.h"
#include "pw_status/status.h"
#include "pw_unit_test/framework.h"

namespace pw::rpc::internal {
namespace {

constexpr uint32_t kChannelId = 1;
constexpr uint32_t kNonExistentChannelId = 2;
constexpr Packet kPacket;

struct TestChannelOutput final : public ChannelOutput {
  bool received_data = false;

  TestChannelOutput(const char* name) : ChannelOutput(name) {}
  Status Send(span<const std::byte>) override {
    received_data = true;
    return OkStatus();
  }
};

TEST(Get, DefaultCtorListReturnsNullptr) {
  ChannelList list;

  EXPECT_EQ(list.Get(kChannelId), nullptr);
}

TEST(Get, ReturnsNullptrForNonexistentChannel) {
  std::array<Channel, 1> channels = {Channel::Create<kChannelId>(nullptr)};
  ChannelList list(channels);

  EXPECT_EQ(list.Get(kNonExistentChannelId), nullptr);
}

// If `PW_RPC_DYNAMIC_ALLOCATION` is enabled, then a copy of the channels are
// created and so it's not possible to directly compare their addresses in
// memory to make sure they are the same object. Instead, what is important is
// that any data sent through these channels goes through the same channel
// output, so the channel is checked by sending a packet and asserting the right
// channel output processed it.
TEST(Get, ReturnsCorrectChannel) {
  TestChannelOutput test_channel_output("test");
  std::array<Channel, 1> channels = {
      Channel::Create<kChannelId>(&test_channel_output)};
  ChannelList list(channels);

  ChannelBase* channel = list.Get(kChannelId);
  ASSERT_NE(channel, nullptr);
  RpcLockGuard lock_guard;
  ASSERT_EQ(channel->Send(kPacket), OkStatus());

  EXPECT_TRUE(test_channel_output.received_data);
}

// If `PW_RPC_DYNAMIC_ALLOCATION` is enabled, then a copy of the channels are
// created and so it's not possible to directly compare their addresses in
// memory to make sure they are the same object. Instead, what is important is
// that any data sent through these channels goes through the same channel
// output, so the channel is checked by sending a packet and asserting the right
// channel output processed it.
TEST(ChannelList, DefaultOutputCorrectlyConfigured) {
  TestChannelOutput default_channel_output("default");
  std::array<Channel, 1> channels = {Channel::Create<kChannelId>(nullptr)};
  ChannelList list(channels);
  ASSERT_EQ(list.SetDefaultChannelOutput(default_channel_output), OkStatus());

  ChannelBase* channel = list.Get(kNonExistentChannelId);
  ASSERT_NE(channel, nullptr);
  RpcLockGuard lock_guard;
  ASSERT_EQ(channel->Send(kPacket), OkStatus());

  EXPECT_TRUE(default_channel_output.received_data);
}

TEST(ChannelList, SetDefaultChannelOutputFailsIfAlreadySet) {
  TestChannelOutput default_channel_output("default");
  std::array<Channel, 1> channels = {Channel::Create<kChannelId>(nullptr)};
  ChannelList list(channels);
  ASSERT_EQ(list.SetDefaultChannelOutput(default_channel_output), OkStatus());

  EXPECT_EQ(list.SetDefaultChannelOutput(default_channel_output),
            Status::AlreadyExists());
}

}  // namespace
}  // namespace pw::rpc::internal
