# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: BUSL-1.1

require "log4r"

module VagrantPlugins
  module ProviderVirtualBox
    module Action
      # This sets the default NIC type used for network adapters created
      # on the guest. Also includes a check of NIC types in use and VirtualBox
      # version to determine if E1000 NIC types are vulnerable.
      #
      # NOTE: Vulnerability was fixed here: https://www.virtualbox.org/changeset/75330/vbox
      class SetDefaultNICType
        # Defines versions of VirtualBox with susceptible implementation
        # of the E1000 devices.
        E1000_SUSCEPTIBLE = Gem::Requirement.new("< 5.2.22").freeze

        def initialize(app, env)
          @logger = Log4r::Logger.new("vagrant::plugins::virtualbox::set_default_nic_type")
          @app    = app
        end

        def call(env)
          default_nic_type = env[:machine].provider_config.default_nic_type

          e1000_in_use = [
            # simple check on default_nic_type
            ->{ default_nic_type.nil? || default_nic_type.to_s.start_with?("8254") },
            # check provider defined adapters
            ->{ env[:machine].provider_config.network_adapters.values.detect{ |_, opts|
                opts[:nic_type].to_s.start_with?("8254") } },
            # finish with inspecting configured networks
            ->{ env[:machine].config.vm.networks.detect{ |_, opts|
                opts.fetch(:virtualbox__nic_type, opts[:nic_type]).to_s.start_with?("8254") } }
          ]

          # Check if VirtualBox E1000 implementation is vulnerable
          if E1000_SUSCEPTIBLE.satisfied_by?(Gem::Version.new(env[:machine].provider.driver.version))
            @logger.info("Detected VirtualBox version with susceptible E1000 implementation (`#{E1000_SUSCEPTIBLE}`)")
            if e1000_in_use.any?(&:call)
              env[:ui].warn I18n.t("vagrant.actions.vm.set_default_nic_type.e1000_warning")
            end
          end

          if default_nic_type
            @logger.info("Default NIC type for VirtualBox interfaces `#{default_nic_type}`")
            # Update network adapters defined in provider configuration
            env[:machine].provider_config.network_adapters.each do |slot, args|
              _, opts = args
              if opts && !opts.key?(:nic_type)
                @logger.info("Setting default NIC type (`#{default_nic_type}`) adapter `#{slot}` - `#{args}`")
                opts[:nic_type] = default_nic_type
              end
            end

            # Update generally defined networks
            env[:machine].config.vm.networks.each do |type, options|
              next if !type.to_s.end_with?("_network")
              if !options.key?(:nic_type) && !options.key?(:virtualbox__nic_type)
                @logger.info("Setting default NIC type (`#{default_nic_type}`) for `#{type}` - `#{options}`")
                options[:virtualbox__nic_type] = default_nic_type
              end
            end
          end

          @app.call(env)
        end
      end
    end
  end
end
