# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
# frozen_string_literal: false

require 'ffi'
# require the gem
require 'contrast-agent-lib'

module Contrast
  module AgentLib
    # This module is defined in Rust as external, we used it here.
    # Initializes the AgentLib. Here will be all methods from
    # the C bindings agent_init mod.
    module Init
      extend FFI::Library
      ffi_lib ContrastAgentLib::CONTRAST_C

      # Init
      #
      # 0 => OK, -1 => Err
      # The attach function could be called also like this:
      # attach_function :ruby_name, :c_name, [ :params ], :returns, { :options => values }
      # an that way we define a ruby_name for the C method, but we alias to make a documentation
      # for the method.
      #
      # Also we extend the FFI::Library inside this module so we could also redefine the
      # attach_function to our taste, not worry about it leaking outside of this module.
      #
      # @param [Symbol] Name of required function.
      # @param [Array] An array of argument types.
      # @return [Integer] Return type of the function.
      attach_function :init, [], :int

      #  Initialize agent lib without any optional settings. To set optional settings consider using
      # `init_with_options` instead If you want to enable logging, it must be set using environment variables
      # `CONTRAST_AGENTLIB_LOG_LEVEL` - set to log level.  Must of one of ERROR, WARN, INFO, DEBUG or TRACE
      # `CONTRAST_AGENTLIB_LOG_DIR` - must point to an accessible directory where logs will be written.
      #  The name of the log file
      # is auto-generated and cannot be set. The format is 'libcontrast_<date>_<process_id>.log'
      #
      # If these environment variables are not present during `init`, agent-lib will be initialized with
      # logging disabled and you will not be able to re-enable it using `change_log_settings`
      # @param [Symbol] Name of required function.
      # @param [Array] An array of argument types.
      # @return [Integer] Return type of the function.
      attach_function :init_with_options, %i[bool string string], :int

      # Change log settings for agent lib after it's been initialized.  This api must be used after init
      #
      # Safety
      # The `log_level` parameter must point to must point to an UTF-8 encoded string C-string
      # @param enable_logging [Boolean] flag to enable or disable logging.
      # @param log_level [String] UTF-8 encoded string indicating the maximum log level
      # if logging is enabled
      attach_function :change_log_settings, %i[bool string], :int

      # Initialize AgentLib with options.
      # If init returns 0 = successful setup with options
      # if init returns 1 = unsuccessful setup with options
      #
      # @param enable_logging [Boolean] flag to enable or disable logging.
      # @param log_dir [String] path to existing log directory.
      # @param log_level [String] OFF, ERROR, WARN, INFO, DEBUG or TRACE.
      # @return [Boolean] true if initialized false if not.
      def dl__init_with_options enable_logging, log_dir, log_level
        # This is useful for scope sensitive string pointers, if function ends and
        # you need them to be picked up after the C function.
        init_with_options(enable_logging, log_dir, log_level).zero?
      end

      private

      # Initialize AgentLib without options. Log level could not be set after.
      # If you want to set the log levels you can do it be ENV variables:
      # EVN [CONTRAST_AGENTLIB_LOG_LEVEL] - set to log level.  Must of one of ERROR, WARN, INFO, DEBUG or TRACE
      # ENV['CONTRAST_AGENTLIB_LOG_DIR'] - must point to an accessible directory where logs will be written.
      # The name of the log file.
      #
      # @return [Boolean] true if initialized, false if not.
      def dl__init
        init.zero?
      end

      # Change the log settings. This api must be called after the dl__init_with_options.
      #
      # @param enable_log [Boolean] flag to enable or disable logging this sets the inner flag.
      # @param log_level [String] OFF, ERROR, WARN, INFO, DEBUG or TRACE.
      # @return [Boolean] true if initialized false if not.
      def dl__change_log_settings enable_log, log_level
        return true if change_log_settings(enable_log, log_level).zero?

        false
      end
    end
  end
end