require "digest"

module Relinker
  class Discoverer

    attr_reader :options, :cache_file

    def initialize(cache_file, options = {})
      @cache_file = cache_file
      @options = options
    end

    def collect(base_dir)
      File.open(cache_file, "a+") do |cache|
        discover(base_dir) do |file, checksum|
          cache.write("#{checksum}$$$\t#{file}\n")
        end
      end
      resort_cache
    end

    def resort_cache
      system("cat #{cache_file} | sort | uniq | cat > #{cache_file}.tmp \
              && mv #{cache_file}.tmp #{cache_file}")
    end

    def discover(base_dir)
      children = Pathname.new(base_dir).children
      children.each do |child|
        next if child.symlink?
        if child.directory?
          discover(child) { |file| yield(file, checksum(file)) if regular_file?(file) }
        else
          yield(child, checksum(child)) if regular_file?(child)
        end
      end
    end

    def list(base_dir)
      files = []
      discover(base_dir) do |file, check|
        files << { path: file.to_s, checksum: check }
      end
      files
    end

    private

    def regular_file?(path)
      special_types = %i[symlink chardev blockdev socket pipe]
      special_type = special_types.find { |test| path.send("#{test}?") }
      path.file? && !special_type && !path.to_s.match(/:|pid$|swap$|lock$/)
    end

    def merge_options(options = {})
      options = options.map { |key, val| [key.to_sym, val] }.to_h
      default_options.merge(options)
    end

    def default_options
      { cache_dir: "/tmp" }
    end

    def checksum(file)
      Digest::MD5.file(file).hexdigest
    end
  end
end