// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "services/network/devtools_durable_msg_collector_manager.h"

#include "base/metrics/histogram_functions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "base/trace_event/memory_dump_manager.h"
#include "base/trace_event/process_memory_dump.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "services/network/public/cpp/features.h"

namespace network {

namespace {
// The name of the dump provider.
constexpr const char* kDumpProviderName =
    "DevtoolsDurableMessageCollectorManager";
}  // namespace

DevtoolsDurableMessageCollectorManager::DevtoolsDurableMessageCollectorManager()
    : max_global_buffer_size_(static_cast<uint64_t>(
          network::features::kDurableMessagesGlobalBufferSize.Get())) {
  base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
      this, kDumpProviderName,
      base::SingleThreadTaskRunner::GetCurrentDefault());
}

DevtoolsDurableMessageCollectorManager::
    ~DevtoolsDurableMessageCollectorManager() {
  base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
      this);
}

void DevtoolsDurableMessageCollectorManager::AddCollector(
    mojo::PendingReceiver<network::mojom::DurableMessageCollector> receiver) {
  mojo::MakeSelfOwnedReceiver(std::make_unique<DevtoolsDurableMessageCollector>(
                                  weak_factory_.GetWeakPtr()),
                              std::move(receiver));
}

void DevtoolsDurableMessageCollectorManager::OnCollectorCreated(
    DevtoolsDurableMessageCollector* collector) {
  collectors_.insert(collector);
}

void DevtoolsDurableMessageCollectorManager::OnCollectorDestroyed(
    DevtoolsDurableMessageCollector* collector) {
  // An O(n) scan is fine here, because the number of collectors is
  // expected to be small.
  for (auto it = profile_collectors_.begin();
       it != profile_collectors_.end();) {
    if (it->second == collector) {
      it = profile_collectors_.erase(it);
    } else {
      ++it;
    }
  }
  collectors_.erase(collector);
}

bool DevtoolsDurableMessageCollectorManager::CanAccommodate(size_t size) const {
  if (max_global_buffer_size_ == 0) {
    // No global limit set, so we can accommodate any size.
    return true;
  }
  if (total_memory_usage_ >= max_global_buffer_size_) {
    return false;
  }
  return size <= max_global_buffer_size_ - total_memory_usage_;
}

void DevtoolsDurableMessageCollectorManager::OnCollectorAddedBytes(
    size_t size) {
  total_memory_usage_ += size;
}

void DevtoolsDurableMessageCollectorManager::OnCollectorRemovedBytes(
    size_t size) {
  DCHECK_GE(total_memory_usage_, size);
  total_memory_usage_ -= size;
}

void DevtoolsDurableMessageCollectorManager::OnCollectorAddedMessage(
    size_t count) {
  total_message_count_ += count;
}

void DevtoolsDurableMessageCollectorManager::OnCollectorRemovedMessage(
    size_t count) {
  DCHECK_GE(total_message_count_, count);
  total_message_count_ -= count;
}

bool DevtoolsDurableMessageCollectorManager::OnMemoryDump(
    const base::trace_event::MemoryDumpArgs& args,
    base::trace_event::ProcessMemoryDump* pmd) {
  base::trace_event::MemoryAllocatorDump* dump =
      pmd->CreateAllocatorDump("devtools/durable_message_collectors");
  dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
                  base::trace_event::MemoryAllocatorDump::kUnitsBytes,
                  total_memory_usage_);
  dump->AddScalar("message_count",
                  base::trace_event::MemoryAllocatorDump::kUnitsObjects,
                  total_message_count_);
  dump->AddScalar("collector_count",
                  base::trace_event::MemoryAllocatorDump::kUnitsObjects,
                  collectors_.size());
  return true;
}

std::vector<DevtoolsDurableMessageCollector*>
DevtoolsDurableMessageCollectorManager::GetCollectorsEnabledForProfile(
    const base::UnguessableToken& profile_id) {
  std::vector<DevtoolsDurableMessageCollector*> collectors;
  auto range = profile_collectors_.equal_range(profile_id);
  for (auto it = range.first; it != range.second; ++it) {
    collectors.push_back(it->second);
  }
  return collectors;
}

void DevtoolsDurableMessageCollectorManager::EnableForProfile(
    const base::UnguessableToken& profile_id,
    DevtoolsDurableMessageCollector& collector) {
  profile_collectors_.insert({profile_id, &collector});
}

void DevtoolsDurableMessageCollectorManager::DisableForProfile(
    const base::UnguessableToken& profile_id,
    DevtoolsDurableMessageCollector& collector) {
  auto range = profile_collectors_.equal_range(profile_id);
  for (auto it = range.first; it != range.second;) {
    if (it->second == &collector) {
      it = profile_collectors_.erase(it);
    } else {
      ++it;
    }
  }
}

}  // namespace network
