#!/usr/bin/env ruby
#
# audit_modified_casks
#

###
### dependencies
###

require 'pathname'
require 'open3'

$LOAD_PATH.unshift(Pathname(__FILE__).realpath.join('../../../lib'))

require 'vendor/homebrew-fork/global'
require 'hbc'

###
### constants
###

CASK_DIR = Pathname(__FILE__).realpath.join('../../../Casks')
RELEVANT_STANZAS = %i{version sha256 url}

###
### methods
###

def cd_to_project_root
  Dir.chdir Pathname(__FILE__).realpath.dirname
  @git_root ||= git(*%w[rev-parse --show-toplevel])
  Dir.chdir @git_root
end

def die(msg, status=1)
  onoe msg
  exit status
end

def main
  cd_to_project_root
  modified_cask_files.zip(modified_casks).each do |cask_file, cask|
    audit(cask, cask_file)
  end
  report_failures
end

def commit_range
  $commit_range
end

def modified_cask_files
  return @modified_cask_files if defined? @modified_cask_files
  out = git(*%w[diff --name-only --diff-filter=AM], commit_range,
            '--', CASK_DIR)
  @modified_cask_files = out.split("\n")
end

def modified_casks
  return @modified_casks if defined? @modified_casks
  @modified_casks = modified_cask_files.map { |f| Hbc.load(f) }
  if @modified_casks.any?
    num_modified = @modified_casks.size
    ohai "#{num_modified} modified #{pluralize('cask', num_modified)}: " \
      "#{@modified_casks.join(' ')}"
  end
  @modified_casks
end

def audit(cask, cask_file)
  audit_download = audit_download?(cask, cask_file)
  success = Hbc::Auditor.audit(cask, audit_download: audit_download)
  failed_casks << cask unless success
end

def failed_casks
  @failed_casks ||= []
end

def audit_download?(cask, cask_file)
  cask.sha256 != :no_check && relevant_stanza_modified?(cask_file)
end

def relevant_stanza_modified?(cask_file)
  out = git('diff', commit_range, '--', cask_file)
  out =~ /^\+\s*(#{RELEVANT_STANZAS.join('|')})/
end

def git(*args)
  ohai ['git', *args].join(' ') if $debug
  out, err, status = Open3.capture3('git', *args)
  return out.chomp if status.success?
  die err.chomp, status.exitstatus
end

def report_failures
  return if failed_casks.empty?
  num_failed = failed_casks.size
  cask_pluralized = pluralize('cask', num_failed)
  die "audit failed for #{num_failed} #{cask_pluralized}: " \
    "#{failed_casks.join(' ')}"
end

def pluralize(str, num)
  num == 1 ?  str : "#{str}s"
end

def cleanup
  Hbc::CLI::Cleanup.run if $clean_cache
end

###
### main
###

usage = <<-EOS
Usage: audit_modified_casks [options...] <commit range>

Given a range of Git commits, find any Casks that were modified and run `brew
cask audit' on them. If the `url', `version', or `sha256' stanzas were modified,
run with the `--download' flag to verify the hash.

Options:
  -c, --clean-cache
    Remove all cached downloads. Use with care.
  -d, --debug
    Enable debugging output.
  -h, --help
    Display usage and exit.
EOS

while ARGV.any? do
  case ARGV.first
  when /^-+h(elp)?$/i
    puts usage
    exit 0
  when /^-+d(ebug)?$/i
    $debug = 1
    ARGV.shift
  when /^-+c(lean-cache)?$/i
    $clean_cache = 1
    ARGV.shift
  else
    die usage if $commit_range
    $commit_range = ARGV.shift
  end
end

die usage unless $commit_range

at_exit { cleanup }

main
