// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Copyright (c) 2012-2013 The Boolberry developers
// Copyright (c) 2017-2025 Lethean (https://lt.hn)
//
// Licensed under the European Union Public Licence (EUPL) version 1.2.
// You may obtain a copy of the licence at:
//
//     https://joinup.ec.europa.eu/software/page/eupl/licence-eupl
//
// The EUPL is a copyleft licence that is compatible with the MIT/X11
// licence used by the original projects; the MIT terms are therefore
// considered “grandfathered” under the EUPL for this code.
//
// SPDX‑License‑Identifier: EUPL-1.2
//


#ifdef ANDROID_BUILD
  #include <android/log.h>
#endif
#include "plain_wallet_api.h"
#include "plain_wallet_api_defs.h"
#include "config/currency_config.h"
#include "version.h"
#include "string_tools.h"
#include "currency_core/currency_format_utils.h"
#include "wallets_manager.h"
#include "common/base58.h"
#include "common/config_encrypt_helper.h"
#include "static_helpers.h"
#include "wallet_helpers.h"

#define ANDROID_PACKAGE_NAME    "com.lethean.mobile"

#define LOGS_FOLDER             "logs"

#define WALLETS_FOLDER_NAME     "wallets"
#define APP_CONFIG_FOLDER       "app_config"
#define APP_CONFIG_FILENAME     "app_cfg.bin"

#define  MOBILE_APP_DATA_FILE_BINARY_SIGNATURE   0x1000111201101011LL //Bender's nightmare

#define GENERAL_INTERNAL_ERRROR_INSTANCE "GENERAL_INTERNAL_ERROR: WALLET INSTNACE NOT FOUND"
#define GENERAL_INTERNAL_ERRROR_INIT "Failed to intialize library"

//TODO: global objects, subject to refactoring



#define GET_INSTANCE_PTR(ptr_name) \
  auto ptr_name = std::atomic_load(&ginstance_ptr); \
  if (!ptr_name) \
  { \
    LOG_ERROR("Core already deinitialised or not initialized yet."); \
    epee::json_rpc::response<view::api_responce_return_code, epee::json_rpc::dummy_error> ok_response = AUTO_VAL_INIT(ok_response); \
    ok_response.result.return_code = API_RETURN_CODE_UNINITIALIZED; \
    return sanitized_store_to_json(ok_response); \
  }

#define PLAIN_WALLET_BEGIN_TRY_ENTRY()     try {
#define PLAIN_WALLET_CATCH()     } \
        catch (const std::exception& e) \
        { \
          LOG_ERROR("Exception during plain wallet call [" << __func__ << "]: " << e.what()); \
          epee::json_rpc::response<view::api_responce_return_code, epee::json_rpc::dummy_error> ok_response = AUTO_VAL_INIT(ok_response); \
          ok_response.result.return_code = std::string(API_RETURN_CODE_INTERNAL_ERROR) + " " + e.what(); \
          return sanitized_store_to_json(ok_response); \
        } \
        catch (...) \
        { \
          LOG_ERROR("Unknown exception during plain wallet call [" << __func__ << "]"); \
          epee::json_rpc::response<view::api_responce_return_code, epee::json_rpc::dummy_error> ok_response = AUTO_VAL_INIT(ok_response); \
          ok_response.result.return_code = API_RETURN_CODE_INTERNAL_ERROR; \
          return sanitized_store_to_json(ok_response); \
        } 














namespace plain_wallet
{
  void deinit();
}

void static_destroy_handler()
{
  LOG_PRINT_L0("[DESTROY CALLBACK HANDLER STARTED]: ");
  plain_wallet::deinit();
  LOG_PRINT_L0("[DESTROY CALLBACK HANDLER FINISHED]: ");
}




namespace plain_wallet
{
  struct plain_wallet_instance
  {
    plain_wallet_instance() :initialized(false), gjobs_counter(1)
    {}
    wallets_manager gwm;
    std::atomic<bool> initialized = false;
    std::atomic<bool> postponed_run_wallet = false;
    std::atomic<bool> postponed_main_worked_started = false;
    callback_type m_callback = nullptr;

    std::atomic<uint64_t> gjobs_counter;
    std::map<uint64_t, std::string> gjobs;
    epee::critical_section gjobs_lock;
    tools::default_http_core_proxy m_common_daemon_proxy;
  };

  std::shared_ptr<plain_wallet::plain_wallet_instance> ginstance_ptr;


