# typed: strict
# frozen_string_literal: true

module Utils
  # Helper functions for creating gzip files.
  module Gzip
    # Apple's gzip also uses zlib so use the same buffer size here.
    # https://github.com/apple-oss-distributions/file_cmds/blob/file_cmds-400/gzip/gzip.c#L147
    GZIP_BUFFER_SIZE = T.let(64 * 1024, Integer)

    sig {
      params(
        path:      T.any(String, Pathname),
        mtime:     T.any(Integer, Time),
        orig_name: String,
        output:    T.any(String, Pathname),
      ).returns(Pathname)
    }
    def self.compress_with_options(path, mtime: ENV["SOURCE_DATE_EPOCH"].to_i, orig_name: File.basename(path),
                                   output: "#{path}.gz")
      # Ideally, we would just set mtime = 0 if SOURCE_DATE_EPOCH is absent, but Ruby's
      # Zlib::GzipWriter does not properly handle the case of setting mtime = 0:
      # https://bugs.ruby-lang.org/issues/16285
      #
      # This was fixed in https://github.com/ruby/zlib/pull/10. Remove workaround
      # once we are using zlib gem version 1.1.0 or newer.
      if mtime.to_i.zero?
        odebug "Setting `mtime = 1` to avoid zlib gem bug when `mtime == 0`."
        mtime = 1
      end

      File.open(path, "rb") do |fp|
        odebug "Creating gzip file at #{output}"
        gz = Zlib::GzipWriter.open(output)
        gz.mtime = mtime
        gz.orig_name = orig_name
        gz.write(fp.read(GZIP_BUFFER_SIZE)) until fp.eof?
      ensure
        # GzipWriter should be closed in case of error as well
        gz.close
      end

      FileUtils.rm_f path
      Pathname.new(output)
    end

    sig {
      params(
        paths:        T.any(String, Pathname),
        reproducible: T::Boolean,
        mtime:        T.any(Integer, Time),
      ).returns(T::Array[Pathname])
    }
    def self.compress(*paths, reproducible: true, mtime: ENV["SOURCE_DATE_EPOCH"].to_i)
      if reproducible
        paths.map do |path|
          compress_with_options(path, mtime:)
        end
      else
        paths.map do |path|
          safe_system "gzip", path
          Pathname.new("#{path}.gz")
        end
      end
    end
  end
end
