module Helpers
  PUBLIC_KEY_PATH = File.dirname(__FILE__) + '/gpg.public'
  PRIVATE_KEY_PATH = File.dirname(__FILE__) + '/gpg.sekrit'
  PUBLIC_KEY_ID = '4D5AA1CE74A97ADB'

  def run_before
    read_keys

    FakeWeb.clean_registry
    FakeWeb.passthrough_uri(:any, %r{^https?://s3.amazonaws.com/})

    FakeWeb.register_uri(:get, "http://169.254.169.254/latest/meta-data/local-hostname", :body => "localhost")
    FakeWeb.register_uri(:get, "http://169.254.169.254/latest/meta-data/instance-id", :body => "i-awesome")

    FakeWeb.register_uri(:get, %r{https://ec2.amazonaws.com/.*}, :status => 200)
    @mock_environment_name = 'myenv'

    FakeWeb.register_uri(:post, "http://example.org/api/environments", :body => JSON.generate({
      @mock_environment_name => {:id => 1}
    }))
    
    create_or_clean_dir(tmp_dir)
    create_or_clean_dir(log_dir)

    # FileUtils.mkdir_p(tmp_dir)
    # FileUtils.mkdir_p(log_dir)
    # Dir.glob("#{tmp_dir}/*").each do |f|
    #   FileUtils.rm(f)
    # end
    stub_configs
  end
  
  def create_or_clean_dir(dir)
    FileUtils.mkdir_p(dir)
    Dir.glob("#{dir}/*").each do |f|
      FileUtils.rm(f)
    end
  end

  def stub_configs
    #print "in stub_configs: #{YAML::dump(@database_config)}, #{YAML::dump(spec_config)}\n"
    config = @database_config || spec_config
    config = config.merge({ :tmp_dir => tmp_dir, :log_dir => log_dir })
    # print "in stub_configs2: #{YAML::dump(config)}\n"
    File.open(backup_config_file, "w") do |f|
      f.puts YAML.dump(config )
    end
  end

  def import_gpg
    system("gpg --import #{PUBLIC_KEY_PATH}") || raise("Could not import public key")
  end

  def write_keys(public_key, private_key)
    File.open('gpg.public', 'w') do |f|
      f << public_key
    end

    File.open('gpg.sekrit', 'w') do |f|
      f << private_key
    end
  end

  def read_keys
    return File.read(PUBLIC_KEY_PATH), File.read(PRIVATE_KEY_PATH)
  end

  def run_after(skip_file_remove = false)
    FileUtils.rm_f(backup_config_file)
    filenames = Dir.glob("#{tmp_dir}/*")
    if filenames.any?
      raise "failed to remove #{filenames.join(', ')}"
    end
  end

  def teardown_dna()
    FileUtils.rm_f("#{tmp_dir}/chef/dna.json")
    FileUtils.rm_r("#{tmp_dir}/chef")
  end

  def setup_dna(data)
    FileUtils.mkdir_p("#{tmp_dir}/chef")
    if File.exist?("#{tmp_dir}/chef/dna.json")
      FileUtils.rm("#{tmp_dir}/chef/dna.json")
    end
    File.open("#{tmp_dir}/chef/dna.json", "w") do |f|
      f.puts data.to_json
    end
  end

  def backup_config_file
    "#{tmp_dir}/spec_backups.yml"
  end
  
  def mk_tmp
    FileUtils.mkdir_p("/tmp")
  end

  def mysql_password_option
    mysql_password.nil? ? "" : "-p#{mysql_password}"
  end

  def generate_database_name(type)
    "ey_flex_#{type}_db_#{/\w{5,7}/.gen}".downcase
  end

  def create_mysql_database(db_key, region = 'us-east-1')
    db_name = generate_database_name('mysql')
    system(%Q{mysql -u#{mysql_user} -h#{mysql_host} #{mysql_password_option} -e "drop database if exists #{db_name};"})
    system(%Q{mysql -u#{mysql_user} -h#{mysql_host} #{mysql_password_option} -e "create database #{db_name};"}) || raise("Could not create db: #{db_name}")
    created_mysql_dbs[db_key] = db_name
    puts "*** MySQL DB Created: #{db_name}"
    write_database_config('mysql', mysql_user, mysql_password, mysql_host, created_mysql_dbs.values, region)
    db_name
  end
  
  def drop_mysql_database(db_name)
    command = %Q{mysql -u#{mysql_user} -h#{mysql_host} #{mysql_password_option} -e "drop database if exists #{db_name};"}
    puts "*** MySQL Drop Command: #{command}"
    system(command)
  end

  def drop_postgresql_database(db_name)
    command = %Q{ PGPASSWORD='#{postgresql_password}' dropdb -U #{postgresql_user} -h #{postgresql_host} #{db_name} }
    puts "Postgres Drop Command: #{command}"
    system(command)
  end

  def check_postgresql_database(db_name)
    command = %Q{ PGPASSWORD='#{postgresql_password}' psql -l -U #{postgresql_user} -h #{postgresql_host} | grep '#{db_name}' }
    system(command) || raise("Could not find db: #{db_name} with command:\n #{command}")
    db_name
  end

  def create_postgresql_database(db_key, region = 'us-east-1')
    db_name = generate_database_name('postgresql')
    created_postgresql_dbs[db_key] = db_name
    drop_postgresql_database(db_name)
    command = %Q{ PGPASSWORD='#{postgresql_password}' createdb -U #{postgresql_user} -h #{postgresql_host} #{db_name} }
    system(command) || raise("Could not create db: #{db_name} with command:\n #{command}")
    write_database_config('postgresql', postgresql_user, postgresql_password, postgresql_host, created_postgresql_dbs.values, region)
    db_name
  end


  def write_database_config(type, root_user, root_password, host, databases, region)
    @database_config = {
      :env            => 'production',
      :dbuser         => root_user,
      :dbpass         => root_password,
      :dbhost         => host,
      :aws_secret_id  => spec_config['aws_secret_id'] || spec_config[:aws_secret_id],
      :aws_secret_key => spec_config['aws_secret_key'] || spec_config[:aws_secret_key],
      :databases      => databases,
      :keep           => keep,
      :region         => region,
      :backup_bucket  => "ey-backup-test-#{region}",
    }
    stub_configs
  end

  def set_keep(keep)
    @keep = keep
  end

  def keep
    @keep || 5
  end

  def run_sql(command, database)
    system("mysql -u#{mysql_user} -h#{mysql_host} #{mysql_password_option} -D#{database} -e '#{command}'")
  end

  def run_sql_pipe(io, database)
    pid, stdin, _, stderr = Open4.popen4("mysql -u#{mysql_user} -h#{mysql_host} #{mysql_password_option} -D #{database}")

    stdin << io.read
    stdin.flush
    stdin.close

    _, status = Process::waitpid2 pid

    status
  end

  def run_psql(command, database)
    system("PGPASSWORD='#{postgresql_password}' psql -c '#{command}' -h #{postgresql_host} -U #{postgresql_user} #{database}")
  end

  def tmp_dir
    if spec_config['tmp_dir'].nil?
      File.dirname(__FILE__) + "/tmp/"
    else
      spec_config['tmp_dir'] || File.dirname("./tmp/")
    end
  end
  
  def log_dir
    if spec_config['log_dir'].nil?
      File.dirname(__FILE__) + "/log/"
    else
      spec_config['log_dir'] || File.dirname("./log/")
    end
  end

  def mysql_user
    spec_config['mysql_user'] || "root"
  end

  def postgresql_user
    spec_config['postgresql_user'] || "postgres"
  end

  def mysql_password
    spec_config['mysql_password']
  end

  def postgresql_password
    spec_config['postgresql_password']
  end

  def mysql_host
    spec_config['mysql_host'] || '127.0.0.1'
  end

  def postgresql_host
    spec_config['postgresql_host'] || '127.0.0.1'
  end

  def created_mysql_dbs
    @created_mysql_dbs ||= {}
  end

  def created_postgresql_dbs
    @created_postgresql_dbs ||= {}
  end

  def spec_config
    Helpers.spec_config
  end

  def self.spec_config
    @spec_config
  end

  def self.load_spec_config
    filename = File.dirname(__FILE__) + '/config.yml'

    if File.exist?(filename)
      @spec_config = YAML.load_file(filename)
    elsif Fog.mocking?
      @spec_config = {
        'aws_secret_id'  => SecureRandom.base64(10),
        'aws_secret_key' => SecureRandom.base64(20),
      }
    else
      abort "Please setup your spec/config.yml file"
    end
  end

  def last_stdout
    @last_stdout || raise("Run a command with `capturing_stdio' before checking `last_stdout'")
  end

  def last_stderr
    @last_stderr || raise("Run a command with `capturing_stdio' before checking `last_stderr'")
  end

  def last_exit_code
    @last_exit_code || raise("Run a command with `capturing_stdio' before checking `last_exit_code'")
  end

  def io_logger
    @io_logger ||= Logger.new(RealFile.dirname(__FILE__) + '/../tmp/io.log')
  end

  def capturing_stdio(&block)
    io_logger.debug "beginning to capture IO"
    @last_stdout, @last_stderr = "", ""
    old_stdout, old_stderr = $stdout, $stderr
    new_stdout, new_stderr = StringIO.new(@last_stdout), StringIO.new(@last_stderr)
    $stdout, $stderr = new_stdout, new_stderr
    yield
    exit 0
  rescue SystemExit => e
    @last_exit_code = e.status
    [new_stdout.string.split("\n"), new_stderr.string.split("\n")]
  ensure
    io_logger.debug "stdout: \n#{new_stdout.string}"
    io_logger.debug "stderr: \n#{new_stderr.string}"
    io_logger.debug "finished capturing IO"
    $stdout, $stderr = old_stdout, old_stderr
  end

  def reset_logger
    EY::Backup.logger = EY::Backup::Logger.new(StringIO.new, StringIO.new)
  end

  def stdout
    EY::Backup.logger.stdout.string
  end
  
  def stderr
    EY::Backup.logger.stderr.string
  end

  load_spec_config
end