class ElasticAPM::CentralConfig

@api private

def assign(update)

def assign(update)
  # For each updated option, store the original value,
  # unless already stored
  update.each_key do |key|
    @modified_options[key] ||= config.get(key.to_sym)&.value
  end
  # If the new update doesn't set a previously modified option,
  # revert it to the original
  @modified_options.each_key do |key|
    next if update.key?(key)
    update[key] = @modified_options.delete(key)
  end
  config.assign(update)
end

def fetch_and_apply_config

def fetch_and_apply_config
  @promise =
    Concurrent::Promise
    .execute(&method(:fetch_config))
    .on_success(&method(:handle_success))
    .rescue(&method(:handle_error))
end

def fetch_config

rubocop:disable Metrics/MethodLength
def fetch_config
  resp = perform_request
  case resp.status
  when 200..299
    resp
  when 300..399
    resp
  when 400..499
    raise ClientError, resp
  when 500..599
    raise ServerError, resp
  end
end

def handle_error(error)

def handle_error(error)
  error(
    'Failed fetching config: %s, trying again in %d seconds',
    error.response.body, DEFAULT_MAX_AGE
  )
  assign({})
  schedule_next_fetch(error.response)
end

def handle_success(resp)

rubocop:disable Metrics/MethodLength
def handle_success(resp)
  if resp.status == 304
    info 'Received 304 Not Modified'
  else
    update = JSON.parse(resp.body.to_s)
    assign(update)
    info 'Updated config from Kibana'
  end
  schedule_next_fetch(resp)
  true
rescue Exception => e
  error 'Failed to apply remote config, %s', e.inspect
  debug { e.backtrace.join('\n') }
end

def initialize(config)

def initialize(config)
  @config = config
  @modified_options = {}
  @service_info = {
    'service.name': config.service_name,
    'service.environment': config.environment
  }.to_json
end

def perform_request

def perform_request
  Http.post(
    config.server_url + '/agent/v1/config/',
    body: @service_info,
    headers: { etag: 1, content_type: 'application/json' }
  )
end

def schedule_next_fetch(resp)

def schedule_next_fetch(resp)
  seconds =
    if (cache_header = resp.headers['Cache-Control'])
      CacheControl.new(cache_header).max_age
    else
      DEFAULT_MAX_AGE
    end
  @scheduled_task =
    Concurrent::ScheduledTask
    .execute(seconds, &method(:fetch_and_apply_config))
end

def start

def start
  return unless config.central_config?
  fetch_and_apply_config
end

def stop

def stop
  @scheduled_task&.cancel
end