require ‘digest/md5’

module Sprout

class RemoteFileTarget < FileTarget

  attr_accessor :archive_type
  attr_accessor :url
  attr_accessor :md5

  def validate
    super
    raise Sprout::Errors::ValidationError.new "RemoteFileTarget.url is a required field" if url.nil?
    raise Sprout::Errors::ValidationError.new "RemoteFileTarget.md5 is a required field" if md5.nil?
    raise Sprout::Errors::ValidationError.new "RemoteFileTarget.archive_type is a required field" if archive_type.nil?
  end

  def resolve
    validate
    load_unpack_or_ignore_archive
    self
  end

  ##
  # Do not cache this value...
  #
  # This response can change over time IF:
  #  - The downloaded bytes do not match the expected MD5
  #  - AND the user confirms the prompt that they are OK with this
  def downloaded_file
    File.join(Sprout.cache, pkg_name, "#{md5}.#{archive_type}")
  end

  def unpacked_file
    upcased_pkg = pkg_name.upcase
    upcased_version = pkg_version.upcase.gsub /\./, '_'
      ENV["SPROUT_#{upcased_pkg}_#{upcased_version}"] ||
      ENV["SPROUT_#{upcased_pkg}"] ||
      ENV["#{upcased_pkg}_#{upcased_version}"] ||
      ENV[upcased_pkg] ||
      File.join(Sprout.cache, pkg_name, pkg_version)
  end

  protected

  def logger
    Sprout::Log
  end

  def expand_local_path path
    File.join unpacked_file, path
  end

  private

  def load_unpack_or_ignore_archive
    if(!unpacked_files_exist?)
      if(!File.exists?(downloaded_file))
        bytes = download_archive
        write_archive bytes
      end

      # If we *just* downloaded the file,
      # use the bytes directly, otherwise
      # read them off disk from a previous
      # download attempt:
      bytes ||= File.open(downloaded_file, 'rb').read

      if should_unpack?(bytes, md5)
        unpack_archive
      end
    end
  end

  def unpacked_files_exist?
    File.exists?(unpacked_file) && !Dir.empty?(unpacked_file)
  end

  def download_archive
    Sprout::RemoteFileLoader.load url, pkg_name
  end

  def write_archive bytes
    FileUtils.mkdir_p File.dirname(downloaded_file)
    File.open downloaded_file, 'wb+' do |f|
      f.write bytes
    end
  end

  def should_unpack? bytes, expected_md5sum
    if expected_md5sum
      downloaded_md5 = Digest::MD5.new
      downloaded_md5 << bytes

      if(expected_md5sum != downloaded_md5.hexdigest)
        return prompt_for_md5_failure downloaded_md5, expected_md5sum
      end
    end
    return true
  end

  def prompt_for_md5_failure downloaded_md5, expected_md5sum
    puts "The MD5 Sum of the downloaded file (#{downloaded_md5.hexdigest}) does not match what was expected (#{expected_md5sum})."
    puts "Would you like to install anyway? [Yn]"
    user_response = $stdin.gets.chomp!
    if(user_response.downcase == 'y')
      return true
    else
      raise Sprout::Errors::RemoteFileLoaderError.new('MD5 Checksum failed')
    end
  end

  def unpack_archive
    logger.puts "Unpacking archive at #{downloaded_file} now. This can take anywhere from a few seconds to many minutes depending on your OS and the size of the archive.\n\nIf you're on windows, consider using this ample time to look into improving the zip utils in Ruby..."
    FileUtils.mkdir_p unpacked_file
    unpacker = Sprout::ArchiveUnpacker.new
    unpacker.unpack downloaded_file, unpacked_file, archive_type
  end

end

end