# typed: true
# frozen_string_literal: true

require "fileutils"
require "cask/cache"
require "cask/quarantine"

module Cask
  # A download corresponding to a {Cask}.
  #
  # @api private
  class Download
    include Context

    attr_reader :cask

    def initialize(cask, quarantine: nil)
      @cask = cask
      @quarantine = quarantine
    end

    def fetch(quiet: nil, verify_download_integrity: true, timeout: nil)
      downloaded_path = begin
        downloader.shutup! if quiet
        downloader.fetch(timeout: timeout)
        downloader.cached_location
      rescue => e
        error = CaskError.new("Download failed on Cask '#{cask}' with message: #{e}")
        error.set_backtrace e.backtrace
        raise error
      end
      quarantine(downloaded_path)
      self.verify_download_integrity(downloaded_path) if verify_download_integrity
      downloaded_path
    end

    def downloader
      @downloader ||= begin
        strategy = DownloadStrategyDetector.detect(cask.url.to_s, cask.url.using)
        strategy.new(cask.url.to_s, cask.token, cask.version, cache: Cache.path, **cask.url.specs)
      end
    end

    def time_file_size(timeout: nil)
      downloader.resolved_time_file_size(timeout: timeout)
    end

    def clear_cache
      downloader.clear_cache
    end

    def cached_download
      downloader.cached_location
    end

    def basename
      downloader.basename
    end

    def verify_download_integrity(fn)
      if @cask.sha256 == :no_check
        opoo "No checksum defined for cask '#{@cask}', skipping verification."
        return
      end

      begin
        ohai "Verifying checksum for cask '#{@cask}'" if verbose?
        fn.verify_checksum(@cask.sha256)
      rescue ChecksumMissingError
        opoo <<~EOS
          Cannot verify integrity of '#{fn.basename}'.
          No checksum was provided for this cask.
          For your reference, the checksum is:
            sha256 "#{fn.sha256}"
        EOS
      end
    end

    private

    def quarantine(path)
      return if @quarantine.nil?
      return unless Quarantine.available?

      if @quarantine
        Quarantine.cask!(cask: @cask, download_path: path)
      else
        Quarantine.release!(download_path: path)
      end
    end
  end
end
