# frozen_string_literal: true

require 'netaddr'

module Twitch
  module Audit
    module Mixins
      # A mixin for Rack::Request which overrides the ip resolution to give proper X-Forwarded-For validation
      module ClientIPResolution
        def ip
          resolve_client_ip(get_header('HTTP_X_FORWARDED_FOR'), get_header('REMOTE_ADDR'))
        end

        private

        # Finds the client ip given a list of possible ips
        # @param [String, nil] forwarded_ips value of the X-Forwarded-For header
        # @param [String] client_ip the ip address as per the network socket
        # @return [String]
        def resolve_client_ip(forwarded_ips, client_ip)
          return client_ip unless trusted_ip?(client_ip)

          forwarded_ips = split_ip_addresses(forwarded_ips)
          forwarded_ips.reverse_each { |ip| return ip unless trusted_ip?(ip) }
          forwarded_ips.first || client_ip
        end

        # Tests if a given ip is considered to be a trusted source.
        # @param [String, NetAddr:CIDR] ip
        # @return [Boolean]
        def trusted_ip?(ip)
          ip = NetAddr::CIDR.create(ip) unless ip.is_a?(NetAddr::CIDR)
          trusted_subnets.any? do |subnet|
            ip.version == subnet.version && [0, 1].include?(NetAddr.cidr_compare(subnet, ip))
          end
        end

        # A memoized list of trusted subnets
        # @return [Array<NetAddr::CIDR>]
        def trusted_subnets
          @trusted_subnets ||= Twitch::Audit::Middleware.config.trusted_subnets.collect do |subnet|
            subnet.is_a?(NetAddr::CIDR) ? subnet : NetAddr::CIDR.create(subnet)
          end
        end
      end
    end
  end
end
