/*
 * Copyright (c) 2020, 2026, Oracle and/or its affiliates.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License, version 2.0,
 * as published by the Free Software Foundation.
 *
 * This program is designed to work with certain software (including
 * but not limited to OpenSSL) that is licensed under separate terms,
 * as designated in a particular file or component or in included license
 * documentation.  The authors of MySQL hereby grant you an additional
 * permission to link the program and your derivative works with the
 * separately licensed software that they have either included with
 * the program or referenced in the documentation.
 *
 * This program is distributed in the hope that it will be useful,  but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 * the GNU General Public License, version 2.0, for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef MODULES_UTIL_LOAD_LOAD_DUMP_OPTIONS_H_
#define MODULES_UTIL_LOAD_LOAD_DUMP_OPTIONS_H_

#include <cinttypes>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <unordered_set>
#include <vector>

#include "mysqlshdk/include/scripting/types.h"
#include "mysqlshdk/libs/db/filtering_options.h"
#include "mysqlshdk/libs/utils/utils_file.h"
#include "mysqlshdk/libs/utils/utils_sqlstring.h"
#include "mysqlshdk/libs/utils/version.h"

#include "modules/mod_utils.h"
#include "modules/util/common/common_options.h"
#include "modules/util/import_table/helpers.h"
#include "modules/util/load/convert_vector_store.h"
#include "modules/util/load/heatwave_load.h"
#include "modules/util/load/lakehouse_source_option.h"

namespace mysqlsh {

inline std::string schema_object_key(std::string_view schema,
                                     std::string_view table) {
  std::string res;
  res.reserve(schema.size() + table.size() + 5);

  res.append(shcore::quote_identifier(schema));

  if (!table.empty()) {
    res.append(".");
    res.append(shcore::quote_identifier(table));
  }

  return res;
}

inline std::string schema_table_object_key(std::string_view schema,
                                           std::string_view table,
                                           std::string_view partition) {
  auto res = schema_object_key(schema, table);
  if (partition.empty()) return res;

  res.append(".");
  res.append(shcore::quote_identifier(partition));
  return res;
}

#ifdef _WIN32
#undef IGNORE
#endif

class Load_dump_options : public common::Common_options {
 public:
  using Filtering_options = mysqlshdk::db::Filtering_options;
  using Connection_options = mysqlshdk::db::Connection_options;

  enum class Analyze_table_mode { OFF, HISTOGRAM, ON };

  enum class Defer_index_mode { OFF, FULLTEXT, ALL };

  enum class Update_gtid_set { OFF, REPLACE, APPEND };

  enum class Handle_grant_errors { ABORT, DROP_ACCOUNT, IGNORE };

  enum class Bulk_load_fs { UNSUPPORTED, INFILE, URL, S3 };

  struct Bulk_load_info {
    bool enabled = false;
    bool monitoring = false;
    uint64_t threads = 0;
    Bulk_load_fs fs = Bulk_load_fs::UNSUPPORTED;
    std::string file_prefix;
  };

  Load_dump_options();

  Load_dump_options(const Load_dump_options &other) = default;
  Load_dump_options(Load_dump_options &&other) = default;

  Load_dump_options &operator=(const Load_dump_options &other) = default;
  Load_dump_options &operator=(Load_dump_options &&other) = default;

  ~Load_dump_options() override = default;

  static const shcore::Option_pack_def<Load_dump_options> &options();

  std::string target_import_info(const char *operation = "Loading",
                                 const std::string &extra = {}) const;

  std::unique_ptr<mysqlshdk::storage::IDirectory> create_dump_handle() const;

  std::unique_ptr<mysqlshdk::storage::IFile> create_progress_file_handle()
      const;

  const std::optional<std::string> &progress_file() const {
    return m_progress_file;
  }

  void set_progress_file(const std::string &file);

  uint64_t threads_count() const { return m_threads_count; }

  uint64_t background_threads_count(uint64_t def) const {
    return m_background_threads_count.value_or(def);
  }

  void set_background_threads_count(uint64_t count) {
    m_background_threads_count = count;
  }

  uint64_t threads_per_add_index() const { return m_threads_per_add_index; }

  uint64_t dump_wait_timeout_ms() const { return m_wait_dump_timeout_ms; }

  void set_dump_wait_timeout_ms(uint64_t timeout_ms) {
    m_wait_dump_timeout_ms = timeout_ms;
  }

  const std::string &character_set() const { return m_character_set; }

  bool load_data() const { return m_load_data; }

  void set_load_data(bool load) { m_load_data = load; }

  bool load_ddl() const { return m_load_ddl; }

  void set_load_ddl(bool load) { m_load_ddl = load; }

  bool load_users() const { return m_load_users; }

  void set_load_users(bool load) { m_load_users = load; }

  bool dry_run() const { return m_dry_run; }

  bool reset_progress() const { return m_reset_progress; }

  bool skip_binlog() const { return m_skip_binlog; }

  const Filtering_options &filters() const { return m_filtering_options; }

  inline Filtering_options &filters() { return m_filtering_options; }

  bool ignore_existing_objects() const { return m_ignore_existing_objects; }

  bool drop_existing_objects() const { return m_drop_existing_objects; }

  bool ignore_version() const { return m_ignore_version; }

  Defer_index_mode defer_table_indexes() const { return m_defer_table_indexes; }

  bool load_deferred_indexes() const {
    return m_load_indexes && m_defer_table_indexes != Defer_index_mode::OFF;
  }

  Analyze_table_mode analyze_tables() const { return m_analyze_tables; }

  Update_gtid_set update_gtid_set() const { return m_update_gtid_set; }

  const std::string &target_schema() const { return m_target_schema; }

  const mysqlshdk::utils::Version &target_server_version() const {
    return m_target_server_version;
  }

  bool is_mds() const { return m_is_mds; }

  bool is_lakehouse_enabled() const { return m_is_lakehouse_enabled; }

  bool show_metadata() const { return m_show_metadata; }

  void set_show_metadata(bool show) { m_show_metadata = show; }

  inline std::optional<bool> should_create_pks() const noexcept {
    return m_create_invisible_pks;
  }

  /**
   * Whether target server supports Generated Invisible Primary Keys.
   */
  bool is_gipk_supported() const;

  /**
   * Whether target server supports GIPKs and user is able to toggle the
   * system variable.
   */
  bool is_auto_create_pks_supported() const {
    return is_gipk_supported() &&
           is_sql_generate_invisible_primary_key_settable();
  }

  /**
   * Global value of 'sql_generate_invisible_primary_key' in the target server.
   */
  inline bool sql_generate_invisible_primary_key() const noexcept {
    return m_sql_generate_invisible_primary_key;
  }

  /**
   * Is user loading the dump able to toggle
   * 'sql_generate_invisible_primary_key'.
   */
  inline bool is_sql_generate_invisible_primary_key_settable() const noexcept {
    return m_is_sql_generate_invisible_primary_key_settable;
  }

  std::optional<uint64_t> max_bytes_per_transaction() const {
    return m_max_bytes_per_transaction;
  }

  const std::string &server_uuid() const { return m_server_uuid; }

  const std::vector<std::string> &session_init_sql() const {
    return m_session_init_sql;
  }

  Handle_grant_errors on_grant_errors() const { return m_handle_grant_errors; }

  bool partial_revokes() const { return m_partial_revokes; }

  inline int8_t lower_case_table_names() const noexcept {
    return m_lower_case_table_names;
  }

  bool fast_sub_chunking() const { return m_use_fast_sub_chunking; }

  void enable_fast_sub_chunking() { m_use_fast_sub_chunking = true; }

  inline bool checksum() const noexcept { return m_checksum; }

  inline const Bulk_load_info &bulk_load_info() const noexcept {
    return m_bulk_load_info;
  }

  inline bool is_mle_component_installed() const noexcept {
    return m_is_mle_component_installed;
  }

  inline bool is_library_ddl_supported() const noexcept {
    return m_is_library_ddl_supported;
  }

  inline bool supports_pke_as_pk() const noexcept {
    return m_supports_pke_as_pk;
  }

  inline bool is_ddm_ddl_supported() const noexcept {
    return m_is_ddm_ddl_supported;
  }

  inline bool is_ddm_component_installed() const noexcept {
    return m_is_ddm_component_installed;
  }

  inline bool has_lakehouse_source_option() const {
    return !m_lakehouse_source.par().empty() ||
           !m_lakehouse_source.resource_principals().empty();
  }

  inline const load::Lakehouse_source_option &lakehouse_source()
      const noexcept {
    return m_lakehouse_source;
  }

  inline bool has_convert_vector_store_option() const {
    return m_convert_vector_store.has_value();
  }

  inline void set_convert_vector_store(load::Convert_vector_store mode) {
    m_convert_vector_store = mode;
  }

  inline load::Convert_vector_store convert_vector_store() const {
    // WL16802-FR2.1.2: if not given, use the AUTO mode
    return m_convert_vector_store.value_or(load::Convert_vector_store::AUTO);
  }

  inline bool has_heatwave_load_option() const {
    return m_has_heatwave_load_option;
  }

  inline void set_heatwave_load(load::Heatwave_load mode) {
    m_heatwave_load = mode;
  }

  inline load::Heatwave_load heatwave_load() const {
    return load_data() ? m_heatwave_load : load::Heatwave_load::NONE;
  }

  using Storage_options::set_storage_config;

  using Common_options::session;
  using Common_options::set_show_progress;

 private:
  void on_set_url(const std::string &url, Storage_type storage,
                  const mysqlshdk::storage::Config_ptr &config) override;

  void on_unpacked_options();

  void on_set_session(
      const std::shared_ptr<mysqlshdk::db::ISession> &session) override;

  void on_configure() override;

  void set_wait_timeout(const double &timeout_seconds);

  void set_max_bytes_per_transaction(const std::string &value);

  void set_handle_grant_errors(const std::string &action);

  bool include_object(std::string_view schema, std::string_view object,
                      const std::unordered_set<std::string> &included,
                      const std::unordered_set<std::string> &excluded) const;

  void configure_bulk_load();

  void set_convert_vector_store_option(const std::string &value);

  void set_heatwave_load_option(const std::string &value);

  uint64_t m_threads_count = 4;
  std::optional<uint64_t> m_background_threads_count;

  Filtering_options m_filtering_options;

  uint64_t m_wait_dump_timeout_ms = 0;
  bool m_reset_progress = false;
  std::optional<std::string> m_progress_file;
  std::string m_default_progress_file;
  std::string m_character_set;
  bool m_load_data = true;
  bool m_load_ddl = true;
  bool m_load_users = false;
  Analyze_table_mode m_analyze_tables = Analyze_table_mode::OFF;
  bool m_dry_run = false;
  bool m_skip_binlog = false;
  bool m_ignore_existing_objects = false;
  bool m_drop_existing_objects = false;
  bool m_ignore_version = false;
  Defer_index_mode m_defer_table_indexes = Defer_index_mode::FULLTEXT;
  bool m_load_indexes = true;
  Update_gtid_set m_update_gtid_set = Update_gtid_set::OFF;
  std::string m_target_schema;
  bool m_disable_bulk_load = false;

  mysqlshdk::utils::Version m_target_server_version;
  bool m_is_mds = false;
  bool m_is_lakehouse_enabled = false;
  bool m_show_metadata = false;
  std::optional<bool> m_create_invisible_pks;
  bool m_sql_generate_invisible_primary_key = false;
  bool m_is_sql_generate_invisible_primary_key_settable = false;

  std::optional<uint64_t> m_max_bytes_per_transaction;

  std::string m_server_uuid;

  std::vector<std::string> m_session_init_sql;

  Handle_grant_errors m_handle_grant_errors = Handle_grant_errors::ABORT;

  // how many threads are used by the server per one ALTER TABLE ... ADD INDEX
  uint64_t m_threads_per_add_index = 1;

  bool m_checksum = false;

  // whether partial revokes are enabled
  bool m_partial_revokes = false;

  int8_t m_lower_case_table_names;

  bool m_use_fast_sub_chunking = false;

  Bulk_load_info m_bulk_load_info;

  bool m_is_mle_component_installed = false;

  bool m_is_library_ddl_supported = false;

  bool m_supports_pke_as_pk = false;

  // DDM - dynamic data masking
  bool m_is_ddm_ddl_supported = false;

  bool m_is_ddm_component_installed = false;

  load::Lakehouse_source_option m_lakehouse_source;

  std::optional<load::Convert_vector_store> m_convert_vector_store;

  // WL16802-FR2.3.2: heatwaveLoad is set to 'vector_store' by default
  load::Heatwave_load m_heatwave_load = load::Heatwave_load::VECTOR_STORE;
  bool m_has_heatwave_load_option = false;
};

}  // namespace mysqlsh

#endif  // MODULES_UTIL_LOAD_LOAD_DUMP_OPTIONS_H_