  typedef epee::json_rpc::response<epee::json_rpc::dummy_result, error> error_response;

  template<class t_struct>
  std::string sanitized_store_to_json(const t_struct& t_obj)
  {
    std::string r = epee::serialization::store_t_to_json(t_obj);
    tools::sanitize_utf8(r);
    return r;
  }

  std::string get_set_working_dir(bool need_to_set = false, const std::string val = "")
  {
    DEFINE_SECURE_STATIC_VAR(std::string, working_dir);
    if (need_to_set)
      working_dir = val;
    return working_dir;
  }

  std::string get_bundle_working_dir()
  {
    return get_set_working_dir();
  }
  void set_bundle_working_dir(const std::string& dir)
  {
    get_set_working_dir(true, dir);
  }
  
  std::string get_wallets_folder()
  {
#ifdef CAKEWALLET    
    std::string path = "";    
#elif WIN32
    std::string path = get_bundle_working_dir() + "/" + WALLETS_FOLDER_NAME + "/";
#else
    std::string path = get_bundle_working_dir() + "/" + WALLETS_FOLDER_NAME + "/";
#endif // WIN32
    return path;
  }

  std::string get_app_config_folder()
  {
#ifdef CAKEWALLET    
    std::string path = "";
#elif WIN32
    std::string path = get_bundle_working_dir() + "/" + APP_CONFIG_FOLDER + "/";
#else
    std::string path = get_bundle_working_dir() + "/" + APP_CONFIG_FOLDER + "/";
#endif // WIN32
    return path;
  }
#ifdef ANDROID_BUILD
  class android_logger : public log_space::ibase_log_stream
  {
  public:
    int get_type() { return LOGGER_CONSOLE; }
    virtual bool out_buffer(const char* buffer, int buffer_len, int log_level, int color, const char* plog_name = NULL)
    {
      __android_log_write(ANDROID_LOG_INFO, "[tag]", buffer);
      return  true;
    }
  };
#endif

  void initialize_logs(int log_level)
  {
    std::string log_dir = get_bundle_working_dir();
    log_dir += "/" LOGS_FOLDER;

    log_space::get_set_need_thread_id(true, true);
    log_space::log_singletone::enable_channels("core,currency_protocol,tx_pool,p2p,wallet", false);
    epee::log_space::get_set_log_detalisation_level(true, log_level);
#ifdef ANDROID_BUILD
    epee::log_space::log_singletone::add_logger(new android_logger());
#else
    epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL);
#endif

    epee::log_space::log_singletone::add_logger(LOGGER_FILE, "plain_wallet.log", log_dir.c_str());
    LOG_PRINT_L0("Plain wallet initialized: " << CURRENCY_NAME << " v" << PROJECT_VERSION_LONG << ", log location: " << log_dir + "/plain_wallet.log");

