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

require 'json'
require 'fileutils'
require 'contrast/utils/timer'
require 'contrast/utils/log_utils'
require 'contrast/components/logger'
require 'contrast/utils/object_share'
require 'contrast/config/config'
require 'contrast/config/effective_config'
require 'contrast/config/effective_config_value'

module Contrast
  module Agent
    module DiagnosticsConfig
      # This class is responsible for logging to file the effective Agent configurations after startup.
      class Diagnostics
        include Contrast::Components::Logger::InstanceMethods
        include Contrast::Utils::LogUtils

        # @return [String] path to write the file.
        attr_reader :path

        DEFAULT_PATH = File.join(Dir.pwd).cs__freeze
        ERROR_MESSAGE = '[Configuration Diagnostics] Could not write effective Agent configuration to file'
        FILE_NAME = 'contrast_connection.json'
        WRITE = 'w+'

        # @param path [String] path to write to file.
        def initialize path
          @path = path if path && !path.empty?
        end

        # Write current settings to file.
        #
        # @param reset [Boolean] should we reset the config we have
        # @return [Boolean]
        def write_to_file reset: true
          logger&.info('[Configuration Diagnostics] Writing Effective Configurations to file', path: dir_name)
          status = false
          write_to_file_logic(status, reset: reset)
        rescue IOError => e
          logger&.warn(ERROR_MESSAGE, e)
          false
        end

        def extract_settings
          Contrast::AGENT.to_effective_config(config.effective_config)
          Contrast::API.to_effective_config(config.effective_config)
          Contrast::APP_CONTEXT.to_effective_config(config.effective_config)
          Contrast::ASSESS.to_effective_config(config.effective_config)
          Contrast::INVENTORY.to_effective_config(config.effective_config)
          Contrast::PROTECT.to_effective_config(config.effective_config)
        end

        # Determine the path of the current logger and permissions required for
        # writing to path.
        #
        # @return [String] Path
        def dir_name
          @_dir_name ||= if write_permission?(@path)
                           File.dirname(File.absolute_path(@path))
                         else
                           DEFAULT_PATH
                         end
        rescue Errno::EROFS => e
          logger.warn(ERROR_MESSAGE, e)
        end

        # Returns effective configurations of the agent.
        #
        # @return [Contrast::Agent::DiagnosticsConfig::Config]
        def config
          @_config ||= Contrast::Agent::DiagnosticsConfig::Config.new
        end

        # Reset the state of the current effective config.
        def reset_config
          @_config = Contrast::Agent::DiagnosticsConfig::Config.new
        end

        def to_controlled_hash
          {
              ReportCreate: report_create_time,
              Config: config.to_controlled_hash
          }
        end

        def write_to_file_logic status, reset: true
          # We need to reset the config before updating it's values
          reset_config if reset
          extract_settings
          File.open(File.join(dir_name, FILE_NAME), WRITE) do |file|
            file.truncate(0)
            file.write(JSON.pretty_generate(to_controlled_hash, { space: Contrast::Utils::ObjectShare::EMPTY_STRING }))
            status = true if file
            file.close
          end
          status
        end

        private

        # Return current time in iso8601 format.
        #
        # @return [String] Time of creation
        def report_create_time
          Contrast::Utils::Timer.time_now
        end
      end
    end
  end
end