# assumes you've got roles, deploy_to, rails_env

require 'pp'
require 'active_support'
require 'zlib'

# basically { :a => 1, :b => 2 }.eql?({ :b => 2, :a => 1}) => true
class StrictComparableHash < Hash
  def eql?(other)
    self == other
  end

  # must return an integer or #eql? will be ignored by Comparable
  # assumes hash elements are strings
  # will break if a value is something else whose to_s varies randomly
  def hash
    Zlib.crc32(to_a.map(&:to_s).sort.to_s)
  end
end

# Hash.from_xml(a.gsub(/\n+/, ''))['hash']

namespace :s do
  %w{ c d on off clear reset backup restore default }.each do |cmd|
    task cmd.to_sym, :roles => :app do
      upload File.join(File.dirname(__FILE__), '..', '..', 'lib', 'tasks', 'switches.rake'), File.join(deploy_to, 'current', 'lib', 'tasks', 'switches.rake')
      
      # download switches xml from servers
      raw_input = Hash.new
      run "cd #{deploy_to}/current; rake --silent s:#{cmd}#{"[#{ENV['ARG']}]" if ENV['ARG'].present?} SWITCHES_XML=true RAILS_ENV=#{rails_env}; true" do |channel, stream, data|
        server = channel[:server]
        server_identifier = gfs ? server.host : "#{server.host}:#{server.port}".chomp(':')
        raw_input[server_identifier] ||= Array.new
        raw_input[server_identifier] << data
      end
      
      grouped_by_server = raw_input.inject(Hash.new) do |memo, ary|
        server_identifier, chunks = ary
        server_hash = Hash.from_xml(chunks.join.gsub(/\n+/, ''))['hash']
        memo[server_identifier] = server_hash.is_a?(Hash) ? StrictComparableHash[server_hash] : StrictComparableHash.new
        memo
      end
      
      first_server_switches = grouped_by_server.values.first

      # If GFS, all server switches will be the same---just output them
      if gfs
        pp first_server_switches
      else
        grouped_by_switches = grouped_by_server.inject(Hash.new) do |memo, ary|
          server_identifier, comparable_switches = ary
          memo[comparable_switches] ||= Array.new
          memo[comparable_switches] << server_identifier
          memo
        end
        if grouped_by_switches.keys.length > 1
          puts "Servers are different"
          pp grouped_by_switches.invert
          if grouped_by_switches.length == 2
            a, b = grouped_by_switches.invert.values
            puts
            puts "Difference is"
            pp a.diff(b)
          end
        else
          puts "Servers are the same"
          pp first_server_switches
        end
      end
    end
  end
end