    //glogs_initialized = true;
  }

  std::string set_log_level(int log_level)
  {
    epee::log_space::get_set_log_detalisation_level(true, log_level);
    return "{}";
  }

  void deinit()
  {
    auto local_ptr = std::atomic_load(&ginstance_ptr);
    if (local_ptr)
    {
      std::atomic_store(&ginstance_ptr, std::shared_ptr<plain_wallet_instance>());
      //wait other callers finish
      local_ptr->gjobs_lock.lock();
      local_ptr->gjobs_lock.unlock();
      bool r = local_ptr->gwm.quick_stop_no_save();        
      LOG_PRINT_L0("[QUICK_STOP_NO_SAVE] return " << r);
      //let's prepare wallet manager for quick shutdown
      local_ptr.reset();
    }
  }

  std::string reset()
  {
    PLAIN_WALLET_BEGIN_TRY_ENTRY();
    GET_INSTANCE_PTR(inst_ptr);
    inst_ptr->gwm.quick_clear_wallets_no_save();
    epee::json_rpc::response<view::api_responce_return_code, epee::json_rpc::dummy_error> ok_response = AUTO_VAL_INIT(ok_response);
    ok_response.result.return_code = API_RETURN_CODE_OK;
    return sanitized_store_to_json(ok_response);
    PLAIN_WALLET_CATCH();
  }


  std::string init(const std::string& ip_, const std::string& port_, const std::string& working_dir, int log_level)
  {
    std::string ip = ip_;
    std::string port = port_;

    auto local_ptr = std::atomic_load(&ginstance_ptr);
    if (local_ptr)
    {
      LOG_ERROR("Double-initialization in plain_wallet detected.");
      epee::json_rpc::response<view::api_responce_return_code, epee::json_rpc::dummy_error> ok_response = AUTO_VAL_INIT(ok_response);
      ok_response.result.return_code = API_RETURN_CODE_ALREADY_EXISTS;
      return sanitized_store_to_json(ok_response);
    }

    epee::static_helpers::set_or_call_on_destruct(true, static_destroy_handler);

    std::shared_ptr<plain_wallet_instance> ptr(new plain_wallet_instance());

    if (ip.empty())
    {
      ip = "0.0.0.0";
      port = "0";
      ptr->postponed_run_wallet = true;
    }


    set_bundle_working_dir(working_dir);

    initialize_logs(log_level);
    std::string argss_1 = std::string("--remote-node=") + ip + ":" + port;    
    std::string argss_2 = std::string("--disable-logs-init");
    char * args[4];
    static const char* arg0_stub = "stub";
    args[0] = const_cast<char*>(arg0_stub);
    args[1] = const_cast<char*>(argss_1.c_str());
    args[2] = const_cast<char*>(argss_2.c_str());
    args[3] = nullptr;
    std::string command_line_fail_details;
    if (!(ptr->gwm.init_command_line(3, args, command_line_fail_details) && ptr->gwm.init(nullptr)))
    {
      LOG_ERROR("Failed to init wallets_manager");
      return GENERAL_INTERNAL_ERRROR_INIT;
    }
    
    ptr->gwm.set_use_deffered_global_outputs(true);

    if (!ptr->postponed_run_wallet && !ptr->postponed_main_worked_started)
    {
      if (!ptr->gwm.start())
      {
        LOG_ERROR("Failed to start wallets_manager");
        return GENERAL_INTERNAL_ERRROR_INIT;
      }
      ptr->postponed_main_worked_started = true;
    }

    ptr->m_common_daemon_proxy.set_connection_addr(ip + ":" + port);

    LOG_PRINT_L0("[INIT PLAIN_WALLET_INSTANCE] Ver:" << PROJECT_VERSION_LONG << "(" << BUILD_TYPE << ")" << ENDL << "Working dir: " << working_dir << ENDL << "URL: " << ip << ",  port: " << port);


#ifndef CAKEWALLET
    std::string wallets_folder = get_wallets_folder();
    boost::system::error_code ec;
    boost::filesystem::create_directories(wallets_folder, ec);
    if (ec)
    {
      error_response err_result = AUTO_VAL_INIT(err_result);
      err_result.error.code = API_RETURN_CODE_INTERNAL_ERROR;
      err_result.error.message = LOCATION_STR + " \nmessage:" + ec.message();
      return sanitized_store_to_json(err_result);
    }

    std::string app_config_folder = get_app_config_folder();
    boost::filesystem::create_directories(app_config_folder, ec);
    if (ec)
    {
      error_response err_result = AUTO_VAL_INIT(err_result);
      err_result.error.code = API_RETURN_CODE_INTERNAL_ERROR;
      err_result.error.message = LOCATION_STR + " \nmessage:" + ec.message();
      return sanitized_store_to_json(err_result);
    }
#endif
    std::atomic_store(&ginstance_ptr, ptr);
#ifndef CAKEWALLET
    epee::json_rpc::response<view::api_responce_return_code, epee::json_rpc::dummy_error> ok_response = AUTO_VAL_INIT(ok_response);
    ok_response.result.return_code = API_RETURN_CODE_OK;
    return sanitized_store_to_json(ok_response);
#else
    return API_RETURN_CODE_OK;
#endif
  }

  std::string init(const std::string& address, const std::string& working_dir, int log_level)
  {
    epee::net_utils::http::url_content url_data = AUTO_VAL_INIT(url_data);
    if(!address.empty() && !epee::net_utils::parse_url(address, url_data))
    {
        LOG_ERROR("Failed to parse address");
        return API_RETURN_CODE_BAD_ARG;
    }

    return init(url_data.host, std::to_string(url_data.port), working_dir, log_level);
  }


  std::string get_appconfig(const std::string& encryption_key)
  {
    std::string res_str;
    std::string app_config_config_path = get_app_config_folder() + APP_CONFIG_FILENAME;
    std::string ret_code = tools::load_encrypted_file(app_config_config_path, encryption_key, res_str, MOBILE_APP_DATA_FILE_BINARY_SIGNATURE);
    if (ret_code != API_RETURN_CODE_OK)
    {
      error_response err_result = AUTO_VAL_INIT(err_result);
      err_result.error.code = ret_code;
      return sanitized_store_to_json(err_result);
    }
    return res_str;
  }
  std::string set_appconfig(const std::string& conf_str, const std::string& encryption_key)
  {
    std::string app_config_config_path = get_app_config_folder() + APP_CONFIG_FILENAME;
    std::string ret_code = tools::store_encrypted_file(app_config_config_path, encryption_key, conf_str, MOBILE_APP_DATA_FILE_BINARY_SIGNATURE);
    if (ret_code != API_RETURN_CODE_OK)
    {
      error_response err_result = AUTO_VAL_INIT(err_result);
      err_result.error.code = ret_code;
      return sanitized_store_to_json(err_result);
    }
    else
    {
      epee::json_rpc::response<view::api_responce_return_code, epee::json_rpc::dummy_error> ok_response = AUTO_VAL_INIT(ok_response);
      ok_response.result.return_code = API_RETURN_CODE_OK;
      return sanitized_store_to_json(ok_response);
    }
  }
  bool is_wallet_exist(const std::string& path)
  {
    boost::system::error_code ec;
    return boost::filesystem::exists(path, ec);
  }

  std::string generate_random_key(uint64_t lenght)
  {
    std::string buff; 
    buff.resize(lenght);
    crypto::generate_random_bytes(lenght, const_cast<char*>(buff.data()));
    return tools::base58::encode(buff);
  }

  std::string get_logs_buffer()
  {
    return epee::log_space::log_singletone::copy_logs_to_buffer();
  }

  std::string truncate_log()
  {
    epee::log_space::log_singletone::truncate_log_files();
    epee::json_rpc::response<view::api_responce_return_code, epee::json_rpc::dummy_error> ok_response = AUTO_VAL_INIT(ok_response);
    ok_response.result.return_code = API_RETURN_CODE_OK;
    return sanitized_store_to_json(ok_response);
  }

  std::string get_connectivity_status()
  {
    PLAIN_WALLET_BEGIN_TRY_ENTRY();
    GET_INSTANCE_PTR(inst_ptr);

    return inst_ptr->gwm.get_connectivity_status();
    PLAIN_WALLET_CATCH();
  }

  std::string get_version()
  {
    return PROJECT_VERSION_LONG;
  }

  struct strings_list
  {
    std::list<std::string> items;

    BEGIN_KV_SERIALIZE_MAP()
      KV_SERIALIZE(items)
    END_KV_SERIALIZE_MAP()

  };

  std::string get_wallet_files()
  {
    std::string wallet_files_path = get_wallets_folder();
    strings_list sl = AUTO_VAL_INIT(sl);
    epee::file_io_utils::get_folder_content(wallet_files_path, sl.items, true);
    return sanitized_store_to_json(sl);
  }

  std::string get_export_private_info(const std::string& target_dir)
  {
    const std::string src_folder_path = get_bundle_working_dir();
    boost::system::error_code ec;
    const std::string full_target_path = target_dir + "/Lethean_export" + std::to_string(epee::misc_utils::get_tick_count());
    boost::filesystem::create_directory(full_target_path, ec);
    if (ec)
    {
      LOG_ERROR("Failed to create target directory(" << full_target_path << "):" << ec.message());
      epee::json_rpc::response<view::api_responce_return_code, epee::json_rpc::dummy_error> ok_response = AUTO_VAL_INIT(ok_response);
      ok_response.result.return_code = API_RETURN_CODE_FAIL;
      return sanitized_store_to_json(ok_response);
    }
    if(!tools::copy_dir(src_folder_path, full_target_path))
    {
      LOG_ERROR("Failed to copy target directory");
      epee::json_rpc::response<view::api_responce_return_code, epee::json_rpc::dummy_error> ok_response = AUTO_VAL_INIT(ok_response);
      ok_response.result.return_code = API_RETURN_CODE_FAIL;
      return sanitized_store_to_json(ok_response);
    }
    epee::json_rpc::response<view::api_responce_return_code, epee::json_rpc::dummy_error> ok_response = AUTO_VAL_INIT(ok_response);
    ok_response.result.return_code = API_RETURN_CODE_OK;
    return sanitized_store_to_json(ok_response);
  }

  std::string delete_wallet(const std::string& file_name)
  {
    std::string wallet_files_path = get_wallets_folder();
    strings_list sl = AUTO_VAL_INIT(sl);
    boost::system::error_code er;
    boost::filesystem::remove(wallet_files_path + file_name, er);
    epee::json_rpc::response<view::api_responce_return_code, epee::json_rpc::dummy_error> ok_response = AUTO_VAL_INIT(ok_response);
    ok_response.result.return_code = API_RETURN_CODE_OK;
    return sanitized_store_to_json(ok_response);
  }

  std::string get_address_info(const std::string& addr)
  {
    currency::account_public_address apa = AUTO_VAL_INIT(apa);
    currency::payment_id_t pid = AUTO_VAL_INIT(pid);
    bool valid = false;
    bool wrap = false;
    while (true)
    {
      if (currency::is_address_like_wrapped(addr))
      {
        wrap = true;
        valid = true;
        break;
      }

      if (currency::get_account_address_and_payment_id_from_str(apa, pid, addr))
      {
        valid = true;
      }
      break;
    }

    //lazy to make struct for it
    std::stringstream res;
    res << "{ \"valid\": " << (valid?"true":"false") << ", \"auditable\": "
      << (apa.is_auditable() ? "true" : "false")
      << ",\"payment_id\": " << (pid.size() ? "true" : "false") 
      << ",\"wrap\": " << (wrap ? "true" : "false")
      << "}";
    return res.str();
  }

  std::string open(const std::string& path, const std::string& password)
  {
    PLAIN_WALLET_BEGIN_TRY_ENTRY();
    GET_INSTANCE_PTR(inst_ptr);

    std::string full_path = get_wallets_folder() + path;
    epee::json_rpc::response<view::open_wallet_response, epee::json_rpc::dummy_error> ok_response = AUTO_VAL_INIT(ok_response);
    std::string rsp = inst_ptr->gwm.open_wallet(epee::string_encoding::convert_to_unicode(full_path), password, 20, ok_response.result);
    if (rsp == API_RETURN_CODE_OK || rsp == API_RETURN_CODE_FILE_RESTORED)
    {
      if (rsp == API_RETURN_CODE_FILE_RESTORED)
      {
        ok_response.result.recovered = true;
      }
      if (!inst_ptr->postponed_run_wallet)
      {
        inst_ptr->gwm.run_wallet(ok_response.result.wallet_id);
      }      

      return sanitized_store_to_json(ok_response);
    }
    error_response err_result = AUTO_VAL_INIT(err_result);
    err_result.error.code = rsp;
    return sanitized_store_to_json(err_result); 
    PLAIN_WALLET_CATCH();
  }

  std::string restore(const std::string& seed, const std::string& path, const std::string& password, const std::string& seed_password)
  {
    PLAIN_WALLET_BEGIN_TRY_ENTRY();
    GET_INSTANCE_PTR(inst_ptr);

    std::string full_path = get_wallets_folder() + path;
    epee::json_rpc::response<view::open_wallet_response, epee::json_rpc::dummy_error> ok_response = AUTO_VAL_INIT(ok_response);
    std::string rsp = inst_ptr->gwm.restore_wallet(epee::string_encoding::convert_to_unicode(full_path), password, seed, seed_password, ok_response.result);
    if (rsp == API_RETURN_CODE_OK || rsp == API_RETURN_CODE_FILE_RESTORED)
    {
      if (rsp == API_RETURN_CODE_FILE_RESTORED)
      {
        ok_response.result.recovered = true;
      }
      if (!inst_ptr->postponed_run_wallet)
      {
        inst_ptr->gwm.run_wallet(ok_response.result.wallet_id);
      }
      return sanitized_store_to_json(ok_response);
    }
    error_response err_result = AUTO_VAL_INIT(err_result);
    err_result.error.code = rsp;
    return sanitized_store_to_json(err_result);
    PLAIN_WALLET_CATCH();
  }

  std::string generate(const std::string& path, const std::string& password)
  {
    PLAIN_WALLET_BEGIN_TRY_ENTRY();
    GET_INSTANCE_PTR(inst_ptr);

    std::string full_path = get_wallets_folder() + path;
    epee::json_rpc::response<view::open_wallet_response, epee::json_rpc::dummy_error> ok_response = AUTO_VAL_INIT(ok_response);
    std::string rsp = inst_ptr->gwm.generate_wallet(epee::string_encoding::convert_to_unicode(full_path), password, ok_response.result);
    if (rsp == API_RETURN_CODE_OK || rsp == API_RETURN_CODE_FILE_RESTORED)
    {
      if (rsp == API_RETURN_CODE_FILE_RESTORED)
      {
        ok_response.result.recovered = true;
      }
      if (!inst_ptr->postponed_run_wallet)
      {
        inst_ptr->gwm.run_wallet(ok_response.result.wallet_id);
      }
      return sanitized_store_to_json(ok_response);
    }
    error_response err_result = AUTO_VAL_INIT(err_result);
    err_result.error.code = rsp;
    return sanitized_store_to_json(err_result);
    PLAIN_WALLET_CATCH();
  }

  std::string get_opened_wallets()
  {
    PLAIN_WALLET_BEGIN_TRY_ENTRY();
    GET_INSTANCE_PTR(inst_ptr);
    epee::json_rpc::response<std::list<view::open_wallet_response>, epee::json_rpc::dummy_error> ok_response = AUTO_VAL_INIT(ok_response);
    inst_ptr->gwm.get_opened_wallets(ok_response.result);
    return sanitized_store_to_json(ok_response);
    PLAIN_WALLET_CATCH();
  }

  std::string close_wallet(hwallet h)
  {
    PLAIN_WALLET_BEGIN_TRY_ENTRY();
    GET_INSTANCE_PTR(inst_ptr);

    std::string r = "{\"response\": \"";
    r += inst_ptr->gwm.close_wallet(h);
    r += "\"}";
    return r;
    PLAIN_WALLET_CATCH();
  }

  std::string get_wallet_status(hwallet h)
  {
    PLAIN_WALLET_BEGIN_TRY_ENTRY();
    GET_INSTANCE_PTR(inst_ptr);
    return inst_ptr->gwm.get_wallet_status(h);
    PLAIN_WALLET_CATCH();
  }

  std::string invoke(hwallet h, const std::string& params)
  {
    PLAIN_WALLET_BEGIN_TRY_ENTRY();
    GET_INSTANCE_PTR(inst_ptr);
    std::string res = inst_ptr->gwm.invoke(h, params);
    tools::sanitize_utf8(res);
    return res;
    PLAIN_WALLET_CATCH();
  }

  void put_result(uint64_t job_id, const std::string& res)
  {
    auto inst_ptr = std::atomic_load(&ginstance_ptr);
    if (!inst_ptr)
    {
      return;
    }
    if (inst_ptr->m_callback)
    {
      inst_ptr->m_callback(job_id, res);
      LOG_PRINT_L2("[ASYNC_CALL]: Finished(result caalback), job id: " << job_id);
    }
    else
    {
      CRITICAL_REGION_LOCAL(inst_ptr->gjobs_lock);
      inst_ptr->gjobs[job_id] = res;
      LOG_PRINT_L2("[ASYNC_CALL]: Finished(result put), job id: " << job_id);
    }
  }

  void set_callback(callback_type callback)
  {
    auto inst_ptr = std::atomic_load(&ginstance_ptr);
    if (!inst_ptr)
    {
      return;
    }
    inst_ptr->m_callback = callback;
  }

  std::string async_call(const std::string& method_name, uint64_t instance_id, const std::string& params)
  {
    PLAIN_WALLET_BEGIN_TRY_ENTRY();
    GET_INSTANCE_PTR(inst_ptr);
    std::function<void()> async_callback;

    uint64_t job_id = inst_ptr->gjobs_counter++;
    async_callback = [job_id, instance_id, method_name, params]()
    {
      ON_FUNC_EXIT([&]() {LOG_PRINT_L2("Worker for job " << job_id << " finished"); });
      
      std::string res_str = sync_call(method_name,  instance_id, params);
      put_result(job_id, res_str);
    };

    std::thread t([async_callback]() {async_callback(); });
    t.detach();
    LOG_PRINT_L2("[ASYNC_CALL]: started " << method_name << ", job id: " << job_id);
    return std::string("{ \"job_id\": ") + std::to_string(job_id) + "}";
    PLAIN_WALLET_CATCH();
  }

  std::string handle_reset_connection_url(const std::string& url)
  {
    PLAIN_WALLET_BEGIN_TRY_ENTRY();
    GET_INSTANCE_PTR(inst_ptr);
    inst_ptr->gwm.set_remote_node_url(url);

    LOG_PRINT_L0("[RESET URL] URL:" << url);

    view::api_response ar = AUTO_VAL_INIT(ar);
    ar.error_code = API_RETURN_CODE_OK;
    return sanitized_store_to_json(ar);
    PLAIN_WALLET_CATCH();
  }

  std::string handle_proxy_to_daemon(const std::string& data)
  {
    PLAIN_WALLET_BEGIN_TRY_ENTRY();
    GET_INSTANCE_PTR(inst_ptr);
    tools::wallet_public::COMMAND_PROXY_TO_DAEMON::request req;
    tools::wallet_public::COMMAND_PROXY_TO_DAEMON::response res;

    if (!epee::serialization::load_t_from_json(req, data))
    {
      view::api_response ar = AUTO_VAL_INIT(ar);
      ar.error_code = API_RETURN_CODE_BAD_ARG_INVALID_JSON;
      return sanitized_store_to_json(ar);
    }

    std::string buff = epee::string_encoding::base64_decode(req.base64_body);
    int response_code = 0;
    std::string response_body;
    if (!inst_ptr->m_common_daemon_proxy.call_COMMAND_RPC_INVOKE(req.uri, buff, response_code, response_body))
    {
      view::api_response ar = AUTO_VAL_INIT(ar);
      ar.error_code = API_RETURN_CODE_FAIL;
      return sanitized_store_to_json(ar);
    }
    res.base64_body = epee::string_encoding::base64_encode(response_body);
    res.response_code = response_code;
    return sanitized_store_to_json(res);
    PLAIN_WALLET_CATCH();
  }

  

  std::string handle_run_wallet(uint64_t instance_id)
  {
    PLAIN_WALLET_BEGIN_TRY_ENTRY();
    GET_INSTANCE_PTR(inst_ptr);


    //postponed worker loop launch
    if (!inst_ptr->postponed_main_worked_started)
    {
      if (!inst_ptr->gwm.start())
      {
        LOG_ERROR("Failed to start wallets_manager");
        return API_RETURN_CODE_INTERNAL_ERROR;
      }
      inst_ptr->postponed_main_worked_started = true;
    }

    view::api_response ar = AUTO_VAL_INIT(ar);
    ar.error_code = inst_ptr->gwm.run_wallet(instance_id);
    return sanitized_store_to_json(ar);
    PLAIN_WALLET_CATCH();
  }

  std::string handle_configure(const std::string& settings_json)
  {
    PLAIN_WALLET_BEGIN_TRY_ENTRY();
    GET_INSTANCE_PTR(inst_ptr);
    configure_object conf = AUTO_VAL_INIT(conf);
    configure_response conf_resp = AUTO_VAL_INIT(conf_resp);
    bool res = epee::serialization::load_t_from_json(conf, settings_json);
    if (!res)
    {
      conf_resp.status = API_RETURN_CODE_BAD_ARG;
      return sanitized_store_to_json(conf_resp);
    }
    inst_ptr->postponed_run_wallet = conf.postponed_run_wallet;
    conf_resp.status = API_RETURN_CODE_OK;
    return sanitized_store_to_json(conf_resp);
    PLAIN_WALLET_CATCH();
  }

  std::string sync_call(const std::string& method_name, uint64_t instance_id, const std::string& params)
  {
    std::string res;
    try {

      if (method_name == "close")
      {
        close_wallet(instance_id);
        view::api_responce_return_code rc = AUTO_VAL_INIT(rc);
        rc.return_code = API_RETURN_CODE_OK;
        res = sanitized_store_to_json(rc);
      }
      else if (method_name == "open")
      {
        view::open_wallet_request owr = AUTO_VAL_INIT(owr);
        if (!epee::serialization::load_t_from_json(owr, params))
        {
          view::api_response ar = AUTO_VAL_INIT(ar);
          ar.error_code = "Wrong parameter";
          res = sanitized_store_to_json(ar);
        }
        else
        {
          res = open(owr.path, owr.pass);
        }
      }
      else if (method_name == "restore")
      {
        view::restore_wallet_request rwr = AUTO_VAL_INIT(rwr);
        if (!epee::serialization::load_t_from_json(rwr, params))
        {
          view::api_response ar = AUTO_VAL_INIT(ar);
          ar.error_code = "Wrong parameter";
          res = sanitized_store_to_json(ar);
        }
        else
        {
          res = restore(rwr.seed_phrase, rwr.path, rwr.pass, rwr.seed_pass);
        }
      }
      else if (method_name == "get_seed_phrase_info")
      {
        view::seed_info_param sip = AUTO_VAL_INIT(sip);
        if (!epee::serialization::load_t_from_json(sip, params))
        {
          view::api_response ar = AUTO_VAL_INIT(ar);
          ar.error_code = "Wrong parameter";
          res = sanitized_store_to_json(ar);
        }
        else
        {
          view::api_response_t<view::seed_phrase_info> rsp = AUTO_VAL_INIT(rsp);
          rsp.error_code = tools::get_seed_phrase_info(sip.seed_phrase, sip.seed_password, rsp.response_data);
          res = sanitized_store_to_json(rsp);
        }
      }
      else if (method_name == "invoke")
      {
        res = invoke(instance_id, params);
      }
      else if (method_name == "get_wallet_status")
      {
        res = get_wallet_status(instance_id);
      }
      else if (method_name == "configure")
      {
        res = handle_configure(params);
      }
      else if (method_name == "reset_connection_url")
      {
        res = handle_reset_connection_url(params);
      }
      else if (method_name == "run_wallet")
      {
        res = handle_run_wallet(instance_id);
      }
      else if (method_name == "proxy_to_daemon")
      {
        res = handle_proxy_to_daemon(params);
      }
      else
      {
        view::api_response ar = AUTO_VAL_INIT(ar);
        ar.error_code = "UNKNOWN METHOD";
        res = sanitized_store_to_json(ar);
      }
      return res;
    }
    catch (const std::exception& ex)
    {
      LOG_ERROR("Exception during method call: " << method_name << ", instance id:" << instance_id << ", params: " << ENDL << params << ENDL << "exception text: " << ex.what());
      view::api_response ar = AUTO_VAL_INIT(ar);
      ar.error_code = std::string("EXCEPTION: ") + ex.what();
      res = sanitized_store_to_json(ar);
      return res;
    }
    catch (...)
    {
      LOG_ERROR("Unknown exception during method call: " << method_name << ", instance id:" << instance_id << ", params: " << ENDL << params);
      view::api_response ar = AUTO_VAL_INIT(ar);
      ar.error_code = "UNKONW_EXCEPTION";
      res = sanitized_store_to_json(ar);
      return res;
    }
  }

  std::string try_pull_result(uint64_t job_id)
  {
    auto inst_ptr = std::atomic_load(&ginstance_ptr);
    if (!inst_ptr)
    { 
      return "{\"status\": \"canceled\"}";
    }
    //TODO: need refactoring
    CRITICAL_REGION_LOCAL(inst_ptr->gjobs_lock);
    auto it = inst_ptr->gjobs.find(job_id);
    if (it == inst_ptr->gjobs.end())
    {
      return "{\"status\": \"idle\"}";
    }
    std::string res = "{\"status\": \"delivered\", \"result\": ";
    res += it->second;
    res += "  }";
    inst_ptr->gjobs.erase(it);
    return res;
  }

  struct wallet_extended_info
  {
    view::wallet_info wi;
    view::wallet_info_extra wi_extended;

    BEGIN_KV_SERIALIZE_MAP()
      KV_SERIALIZE(wi)
      KV_SERIALIZE(wi_extended)
    END_KV_SERIALIZE_MAP()
  };

  std::string get_wallet_info(hwallet h)
  {
    PLAIN_WALLET_BEGIN_TRY_ENTRY();
    GET_INSTANCE_PTR(inst_ptr);
    wallet_extended_info wei = AUTO_VAL_INIT(wei);
    inst_ptr->gwm.get_wallet_info(h, wei.wi);
    inst_ptr->gwm.get_wallet_info_extra(h, wei.wi_extended);
    return sanitized_store_to_json(wei);
    PLAIN_WALLET_CATCH();
  }
  std::string reset_wallet_password(hwallet h, const std::string& password)
  {
    PLAIN_WALLET_BEGIN_TRY_ENTRY();
    GET_INSTANCE_PTR(inst_ptr);
    return inst_ptr->gwm.reset_wallet_password(h, password);
    PLAIN_WALLET_CATCH();
  }


  // 0 (default), 1 (unimportant), 2 (normal), 3 (elevated), 4 (priority)
  uint64_t get_current_tx_fee(uint64_t priority)
  {
    return TX_DEFAULT_FEE;
  }
}