# frozen_string_literal: true

require "net/http"
require "json"

module SmartTodo
  module Events
    # An event that check if a new version of gem has been released on RubyGem
    # with the expected version specifiers.
    # This event will make an API call to the RubyGem API
    class GemRelease
      # @param gem_name [String]
      # @param requirements [Array] a list of version specifiers.
      #   The specifiers are the same as the one used in Gemfiles or Gemspecs
      #
      # @example Expecting a specific version
      #   GemRelease.new('rails', ['6.0'])
      #
      # @example Expecting a version in the 5.x.x series
      #   GemRelease.new('rails', ['> 5.2', '< 6'])
      def initialize(gem_name, requirements)
        @gem_name = gem_name
        @requirements = Gem::Requirement.new(requirements)
      end

      # @return [String, false]
      def met?
        response = client.get("/api/v1/versions/#{@gem_name}.json")

        if response.code_type < Net::HTTPClientError
          error_message
        elsif (gem = version_released?(response.body))
          message(gem["number"])
        else
          false
        end
      end

      # Error message send to Slack in case a gem couldn't be found
      #
      # @return [String]
      def error_message
        "The gem *#{@gem_name}* doesn't seem to exist, I can't determine if your TODO is ready to be addressed."
      end

      # @return [String]
      def message(version_number)
        "The gem *#{@gem_name}* was released to version *#{version_number}* and your TODO is now ready to be addressed."
      end

      private

      # @param gem_versions [String] the response sent from RubyGems
      # @return [true, false]
      def version_released?(gem_versions)
        JSON.parse(gem_versions).find do |gem|
          @requirements.satisfied_by?(Gem::Version.new(gem["number"]))
        end
      end

      # @return [Net::HTTP] an instance of Net::HTTP
      def client
        @client ||= Net::HTTP.new("rubygems.org", Net::HTTP.https_default_port).tap do |client|
          client.use_ssl = true
        end
      end
    end
  end
end