#pragma once

#include <eosio/asset.hpp>
#include <eosio/contract.hpp>
#include <eosio/crypto.hpp>
#include <eosio/eosio.hpp>
#include <eosio/multi_index.hpp>
#include <eosio/system.hpp>
#include <eosio/time.hpp>

#include "../lib/index.hpp"
#include "../lib/core/ledger2/accounts.hpp"
#include "../lib/core/ledger2/operations.hpp"
#include "../lib/core/ledger2/wallets.hpp"

using namespace eosio;

/**
\defgroup public_ledger2 Контракт LEDGER2
\ingroup public_contracts
\brief Двухконтурный учёт: управленческие кошельки + двойная бухгалтерия.

Контракт ledger2 (пересмотр Epic 1 addendum, 2026-04-18) реализует единый
интерфейс `apply(operation_code, ...)` как orchestrator: он рассылает 3
атомарных inline-action, связанных общим `process_hash`:

  1. `walletop` — issue/transfer/block/unblock на wallets2;
  2. `debit`   — +debit_balance на accounts2[Dr] + пересчёт сальдо;
  3. `credit`  — +credit_balance на accounts2[Cr] + пересчёт сальдо.

Каждая inline-action попадает в blockchain_actions отдельной записью с
полями (..., process_hash). Бэкенд собирает «тройку» простым SQL:
`account = 'ledger2' AND data->>'process_hash' = X`.

State-таблицы в RAM:
  • `accounts2` — счёт с account_type, debit_balance, credit_balance, balance;
  • `wallets2`  — кошелёк с available, blocked;
  • `meta`      — служебная (статус миграции).

История в RAM не хранится — она целиком восстанавливается на бэкенде из
blockchain_actions + blockchain_deltas.

Внешних add/sub/writeoff нет — всё движение средств описывается через
OPERATION_REGISTRY.
*/

/** \defgroup public_ledger2_actions Действия ledger2 \ingroup public_ledger2 */
/** \defgroup public_ledger2_tables  Таблицы ledger2  \ingroup public_ledger2 */
/** \defgroup public_ledger2_consts  Константы ledger2 \ingroup public_ledger2 */

/**
 * @ingroup public_contracts
 * @brief Контракт ledger2.
 * @details Единственное внешнее финансовое action — apply(). Миграция с
 *          ledger — одноразовое migrate(). Внешних add/sub/writeoff нет:
 *          всё движение средств описывается через OPERATION_REGISTRY.
 */
class [[eosio::contract(LEDGER2)]] ledger2 : public eosio::contract {
public:
  ledger2(eosio::name receiver, eosio::name code,
          eosio::datastream<const char*> ds)
      : eosio::contract(receiver, code, ds) {}

  /**
   * @brief Единая точка входа финансовых движений ledger2 (orchestrator).
   *
   * Не пишет в state напрямую: рассылает 3 atomic inline action
   * (`walletop`, `debit`, `credit`), связанных общим `process_hash`.
   *
   * @ingroup public_ledger2_actions
   */
  [[eosio::action]] void apply(eosio::name coopname,
                                eosio::name initiator,
                                eosio::name operation_code,
                                eosio::asset amount,
                                eosio::name username,
                                eosio::checksum256 process_hash,
                                std::string memo);

  /**
   * @brief Атомарная операция по кошельку (issue/transfer/block/unblock).
   *
   * Внутренний action — вызывается только через inline из apply().
   * @ingroup public_ledger2_actions
   */
  [[eosio::action]] void walletop(eosio::name coopname,
                                   uint8_t op_code,
                                   eosio::name wallet_from,
                                   eosio::name wallet_to,
                                   eosio::name username,
                                   eosio::asset amount,
                                   eosio::checksum256 process_hash,
                                   std::string memo);

  /**
   * @brief Атомарная дебетовая проводка на счёт + пересчёт сальдо.
   *
   * Внутренний action — вызывается только через inline из apply().
   * @ingroup public_ledger2_actions
   */
  [[eosio::action]] void debit(eosio::name coopname,
                                uint64_t account_id,
                                eosio::asset amount,
                                eosio::checksum256 process_hash,
                                std::string memo);

  /**
   * @brief Атомарная кредитовая проводка на счёт + пересчёт сальдо.
   *
   * Внутренний action — вызывается только через inline из apply().
   * @ingroup public_ledger2_actions
   */
  [[eosio::action]] void credit(eosio::name coopname,
                                 uint64_t account_id,
                                 eosio::asset amount,
                                 eosio::checksum256 process_hash,
                                 std::string memo);

  /**
   * @brief Универсальное миграционное действие — точка расширения для
   * разовых исправлений состояния, которые можно провести автоматически
   * после деплоя контракта.
   *
   * Тело периодически переписывается под текущую задачу миграции; после
   * прогона на проде очищается до пустого `require_auth(get_self())`.
   * История прошлых миграций — в git-истории `ledger2/src/migrate/migrate.cpp`.
   *
   * Текущая задача — см. doxygen-блок в migrate.cpp.
   *
   * @ingroup public_ledger2_actions
   *
   * @note Авторизация требуется от аккаунта: @p ledger2 (get_self()).
   */
  [[eosio::action]] void migrate();

