# frozen_string_literal: true

require 'digest/sha1'
require 'fspath'
require 'image_optim/cache_path'

class ImageOptim
  # Handles image cache
  class Cache
    def initialize(image_optim, workers_by_format)
      return unless image_optim.cache_dir

      @cache_dir = FSPath.new(image_optim.cache_dir)
      @cache_worker_digests = image_optim.cache_worker_digests
      @options_by_format = Hash[workers_by_format.map do |format, workers|
        [format, workers.map(&:inspect).sort.join(', ')]
      end]
      @bins_by_format = Hash[workers_by_format.map do |format, workers|
        [format, workers.map(&:used_bins).flatten!.map! do |sym|
          bin = image_optim.resolve_bin!(sym)
          "#{bin.name}[#{bin.digest}]"
        end.sort!.uniq.join(', ')]
      end]
      @global_options = begin
        options = {}
        options[:timeout] = image_optim.timeout if image_optim.timeout
        options.empty? ? '' : options.inspect
      end
    end

    def fetch(original)
      return yield unless @cache_dir

      digest = digest(original, original.image_format)
      cached = @cache_dir / digest
      return cached.size? && CachePath.convert(cached) if cached.file?

      optimized = yield

      cached.dirname.mkpath

      if optimized
        tmp = FSPath.temp_file_path(digest, @cache_dir)
        FileUtils.mv(optimized, tmp)
        tmp.chmod(~File.umask & 0o666)
        tmp.rename(cached)
        cached_path = CachePath.convert(cached)

        # mark cached image as already optimized
        cached = @cache_dir / digest(cached, original.image_format)
        cached.dirname.mkpath
        FileUtils.touch(cached)

        cached_path
      else
        # mark image as already optimized
        FileUtils.touch(cached)
        nil
      end
    end

  private

    def options_by_format(format)
      @options_by_format[format]
    end

    def bins_by_format(format)
      @bins_by_format[format]
    end

    def digest(path, format)
      digest = Digest::SHA1.file(path)
      digest.update options_by_format(format)
      digest.update bins_by_format(format) if @cache_worker_digests
      digest.update @global_options
      s = digest.hexdigest
      "#{s[0..1]}/#{s[2..-1]}"
    end
  end
end
