// Правила и контрпримеры: «забытый» читатель → MAP_FULL; несоответствие потоков
// Раздел учебника: Том V, глава 27, «Правила и советы», §27.1–§27.4.
// Что демонстрирует: (1) «забытый» read-снапшот удерживает страницы и при
//   переполнении БД даёт MDBX_MAP_FULL — диагностика через reader list и
//   разрешение после освобождения читателя; (2) использование транзакции из
//   другого потока — MDBX_THREAD_MISMATCH.
// Сборка: g++ -std=c++17 -I<libmdbx-include> -I../common 34-rules-counter.c++ -lmdbx -pthread
// Запуск: ./34-rules-counter
// Ожидаемый вывод:
//   MAP_FULL while reader holds snapshot; reader lag=<L>
//   after reader released, write succeeded
//   thread mismatch: MDBX_THREAD_MISMATCH as expected
//   ok: counterexamples reproduced and diagnosed
// При сбое — сообщение в stderr и exit 1.
//
// SPDX-License-Identifier: Apache-2.0

#include <atomic>
#include <iostream>
#include <string>
#include <thread>

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

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

    // Маленькая геометрия, чтобы быстрее упереться в MDBX_MAP_FULL.
    mdbx::env::geometry geo;
    geo.size_lower = 256 * 1024;
    geo.size_now = 256 * 1024;
    geo.size_upper = 256 * 1024;
    mdbx::env_managed env(path, mdbx::env_managed::create_parameters().set_geometry(geo),
                          mdbx::env::operate_parameters());

    // Контрпример 1: «забытый» читатель удерживает снапшот.
    // Сначала небольшое наполнение, чтобы читателю было что держать.
    {
      auto txn = env.start_write();
      auto table = txn.open_map(nullptr);
      txn.insert(table, mdbx::slice("k0"), mdbx::slice("seed"));
      txn.commit();
    }
    std::atomic<bool> reader_ready{false};
    std::atomic<bool> release_reader{false};
    std::thread holder([&] {
      auto rtxn = env.start_read();
      auto table = rtxn.open_map(nullptr);
      (void)rtxn.get(table, mdbx::slice("k0"));
      reader_ready = true;
      while (!release_reader.load())
        std::this_thread::yield();
      rtxn.abort();
    });
    while (!reader_ready.load())
      std::this_thread::yield();

    // Писатель заполняет БД — при переполнении получаем MAP_FULL.
    size_t written = 0;
    try {
      auto txn = env.start_write();
      auto table = txn.open_map(nullptr);
      for (;;) {
        // не пересекается с "k0"
        const auto key = "w" + std::to_string(written);
        const std::string value(1024, 'x');
        txn.insert(table, mdbx::slice(key), mdbx::slice(value));
        ++written;
      }
    } catch (const mdbx::db_full &) {
      // Диагностика: показываем читателей и их отставание.
      struct visitor {
        int operator()(const mdbx::env::reader_info &ri, int) {
          std::cout << "MAP_FULL while reader holds snapshot; reader lag=" << ri.transaction_lag << "\n";
          return mdbx::continue_loop;
        }
      } v;
      env.enumerate_readers(v);
    }

    // Освобождаем читателя — писатель снова может работать.
    release_reader = true;
    holder.join();
    {
      auto txn = env.start_write();
      auto table = txn.open_map(nullptr);
      txn.insert(table, mdbx::slice("after-release"), mdbx::slice("ok"));
      txn.commit();
    }
    std::cout << "after reader released, write succeeded\n";

    // Контрпример 2: использование транзакции из другого потока.
    {
      auto rtxn = env.start_read();
      auto table = rtxn.open_map(nullptr);
      std::atomic<bool> mismatch_seen{false};
      std::thread foreign([&] {
        try {
          // чужой поток
          (void)rtxn.get(table, mdbx::slice("after-release"));
        } catch (const std::exception &ex) {
          // Ожидаемый отказ: THREAD_MISMATCH (или производный код ошибки).
          mismatch_seen = true;
          std::cout << "thread misuse rejected: " << ex.what() << "\n";
        }
      });
      foreign.join();
      rtxn.abort();
      if (!mismatch_seen) {
        std::cerr << "FAIL: thread mismatch not detected\n";
        return EXIT_FAILURE;
      }
      std::cout << "thread mismatch: MDBX_THREAD_MISMATCH as expected\n";
    }

    std::cout << "ok: counterexamples reproduced and diagnosed\n";
    return EXIT_SUCCESS;
  } catch (const std::exception &ex) {
    std::cerr << "error: " << ex.what() << "\n";
    return EXIT_FAILURE;
  }
}