# frozen_string_literal: true

module WireGuard
  # the class dumps the resulting configuration file
  # and reboots the WireGuard server
  # This class updates the config file of the server itself and not the clients.
  # And it is needed mainly to reboot the server after adding new clients (or deleting them)
  class ServerConfigUpdater
    WG_CONF_PATH = "#{Settings.wg_path}/wg0.conf".freeze
    WG_PORT = Settings.wg_port
    WG_PRE_UP = Settings.wg_pre_up
    WG_PRE_DOWN = Settings.wg_pre_down
    WG_DEFAULT_ADDRESS = Settings.wg_default_address
    WG_DEFAULT_ADDRESS_6 = Settings.wg_default_address_6
    WG_DEVICE = Settings.wg_device
    CONNECTING_CLIENT_LIMIT = Settings.connecting_client_limit
    CONNECTING_CLIENT_LIMIT_6 = Settings.connecting_client_limit_6
    WG_POST_UP = Settings.wg_post_up
    WG_POST_DOWN = Settings.wg_post_down

    def initialize
      @json_config = JSON.parse(File.read(WireGuard::Server::WG_JSON_PATH))
      @first_start = !File.exist?(WG_CONF_PATH)
    end

    def self.update
      new.update
    end

    def update
      new_config_build = []
      new_config_build << base_config

      # TODO: Remove 'last_address' in future versions
      json_config['configs'].except('last_id', 'last_address').each_value do |config|
        # NOTE: We simply skip the config and do not add it to the initial configuration,
        # if the 'enable == false'
        next if config['enable'] == false

        new_config_build << build_client(config)
      end

      dump_wireguard_config(new_config_build)
      start_server
    end

    private

    attr_reader :json_config, :first_start

    def start_server
      if first_start
        Kernel.system('wg-quick up wg0')
      else
        Kernel.system('wg syncconf wg0 <(wg-quick strip wg0)')
      end
    end

    def dump_wireguard_config(new_config_build)
      File.write(WG_CONF_PATH, new_config_build.join("\n"))
    end

    def build_client(config)
      <<~TEXT
        # Client ID: #{config['id']}
        [Peer]
        PublicKey = #{config['public_key']}
        PresharedKey = #{config['preshared_key']}
        AllowedIPs = #{config['address']}/32, #{config['address_ipv6']}/128
      TEXT
    end

    def wg_post_up # rubocop:disable Metrics/MethodLength
      return WG_POST_UP unless WG_POST_UP.nil?

      "iptables -t nat -A POSTROUTING -s #{WG_DEFAULT_ADDRESS.gsub('x', '0')}/#{CONNECTING_CLIENT_LIMIT} " \
        "-o #{WG_DEVICE} -j MASQUERADE; " \
        "iptables -A INPUT -p udp -m udp --dport #{WG_PORT} -j ACCEPT; " \
        'iptables -A FORWARD -i wg0 -j ACCEPT; ' \
        'iptables -A FORWARD -o wg0 -j ACCEPT; ' \
        "ip6tables -t nat -A POSTROUTING -s #{WG_DEFAULT_ADDRESS_6.gsub('x', '0')}/#{CONNECTING_CLIENT_LIMIT_6} " \
        "-o #{WG_DEVICE} -j MASQUERADE; " \
        "ip6tables -A INPUT -p udp -m udp --dport #{WG_PORT} -j ACCEPT; " \
        'ip6tables -A FORWARD -i wg0 -j ACCEPT; ' \
        'ip6tables -A FORWARD -o wg0 -j ACCEPT;' \
    end

    def wg_post_down # rubocop:disable Metrics/MethodLength
      return WG_POST_DOWN unless WG_POST_DOWN.nil?

      "iptables -t nat -D POSTROUTING -s #{WG_DEFAULT_ADDRESS.gsub('x', '0')}/#{CONNECTING_CLIENT_LIMIT} " \
        "-o #{WG_DEVICE} -j MASQUERADE; " \
        "iptables -D INPUT -p udp -m udp --dport #{WG_PORT} -j ACCEPT; " \
        'iptables -D FORWARD -i wg0 -j ACCEPT; ' \
        'iptables -D FORWARD -o wg0 -j ACCEPT; ' \
        "ip6tables -t nat -D POSTROUTING -s #{WG_DEFAULT_ADDRESS_6.gsub('x', '0')}/#{CONNECTING_CLIENT_LIMIT_6} " \
        "-o #{WG_DEVICE} -j MASQUERADE; " \
        "ip6tables -D INPUT -p udp -m udp --dport #{WG_PORT} -j ACCEPT; " \
        'ip6tables -D FORWARD -i wg0 -j ACCEPT; ' \
        'ip6tables -D FORWARD -o wg0 -j ACCEPT;'
    end

    def base_config
      <<~TEXT.freeze
        # Note: Do not edit this file directly.
        # Your changes will be overwritten!

        # Server
        [Interface]
        PrivateKey = #{json_config['server']['private_key']}
        Address = #{json_config['server']['address']}/#{CONNECTING_CLIENT_LIMIT}, #{json_config['server']['address_ipv6']}/#{CONNECTING_CLIENT_LIMIT_6}
        ListenPort = #{WG_PORT}
        PreUp = #{WG_PRE_UP}
        PostUp = #{wg_post_up}
        PreDown = #{WG_PRE_DOWN}
        PostDown = #{wg_post_down}
      TEXT
    end
  end
end
