module Import
  module OTRS

    # @!attribute [rw] retry_sleep
    #   @return [Number] the sleep time between the request retries
    module Requester
      extend Import::Helper

      # rubocop:disable Style/ModuleFunction
      extend self

      attr_accessor :retry_sleep

      # Loads entries of the given object.
      #
      # @param object [String] the name of OTRS object
      # @param [Hash] opts the options to load entries.
      # @option opts [String] :limit the maximum amount of entries that should get loaded
      # @option opts [String] :offset the offset where the entry listing should start
      # @option opts [Boolean] :diff request only changed/added entries since the last import
      #
      # @example
      #   Import::OTRS::Requester.load('State', offset: '0', limit: '50')
      #   #=> [{'Name':'pending reminder', ...}, ...]
      #
      # @return [Array<Hash{String => String, Number, nil, Hash, Array}>]
      def load(object, opts = {})

        @cache ||= {}
        if opts.empty? && @cache[object]
          return @cache[object]
        end

        result = request_result(
          Subaction: 'Export',
          Object:    object,
          Limit:     opts[:limit]  || '',
          Offset:    opts[:offset] || '',
          Diff:      opts[:diff] ? 1 : 0
        )

        return result if !opts.empty?
        @cache[object] = result
        @cache[object]
      end

      # Lists the OTRS objects and their amount of importable entries.
      #
      # @example
      #   Import::OTRS::Requester.list #=> {'DynamicFields' => 5, ...}
      #
      # @return [Hash{String => Number}] key = OTRS object, value = amount
      def list
        request_result(Subaction: 'List')
      end

      # Checks if the connection to the OTRS export endpoint works.
      #
      # @todo Refactor to something like .connected?
      #
      # @example
      #   Import::OTRS::Requester.connection_test #=> true
      #
      # @raise [RuntimeError] if the API key is not valid
      #
      # @return [true] always returns true
      def connection_test
        result = request_json({})
        raise 'API key not valid!' if !result['Success']
        true
      end

      private

      def request_result(params)
        tries ||= 1
        response = request_json(params)
        response['Result']
      rescue
        # stop after 3 tries
        raise if tries == 3

        # try again
        tries += 1
        sleep tries * (retry_sleep || 15)
        retry
      end

      def request_json(params)
        response = post(params)
        result   = handle_response(response)
        raise 'Invalid response' if !result
        result
      end

      def handle_response(response)
        encoded_body = Encode.conv('utf8', response.body.to_s)
        # remove null bytes otherwise PostgreSQL will fail
        encoded_body.gsub!('\u0000', '')
        JSON.parse(encoded_body)
      end

      def post(params)
        url             = Setting.get('import_otrs_endpoint')
        params[:Action] = 'ZammadMigrator'
        params[:Key]    = Setting.get('import_otrs_endpoint_key')

        log 'POST: ' + url
        log 'PARAMS: ' + params.inspect

        response = UserAgent.post(
          url,
          params,
          {
            open_timeout:  10,
            read_timeout:  120,
            total_timeout: 360,
            user:          Setting.get('import_otrs_user'),
            password:      Setting.get('import_otrs_password'),
          },
        )

        if !response
          raise "Can't connect to Zammad Migrator"
        end

        if !response.success?
          log "ERROR: #{response.error}"
          raise 'Zammad Migrator returned an error'
        end
        response
      end
    end
  end
end
