// Safe-delete in DUPSORT: two-cursor pattern
// Раздел учебника: Том II, глава 6, «Курсоры», раздел «Safe-delete в DUPSORT» (§6.7).
// Что демонстрирует: паттерн безопасного удаления значений в DUPSORT-таблице
//   двумя курсорами — один итерирует (MDBX_NEXT_DUP), второй удаляет текущую пару.
//   После cursor_del позиция курсора недействительна, поэтому итерация и удаление
//   разнесены на разные курсоры.
// Сборка: g++ -std=c++17 -I<libmdbx-include> -I../common 06-dupsort-delete.c++ -lmdbx
// Запуск: ./06-dupsort-delete
// Ожидаемый вывод:
//   values before delete (target): v1 v2 v3 v4
//   deleted 3 values of "target"
//   values after delete (target): v1
//   ok: safe-delete with two cursors
// При сбое — сообщение в stderr и exit 1.
//
// SPDX-License-Identifier: Apache-2.0

#include <iostream>
#include <string>

#include "common.h++"
#include <mdbx.h++>

static void dump_key(const mdbx::txn_managed &txn, const mdbx::map_handle &multi, const std::string &label,
                     const mdbx::slice &key) {
  std::string line = label;
  auto cur = txn.open_cursor(multi);
  for (auto r = cur.to_key_exact(key, false); r; r = cur.to_current_next_multi(false)) {
    line += r.value.as_string() + " ";
  }
  std::cout << line << "\n";
}

int main() {
  try {
    const std::string path = std::string(example::tmpdir()) + "/mdbx-06-dupsort-delete.mdbx";
    mdbx::env::remove(path);
    mdbx::env_managed env = example::env_open(path);

    {
      auto txn = env.start_write();
      auto multi = txn.create_map("multi", mdbx::key_mode::usual, mdbx::value_mode::multi);
      for (const char *k : {"alpha", "target", "omega"})
        for (const char *v : {"v1", "v2", "v3", "v4"})
          // UPSERT добавляет значение к ключу
          txn.upsert(multi, mdbx::slice(k), mdbx::slice(v));
      txn.commit();
    }

    auto txn = env.start_write();
    auto multi = txn.open_map("multi", mdbx::key_mode::usual, mdbx::value_mode::multi);
    dump_key(txn, multi, "values before delete (target):", mdbx::slice("target"));

    // Паттерн «два курсора»: `it` итерирует по дубликатам ключа,
    // `del` удаляет текущую пару (позиция `it` при этом остаётся валидной).
    const mdbx::slice target("target");
    auto it = txn.open_cursor(multi);
    auto del = txn.open_cursor(multi);
    size_t deleted = 0;
    // Первый dup ключа сохраняем, остальные удаляем.
    auto pos = it.to_key_exact(target);
    if (pos)
      pos = it.to_current_next_multi(false);
    for (; pos; pos = it.to_current_next_multi(false)) {
      // MDBX_GET_BOTH
      del.to_exact_key_value_equal(pos.key, pos.value, false);
      // MDBX_CURRENT: удалить только текущее значение
      if (del.erase(false))
        ++deleted;
    }
    std::cout << "deleted " << deleted << " values of \"target\"\n";

    dump_key(txn, multi, "values after delete (target):", target);
    txn.commit();

    std::cout << "ok: safe-delete with two cursors\n";
    return EXIT_SUCCESS;
  } catch (const std::exception &ex) {
    std::cerr << "error: " << ex.what() << "\n";
    return EXIT_FAILURE;
  }
}