  /**
   * @brief Идемпотентная per-record миграция L3-балансов (Phase 1/2; ADR-008).
   *
   * Заполняет `userwallets[coopname][wallet_name, username]` переданными
   * значениями. Идемпотентно: значения УСТАНАВЛИВАЮТСЯ (не инкрементируются),
   * повторный вызов с теми же параметрами = no-op.
   *
   * Без бух-проводок (это инициализация состояния, не операция). Без сверки
   * с `wallet::users.programs[]` — миграция может заполнять L3 ДО переезда
   * соглашений в `wallet::users` (порядок миграции — на уровне rollout-окна).
   *
   * Auth: `coopname@active`.
   *
   * НЕ путать с зарезервированным `ledger2::migrate` (legacy ledger → ledger2).
   *
   * @param coopname     кооператив (auth + payer)
   * @param wallet_name  USER_SHARED-кошелёк (см. LEDGER2_WALLET_REGISTRY)
   * @param username     пайщик
   * @param available    значение available (asset)
   * @param blocked      значение blocked (asset)
   *
   * @ingroup public_ledger2_actions
   */
  [[eosio::action]] void migrate3(eosio::name coopname,
                                   eosio::name wallet_name,
                                   eosio::name username,
                                   eosio::asset available,
                                   eosio::asset blocked);

  /**
   * @brief Перевод между кошельками внутри одного бух.счёта (operation o.adj.walmove).
   *
   * Ручная корректировка председателя: переносит `amount` с `from_wallet`
   * на `to_wallet` БЕЗ движения по бух.счетам (`debit`/`credit` не вызываются).
   * Применение: разнесение по аналитическим кошелькам после криво легшей миграции
   * (типичный кейс voskhod), мелкие исправления в рамках одного фонда.
   *
   * Auth: `coopname@active` (председатель). Audit: action+inline walletop попадают
   * в blockchain_actions с общим `process_hash`, бэкенд видит как один процесс
   * `processes::adjustment::CORRECTION` (`p.adj.fix`).
   *
   * Валидация: from_wallet ≠ to_wallet, оба в LEDGER2_WALLET_REGISTRY,
   * memo не пуст. Соответствие `account_id` для двух кошельков обеспечивает
   * UI/backend (контракт не хранит wallet→account mapping).
   *
   * @param coopname     кооператив (одновременно payer auth)
   * @param initiator    инициатор корректировки (для аудита; обычно совпадает с coopname)
   * @param username     владелец кошельков (для аналитики; обычно coopname для коллективных кошельков)
   * @param from_wallet  кошелёк-источник
   * @param to_wallet    кошелёк-получатель
   * @param amount       сумма в _root_govern_symbol
   * @param process_hash уникальный хэш корректировки (генерирует backend)
   * @param memo         обязательное обоснование (≤ 255 символов)
   *
   * @ingroup public_ledger2_actions
   */
  [[eosio::action]] void walmove(eosio::name coopname,
                                  eosio::name initiator,
                                  eosio::name username,
                                  eosio::name from_wallet,
                                  eosio::name to_wallet,
                                  eosio::asset amount,
                                  eosio::checksum256 process_hash,
                                  std::string memo);

  /**
   * @brief Откат ранее проведённой операции (operation o.adj.rev).
   *
   * Создаёт зеркальную проводку по операции `original_operation_id`:
   * меняет местами Dr/Cr счета и (для wallet_op) wallet_from/wallet_to.
   * Для исходного ISSUE используется `WalletOp::BURN` (изъятие
   * с `wallet_from` без увеличения куда-либо). Различие «штатное сжигание»
   * vs «зеркало revert» делается через operation_code (`o.adj.rev`).
   *
   * Параметры зеркала готовит backend из своей БД (по записи оригинала
   * в blockchain_actions/state) — контракт не имеет доступа к истории операций.
   *
   * Запрещено откатывать миграционные операции (operation_code starts with `o.mig.`).
   * Повторный откат отката (revert от revert) разрешён.
   *
   * Auth: `coopname@active` (председатель).
   *
   * @param coopname                    кооператив (payer auth)
   * @param initiator                   инициатор отката (для аудита)
   * @param original_operation_id       id оригинальной записи в blockchain_actions
   * @param original_operation_code     operation_code оригинала (для запрета o.mig.*)
   * @param username                    username оригинала (для аналитики)
   * @param amount                      сумма (как в оригинале)
   * @param mirror_wallet_op            тип wallet-операции зеркала (TRANSFER/BURN)
   * @param mirror_wallet_from          кошелёк-источник зеркала
   * @param mirror_wallet_to            кошелёк-получатель зеркала (пустое имя для BURN)
   * @param mirror_debit_account_id     Dr-счёт зеркала (0 если оригинал был без бухпроводок)
   * @param mirror_credit_account_id    Cr-счёт зеркала (0 если оригинал был без бухпроводок)
   * @param process_hash                уникальный хэш для зеркальной операции
   * @param memo                        обязательное обоснование (≤ 255 символов)
   *
   * @ingroup public_ledger2_actions
   */
  [[eosio::action]] void revert(eosio::name coopname,
                                 eosio::name initiator,
                                 uint64_t original_operation_id,
                                 eosio::name original_operation_code,
                                 eosio::name username,
                                 eosio::asset amount,
                                 uint8_t mirror_wallet_op,
                                 eosio::name mirror_wallet_from,
                                 eosio::name mirror_wallet_to,
                                 uint64_t mirror_debit_account_id,
                                 uint64_t mirror_credit_account_id,
                                 eosio::checksum256 process_hash,
                                 std::string memo);
};
