#include <benchmark/benchmark.h>

#include "esphome/components/api/api_pb2.h"
#include "esphome/components/api/api_buffer.h"

namespace esphome::api::benchmarks {

// Inner iteration count to amortize CodSpeed instrumentation overhead.
// Without this, the ~60ns per-iteration valgrind start/stop cost dominates
// sub-microsecond benchmarks.
static constexpr int kInnerIterations = 2000;

// --- SensorStateResponse (highest frequency message) ---

static void Encode_SensorStateResponse(benchmark::State &state) {
  APIBuffer buffer;
  SensorStateResponse msg;
  msg.key = 0x12345678;
  msg.state = 23.5f;
  msg.missing_state = false;
  uint32_t size = msg.calculate_size();
  buffer.resize(size);

  for (auto _ : state) {
    for (int i = 0; i < kInnerIterations; i++) {
      ProtoWriteBuffer writer(&buffer, 0);
      msg.encode(writer);
    }
    benchmark::DoNotOptimize(buffer.data());
  }
  state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(Encode_SensorStateResponse);

static void CalculateSize_SensorStateResponse(benchmark::State &state) {
  SensorStateResponse msg;
  msg.key = 0x12345678;
  msg.state = 23.5f;
  msg.missing_state = false;

  for (auto _ : state) {
    uint32_t result = 0;
    for (int i = 0; i < kInnerIterations; i++) {
      result += msg.calculate_size();
    }
    benchmark::DoNotOptimize(result);
  }
  state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(CalculateSize_SensorStateResponse);

// Steady state: buffer already allocated from previous iteration
static void CalcAndEncode_SensorStateResponse(benchmark::State &state) {
  APIBuffer buffer;
  SensorStateResponse msg;
  msg.key = 0x12345678;
  msg.state = 23.5f;
  msg.missing_state = false;

  for (auto _ : state) {
    for (int i = 0; i < kInnerIterations; i++) {
      uint32_t size = msg.calculate_size();
      buffer.resize(size);
      ProtoWriteBuffer writer(&buffer, 0);
      msg.encode(writer);
    }
    benchmark::DoNotOptimize(buffer.data());
  }
  state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(CalcAndEncode_SensorStateResponse);

// Cold path: fresh buffer each iteration (measures heap allocation cost).
// Inner loop still needed to amortize CodSpeed instrumentation overhead.
// Each inner iteration creates a fresh buffer, so this measures
// alloc+calc+encode per item.
static void CalcAndEncode_SensorStateResponse_Fresh(benchmark::State &state) {
  SensorStateResponse msg;
  msg.key = 0x12345678;
  msg.state = 23.5f;
  msg.missing_state = false;

  for (auto _ : state) {
    for (int i = 0; i < kInnerIterations; i++) {
      APIBuffer buffer;
      uint32_t size = msg.calculate_size();
      buffer.resize(size);
      ProtoWriteBuffer writer(&buffer, 0);
      msg.encode(writer);
      benchmark::DoNotOptimize(buffer.data());
    }
  }
  state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(CalcAndEncode_SensorStateResponse_Fresh);

// --- BinarySensorStateResponse ---

static void Encode_BinarySensorStateResponse(benchmark::State &state) {
  APIBuffer buffer;
  BinarySensorStateResponse msg;
  msg.key = 0xAABBCCDD;
  msg.state = true;
  msg.missing_state = false;
  uint32_t size = msg.calculate_size();
  buffer.resize(size);

  for (auto _ : state) {
    for (int i = 0; i < kInnerIterations; i++) {
      ProtoWriteBuffer writer(&buffer, 0);
      msg.encode(writer);
    }
    benchmark::DoNotOptimize(buffer.data());
  }
  state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(Encode_BinarySensorStateResponse);

// --- HelloResponse (string fields) ---

static void Encode_HelloResponse(benchmark::State &state) {
  APIBuffer buffer;
  HelloResponse msg;
  msg.api_version_major = 1;
  msg.api_version_minor = 10;
  msg.server_info = StringRef::from_lit("esphome v2026.3.0");
  msg.name = StringRef::from_lit("living-room-sensor");
  uint32_t size = msg.calculate_size();
  buffer.resize(size);

  for (auto _ : state) {
    for (int i = 0; i < kInnerIterations; i++) {
      ProtoWriteBuffer writer(&buffer, 0);
      msg.encode(writer);
    }
    benchmark::DoNotOptimize(buffer.data());
  }
  state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(Encode_HelloResponse);

// --- LightStateResponse (complex multi-field message) ---

static void Encode_LightStateResponse(benchmark::State &state) {
  APIBuffer buffer;
  LightStateResponse msg;
  msg.key = 0x11223344;
  msg.state = true;
  msg.brightness = 0.8f;
  msg.color_mode = enums::COLOR_MODE_RGB_WHITE;
  msg.color_brightness = 1.0f;
  msg.red = 1.0f;
  msg.green = 0.5f;
  msg.blue = 0.2f;
  msg.white = 0.0f;
  msg.color_temperature = 4000.0f;
  msg.cold_white = 0.0f;
  msg.warm_white = 0.0f;
  msg.effect = StringRef::from_lit("rainbow");
  uint32_t size = msg.calculate_size();
  buffer.resize(size);

  for (auto _ : state) {
    for (int i = 0; i < kInnerIterations; i++) {
      ProtoWriteBuffer writer(&buffer, 0);
      msg.encode(writer);
    }
    benchmark::DoNotOptimize(buffer.data());
  }
  state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(Encode_LightStateResponse);

static void CalculateSize_LightStateResponse(benchmark::State &state) {
  LightStateResponse msg;
  msg.key = 0x11223344;
  msg.state = true;
  msg.brightness = 0.8f;
  msg.color_mode = enums::COLOR_MODE_RGB_WHITE;
  msg.color_brightness = 1.0f;
  msg.red = 1.0f;
  msg.green = 0.5f;
  msg.blue = 0.2f;
  msg.white = 0.0f;
  msg.color_temperature = 4000.0f;
  msg.cold_white = 0.0f;
  msg.warm_white = 0.0f;
  msg.effect = StringRef::from_lit("rainbow");

  for (auto _ : state) {
    uint32_t result = 0;
    for (int i = 0; i < kInnerIterations; i++) {
      result += msg.calculate_size();
    }
    benchmark::DoNotOptimize(result);
  }
  state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(CalculateSize_LightStateResponse);

// --- DeviceInfoResponse (nested submessages: 20 devices + 20 areas) ---

static DeviceInfoResponse make_device_info_response() {
  DeviceInfoResponse msg;
  msg.name = StringRef::from_lit("living-room-sensor");
  msg.mac_address = StringRef::from_lit("AA:BB:CC:DD:EE:FF");
  msg.esphome_version = StringRef::from_lit("2026.3.0");
  msg.compilation_time = StringRef::from_lit("Mar 16 2026, 12:00:00");
  msg.model = StringRef::from_lit("esp32-poe-iso");
  msg.manufacturer = StringRef::from_lit("Olimex");
  msg.friendly_name = StringRef::from_lit("Living Room Sensor");
#ifdef USE_DEVICES
  for (uint32_t i = 0; i < ESPHOME_DEVICE_COUNT && i < 20; i++) {
    msg.devices[i].device_id = i + 1;
    msg.devices[i].name = StringRef::from_lit("device");
    msg.devices[i].area_id = (i % 20) + 1;
  }
#endif
#ifdef USE_AREAS
  for (uint32_t i = 0; i < ESPHOME_AREA_COUNT && i < 20; i++) {
    msg.areas[i].area_id = i + 1;
    msg.areas[i].name = StringRef::from_lit("area");
  }
#endif
  return msg;
}

static void CalculateSize_DeviceInfoResponse(benchmark::State &state) {
  auto msg = make_device_info_response();

  for (auto _ : state) {
    uint32_t result = 0;
    for (int i = 0; i < kInnerIterations; i++) {
      result += msg.calculate_size();
    }
    benchmark::DoNotOptimize(result);
  }
  state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(CalculateSize_DeviceInfoResponse);

static void Encode_DeviceInfoResponse(benchmark::State &state) {
  auto msg = make_device_info_response();
  APIBuffer buffer;
  uint32_t total_size = msg.calculate_size();
  buffer.resize(total_size);

  for (auto _ : state) {
    for (int i = 0; i < kInnerIterations; i++) {
      ProtoWriteBuffer writer(&buffer, 0);
      msg.encode(writer);
    }
    benchmark::DoNotOptimize(buffer.data());
  }
  state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(Encode_DeviceInfoResponse);

// Steady state: buffer already allocated from previous iteration
static void CalcAndEncode_DeviceInfoResponse(benchmark::State &state) {
  auto msg = make_device_info_response();
  APIBuffer buffer;

  for (auto _ : state) {
    for (int i = 0; i < kInnerIterations; i++) {
      uint32_t size = msg.calculate_size();
      buffer.resize(size);
      ProtoWriteBuffer writer(&buffer, 0);
      msg.encode(writer);
    }
    benchmark::DoNotOptimize(buffer.data());
  }
  state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(CalcAndEncode_DeviceInfoResponse);

// Cold path: fresh buffer each iteration (measures heap allocation cost).
// Inner loop still needed to amortize CodSpeed instrumentation overhead.
// Each inner iteration creates a fresh buffer, so this measures
// alloc+calc+encode per item.
static void CalcAndEncode_DeviceInfoResponse_Fresh(benchmark::State &state) {
  auto msg = make_device_info_response();

  for (auto _ : state) {
    for (int i = 0; i < kInnerIterations; i++) {
      APIBuffer buffer;
      uint32_t size = msg.calculate_size();
      buffer.resize(size);
      ProtoWriteBuffer writer(&buffer, 0);
      msg.encode(writer);
      benchmark::DoNotOptimize(buffer.data());
    }
  }
  state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(CalcAndEncode_DeviceInfoResponse_Fresh);

// --- BluetoothLERawAdvertisementsResponse (12 adverts, highest-volume BLE message) ---

#ifdef USE_BLUETOOTH_PROXY

static BluetoothLERawAdvertisementsResponse make_ble_raw_advs_12() {
  static const uint8_t fake_adv_data[] = {
      0x02, 0x01, 0x06, 0x03, 0x03, 0x9F, 0xFE, 0x17, 0x16, 0x9F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  };
  BluetoothLERawAdvertisementsResponse msg;
  msg.advertisements_len = 12;
  for (int i = 0; i < 12; i++) {
    auto &adv = msg.advertisements[i];
    adv.address = 0xAABBCCDD0000ULL + i;
    adv.rssi = -60 - i;
    adv.address_type = 1;
    memcpy(adv.data, fake_adv_data, sizeof(fake_adv_data));
    adv.data_len = sizeof(fake_adv_data);
  }
  return msg;
}

static void CalculateSize_BLERawAdvs12(benchmark::State &state) {
  auto msg = make_ble_raw_advs_12();

  for (auto _ : state) {
    uint32_t result = 0;
    for (int i = 0; i < kInnerIterations; i++) {
      result += msg.calculate_size();
    }
    benchmark::DoNotOptimize(result);
  }
  state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(CalculateSize_BLERawAdvs12);

static void Encode_BLERawAdvs12(benchmark::State &state) {
  auto msg = make_ble_raw_advs_12();
  APIBuffer buffer;
  uint32_t total_size = msg.calculate_size();
  buffer.resize(total_size);

  for (auto _ : state) {
    for (int i = 0; i < kInnerIterations; i++) {
      ProtoWriteBuffer writer(&buffer, 0);
      msg.encode(writer);
    }
    benchmark::DoNotOptimize(buffer.data());
  }
  state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(Encode_BLERawAdvs12);

static void CalcAndEncode_BLERawAdvs12(benchmark::State &state) {
  auto msg = make_ble_raw_advs_12();
  APIBuffer buffer;

  for (auto _ : state) {
    for (int i = 0; i < kInnerIterations; i++) {
      uint32_t size = msg.calculate_size();
      buffer.resize(size);
      ProtoWriteBuffer writer(&buffer, 0);
      msg.encode(writer);
    }
    benchmark::DoNotOptimize(buffer.data());
  }
  state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(CalcAndEncode_BLERawAdvs12);

static void CalcAndEncode_BLERawAdvs12_Fresh(benchmark::State &state) {
  auto msg = make_ble_raw_advs_12();

  for (auto _ : state) {
    for (int i = 0; i < kInnerIterations; i++) {
      APIBuffer buffer;
      uint32_t size = msg.calculate_size();
      buffer.resize(size);
      ProtoWriteBuffer writer(&buffer, 0);
      msg.encode(writer);
      benchmark::DoNotOptimize(buffer.data());
    }
  }
  state.SetItemsProcessed(state.iterations() * kInnerIterations);
}
BENCHMARK(CalcAndEncode_BLERawAdvs12_Fresh);

#endif  // USE_BLUETOOTH_PROXY

}  // namespace esphome::api::benchmarks
