module FirewallCookbook
  module Helpers
    module Windows
      include FirewallCookbook::Helpers
      include Chef::Mixin::ShellOut

      def fixup_cidr(str)
        newstr = str.clone
        newstr.gsub!('0.0.0.0/0', 'any') if newstr.include?('0.0.0.0/0')
        newstr.gsub!('/0', '') if newstr.include?('/0')
        newstr
      end

      def windows_rules_filename
        "#{ENV['HOME']}/windows-chef.rules"
      end

      def active?
        @active ||= begin
          cmd = shell_out!('netsh advfirewall show currentprofile')
          cmd.stdout =~ /^State\sON/
        end
      end

      def enable!
        shell_out!('netsh advfirewall set currentprofile state on')
      end

      def disable!
        shell_out!('netsh advfirewall set currentprofile state off')
      end

      def reset!
        shell_out!('netsh advfirewall reset')
      end

      def add_rule!(params)
        shell_out!("netsh advfirewall #{params}")
      end

      def delete_all_rules!
        shell_out!('netsh advfirewall firewall delete rule name=all')
      end

      def to_type(new_resource)
        cmd = new_resource.command
        if cmd == :reject || cmd == :deny
          :block
        else
          :allow
        end
      end

      def build_rule(new_resource)
        type = to_type(new_resource)
        parameters = {}

        parameters['description'] = "\"#{new_resource.description}\""
        parameters['dir'] = new_resource.direction

        new_resource.program && parameters['program'] = new_resource.program
        new_resource.service && parameters['service'] = new_resource.service
        parameters['protocol'] = new_resource.protocol

        if new_resource.direction.to_sym == :out
          parameters['localip'] = new_resource.source ? fixup_cidr(new_resource.source) : 'any'
          parameters['localport'] = new_resource.source_port ? port_to_s(new_resource.source_port) : 'any'
          parameters['interfacetype'] = new_resource.interface || 'any'
          parameters['remoteip'] = new_resource.destination ? fixup_cidr(new_resource.destination) : 'any'
          parameters['remoteport'] = new_resource.dest_port ? port_to_s(new_resource.dest_port) : 'any'
        else
          parameters['localip'] = new_resource.destination || 'any'
          parameters['localport'] = dport_calc(new_resource) ? port_to_s(dport_calc(new_resource)) : 'any'
          parameters['interfacetype'] = new_resource.dest_interface || 'any'
          parameters['remoteip'] = new_resource.source ? fixup_cidr(new_resource.source) : 'any'
          parameters['remoteport'] = new_resource.source_port ? port_to_s(new_resource.source_port) : 'any'
        end

        parameters['action'] = type.to_s

        partial_command = parameters.map { |k, v| "#{k}=#{v}" }.join(' ')
        "firewall add rule name=\"#{new_resource.name}\" #{partial_command}"
      end

      def rule_exists?(name)
        @exists ||= begin
          cmd = shell_out!("netsh advfirewall firewall show rule name=\"#{name}\"", returns: [0, 1])
          cmd.stdout !~ /^No rules match the specified criteria/
        end
      end

      def show_all_rules!
        cmd = shell_out!('netsh advfirewall firewall show rule name=all')
        cmd.stdout.each_line do |line|
          Chef::Log.warn(line)
        end
      end

      def rule_up_to_date?(name, type)
        @up_to_date ||= begin
          desired_parameters = rule_parameters(type)
          current_parameters = {}

          cmd = shell_out!("netsh advfirewall firewall show rule name=\"#{name}\" verbose")
          cmd.stdout.each_line do |line|
            current_parameters['description'] = "\"#{Regexp.last_match(1).chomp}\"" if line =~ /^Description:\s+(.*)$/
            current_parameters['dir'] = Regexp.last_match(1).chomp if line =~ /^Direction:\s+(.*)$/
            current_parameters['program'] = Regexp.last_match(1).chomp if line =~ /^Program:\s+(.*)$/
            current_parameters['service'] = Regexp.last_match(1).chomp if line =~ /^Service:\s+(.*)$/
            current_parameters['protocol'] = Regexp.last_match(1).chomp if line =~ /^Protocol:\s+(.*)$/
            current_parameters['localip'] = Regexp.last_match(1).chomp if line =~ /^LocalIP:\s+(.*)$/
            current_parameters['localport'] = Regexp.last_match(1).chomp if line =~ /^LocalPort:\s+(.*)$/
            current_parameters['interfacetype'] = Regexp.last_match(1).chomp if line =~ /^InterfaceTypes:\s+(.*)$/
            current_parameters['remoteip'] = Regexp.last_match(1).chomp if line =~ /^RemoteIP:\s+(.*)$/
            current_parameters['remoteport'] = Regexp.last_match(1).chomp if line =~ /^RemotePort:\s+(.*)$/
            current_parameters['action'] = Regexp.last_match(1).chomp if line =~ /^Action:\s+(.*)$/
          end

          up_to_date = true
          desired_parameters.each do |k, v|
            up_to_date = false if current_parameters[k] !~ /^["]?#{v}["]?$/i
          end

          up_to_date
        end
      end
    end
  end
end
