include Chef::Mixin::ShellOut
require 'plist'

module MacOS
  class CommandLineTools
    attr_reader :version

    def initialize
      @version = if install_receipts.empty?
                   latest_from_catalog
                 else
                   latest_receipt
                 end
    end

    def install_receipts
      packages = Plist.parse_xml(install_history_plist)
      packages.select { |package| package['displayName'].match? 'Command Line Tools' }
    end

    def latest_receipt
      [install_receipts.last['displayName'], install_receipts.last['displayVersion']].join '-'
    end

    def latest_from_catalog
      if all_available.empty?
        Chef::Log.warn 'No Command Line Tools available from Software Update Catalog!'
      else
        catalog_recommendation_label.tr('*', '').gsub('Label: ', '').strip
      end
    end

    def catalog_recommendation_label
      if platform_specific.empty?
        Chef::Log.info "No Command Line Tools specific to #{macos_version} available from Software Update Catalog, selecting by maximum Xcode version."
        Chef::Log.info 'Command Line Tools version will be selected using maximum Xcode version.'
        recommend_version(all_available)
      else
        recommend_version(platform_specific)
      end
    end

    def recommend_version(software_update_package_list)
      versions = software_update_package_list.map { |product| Gem::Version.new xcode_version(product) }
      software_update_package_list.detect { |product| product.include? versions.max.version }
    end

    def all_available
      softwareupdate_list.select { |product_name| product_name.match(/\*.{1,8}Command Line Tools/) }
    end

    def platform_specific
      all_available.select { |product_name| product_name.include? macos_version }
    end

    def enable_install_on_demand
      install_demand = '/tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress'
      FileUtils.touch install_demand
      FileUtils.chown 'root', 'wheel', install_demand
    end

    def xcode_version(product)
      product.match(/Xcode.{0,20}-(?<version>\d+\.\d+(?:\.\d+)?)/)['version']
    end

    def macos_version
      shell_out(['/usr/bin/sw_vers', '-productVersion']).stdout.chomp[/[\d][\d]\.\d+/]
    end

    def softwareupdate_list
      shell_out(['softwareupdate', '--list']).stdout.lines
    end

    def install_history_plist
      File.open('/Library/Receipts/InstallHistory.plist', 'r')
    end
  end
end

Chef::DSL::Recipe.include(MacOS)
Chef::Resource.include(MacOS)
