module Itamae
  module Resource
    class GemPackage < Base
      define_attribute :action, default: :install
      define_attribute :package_name, type: String, default_name: true
      define_attribute :gem_binary, type: [String, Array], default: 'gem'
      define_attribute :options, type: [String, Array], default: []
      define_attribute :version, type: String
      define_attribute :source, type: String

      def pre_action
        case @current_action
        when :install
          attributes.installed = true
        when :uninstall
          attributes.installed = false
        end
      end

      def set_current_attributes
        installed = installed_gems.find {|g| g[:name] == attributes.package_name }
        current.installed = !!installed

        if current.installed
          versions = installed[:versions]
          if versions.include?(attributes.version)
            current.version = attributes.version
          else
            current.version = versions.first
          end
        end
      end

      def action_install(action_options)
        if current.installed
          if attributes.version && current.version != attributes.version
            install!
            updated!
          end
        else
          install!
          updated!
        end
      end

      def action_uninstall(action_options)
        uninstall! if current.installed
      end

      def action_upgrade(action_options)
        return if current.installed && attributes.version && current.version == attributes.version

        install!
        updated!
      end

      def installed_gems
        gems = []
        run_command([*Array(attributes.gem_binary), 'list', '-l']).stdout.each_line do |line|
          if /\A([^ ]+) \(([^\)]+)\)\z/ =~ line.strip
            name = $1
            versions = $2.split(', ')
            gems << {name: name, versions: versions}
          end
        end
        gems
      rescue Backend::CommandExecutionError
        []
      end

      def install!
        cmd = [*Array(attributes.gem_binary), 'install', *Array(attributes.options)]
        if attributes.version
          cmd << '-v' << attributes.version
        end
        if attributes.source
          cmd << '--source' << attributes.source
        end
        cmd << attributes.package_name

        run_command(cmd)
      end

      def uninstall!
        cmd = [*Array(attributes.gem_binary), 'uninstall', '--ignore-dependencies', '--executables', *Array(attributes.options)]
        if attributes.version
          cmd << '-v' << attributes.version
        else
          cmd << '--all'
        end
        cmd << attributes.package_name

        run_command(cmd)
      end
    end
  end
end
