// Crash recovery: steady state after a crashed writer, open_for_recovery
// Textbook section: Volume III, chapter 20, "Durability and recovery", §20.4/§20.5.
// What it demonstrates: a writer crash (_exit in the middle of a write transaction) leaves
//   no traces — reopening shows the last committed state (steady recovery);
//   mdbx_env_open_for_recovery() opens the DB in recovery mode;
//   the mdbx_chk command for integrity checking.
// Build: g++ -std=c++17 -I<libmdbx-include> -I../common 27-recovery.c++ -lmdbx
// Run: ./27-recovery
// Expected output (POSIX):
//   committed: key=v1
//   crashed writer changes rolled back: key=v1, newkey absent
//   open_for_recovery: ok
//   ok: crash recovery and open_for_recovery
// On Windows the example does not run and reports a skip.
//
// SPDX-License-Identifier: Apache-2.0

#include <iostream>
#include <string>

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

#if defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS)
int main() {
  std::cout << "skipped: fork() is not available on Windows\n";
  return EXIT_SUCCESS;
}
#else
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

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

    // Commit the data and close the environment.
    {
      auto env = example::env_open(path);
      auto txn = env.start_write();
      auto table = txn.open_map(nullptr);
      txn.insert(table, mdbx::slice("key"), mdbx::slice("v1"));
      txn.commit();
      std::cout << "committed: key=v1" << std::endl;
    }

    // A "crashing" writer: makes changes and _exit() without commit/abort.
    const pid_t pid = fork();
    if (pid < 0) {
      std::cerr << "fork failed\n";
      return EXIT_FAILURE;
    }
    if (pid == 0) {
      auto env = example::env_open(path);
      auto txn = env.start_write();
      auto table = txn.open_map(nullptr);
      txn.upsert(table, mdbx::slice("key"), mdbx::slice("crash-value"));
      txn.insert(table, mdbx::slice("newkey"), mdbx::slice("x"));
      // Simulating a crash: no commit and no abort.
      _exit(EXIT_SUCCESS);
    }
    int status = 0;
    waitpid(pid, &status, 0);

    // Reopening: the uncommitted changes have been rolled back.
    {
      auto env = example::env_open(path);
      auto txn = env.start_read();
      auto table = txn.open_map(nullptr);
      const std::string value(txn.get(table, mdbx::slice("key")).as_string());
      std::cout << "crashed writer changes rolled back: key=" << value << ", newkey ";
      try {
        (void)txn.get(table, mdbx::slice("newkey"));
        std::cout << "present\n";
        return EXIT_FAILURE;
      } catch (const mdbx::not_found &) {
        std::cout << "absent\n";
      }
      txn.abort();
    }

    // Recovery mode: open a specific meta-page (0..2).
    {
      auto recovery = mdbx::env_managed::open_for_recovery(path.c_str(), 0 /* target_meta */, true);
      std::cout << "open_for_recovery: ok\n";
    }

    // Integrity check (CLI): mdbx_chk -w <path>
    std::cout << "hint: run 'mdbx_chk -w " << path << "' to verify integrity\n";

    std::cout << "ok: crash recovery and open_for_recovery\n";
    return EXIT_SUCCESS;
  } catch (const std::exception &ex) {
    std::cerr << "error: " << ex.what() << "\n";
    return EXIT_FAILURE;
  }
}
#endif // Windows vs POSIX