// CRUD: put/get/del with write flags
// Раздел учебника: Том I, глава 4, «Базовые операции CRUD»,
//   разделы «put/get/del/replace», «Флаги записи».
// Что демонстрирует: NOOVERWRITE (insert_unique) и MDBX_KEYEXIST как ожидаемый код;
//   UPSERT; CURRENT (update); удаление; MDBX_NOTFOUND как ожидаемый код.
// Сборка: g++ -std=c++17 -I<libmdbx-include> -I../common 03-crud.c++ -lmdbx
// Запуск: ./03-crud
// Ожидаемый вывод:
//   insert duplicate -> MDBX_KEYEXIST as expected
//   try_insert duplicate -> done=0
//   after upsert: value2
//   update existing: value3
//   update absent -> MDBX_NOTFOUND as expected
//   erase(key) -> removed=true
//   get(after erase) -> MDBX_NOTFOUND as expected
//   ok: NOOVERWRITE/UPSERT/CURRENT; KEYEXIST/NOTFOUND handled explicitly
// При сбое — сообщение в stderr и exit 1.
//
// SPDX-License-Identifier: Apache-2.0

#include <iostream>
#include <string>

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

int main() {
  try {
    const std::string path = std::string(example::tmpdir()) + "/mdbx-03-crud.mdbx";
    mdbx::env::remove(path);

    mdbx::env_managed env(path, mdbx::env_managed::create_parameters(), mdbx::env::operate_parameters());
    auto txn = env.start_write();
    auto table = txn.open_map(nullptr);

    // NOOVERWRITE: вставка уникального ключа.
    txn.insert(table, mdbx::slice("key"), mdbx::slice("value1"));

    // Повторная вставка того же ключа -> key_exists (MDBX_KEYEXIST).
    try {
      txn.insert(table, mdbx::slice("key"), mdbx::slice("value2"));
      std::cerr << "FAIL: duplicate insert did not throw\n";
      return EXIT_FAILURE;
    } catch (const mdbx::key_exists &) {
      std::cout << "insert duplicate -> MDBX_KEYEXIST as expected\n";
    }

    // try_insert не бросает исключение, а сообщает результат флагом done.
    auto ins = txn.try_insert(table, mdbx::slice("key"), mdbx::slice("value2"));
    std::cout << "try_insert duplicate -> done=" << (ins.done ? 1 : 0) << "\n";

    // UPSERT: вставить или перезаписать.
    txn.upsert(table, mdbx::slice("key"), mdbx::slice("value2"));
    std::cout << "after upsert: " << txn.get(table, mdbx::slice("key")).as_string() << "\n";

    // CURRENT (update): обновить только существующий ключ.
    txn.update(table, mdbx::slice("key"), mdbx::slice("value3"));
    std::cout << "update existing: " << txn.get(table, mdbx::slice("key")).as_string() << "\n";
    try {
      txn.update(table, mdbx::slice("absent"), mdbx::slice("x"));
      std::cerr << "FAIL: update of absent key did not throw\n";
      return EXIT_FAILURE;
    } catch (const mdbx::not_found &) {
      std::cout << "update absent -> MDBX_NOTFOUND as expected\n";
    }

    // Удаление по ключу.
    const bool removed = txn.erase(table, mdbx::slice("key"));
    std::cout << "erase(key) -> removed=" << (removed ? "true" : "false") << "\n";
    try {
      (void)txn.get(table, mdbx::slice("key"));
      std::cerr << "FAIL: get after erase did not throw\n";
      return EXIT_FAILURE;
    } catch (const mdbx::not_found &) {
      std::cout << "get(after erase) -> MDBX_NOTFOUND as expected\n";
    }

    txn.commit();

    std::cout << "ok: NOOVERWRITE/UPSERT/CURRENT; KEYEXIST/NOTFOUND handled explicitly\n";
    return EXIT_SUCCESS;
  } catch (const std::exception &ex) {
    std::cerr << "error: " << ex.what() << "\n";
    return EXIT_FAILURE;
  }
}