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.stdout
    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
