module SplitIoClient
  #
  # helper class to parse fetched splits
  #
  class SplitParser < NoMethodError
    #
    # since value for splitChanges last fetch
    attr_accessor :since

    #
    # till value for splitChanges last fetch
    attr_accessor :till

    #
    # splits data
    attr_accessor :splits

    #
    # splits segments data
    attr_accessor :segments

    def initialize(logger)
      @splits = []
      @since = -1
      @till = -1
      @logger = logger
    end

    #
    # gets all the split names retrived from the endpoint
    #
    # @return [object] array of split names
    def get_split_names
      @splits.map { |s| s.name }
    end

    #
    # @return [boolean] true if the splits content is empty false otherwise
    def is_empty?
      @splits.empty? ? true : false
    end

    #
    # gets all the segment names that are used within the retrieved splits
    #
    # @return [object] array of segment names
    def get_used_segments
      segment_names = []

      @splits.each { |s|
        s.conditions.each { |c|
          c.matchers.each { |m|
            m[:userDefinedSegmentMatcherData].each { |seg, name|
              segment_names << name
            } unless m[:userDefinedSegmentMatcherData].nil?
          } unless c.matchers.nil?
        }
      }
      segment_names.uniq
    end

    #
    # gets a split parsed object by name
    #
    # @param name [string] name of the split
    #
    # @return split [object] split object
    def get_split(name)
      @splits.find { |s| s.name == name }
    end

    #
    # gets the treatment for the given combination of user key and split name
    # using all parsed data for the splits
    #
    # @param id [string] user key
    # @param name [string] split name
    # @param default_treatment [string] default treatment value to be returned
    #
    # @return treatment [object] treatment for this user key, split pair
    def get_split_treatment(id, name, default_treatment, attributes = nil)
      split = get_split(name)
      attribute_matchers = ["ATTR_WHITELIST", "EQUAL_TO", "GREATER_THAN_OR_EQUAL_TO", "LESS_THAN_OR_EQUAL_TO", "BETWEEN"]

      if !split.is_empty? && split.status == 'ACTIVE' && !split.killed?
        split.conditions.each do |c|
          unless c.is_empty?
            matcher = get_matcher_type(c)
            matches = attribute_matchers.include?(matcher.matcher_type) ? matcher.match?(attributes) : matcher.match?(id)
            if matches
              result = Splitter.get_treatment(id, split.seed, c.partitions) #'true match - running split'
              if result.nil?
                return default_treatment
              else
                return result
              end
            end
          end
        end
      end

      default_treatment
    end

    #
    # gets the matcher type from a condition object
    #
    # @param contidion [object] a condition object
    #
    # @return matcher [object] the matcher object for the given condition
    def get_matcher_type(condition)
      final_matcher = nil

      case condition.matcher
        when 'ALL_KEYS'
          final_matcher = AllKeysMatcher.new
        when 'IN_SEGMENT'
          segment = @segments.get_segment(condition.matcher_segment)
          final_matcher = segment.is_empty? ? UserDefinedSegmentMatcher.new(nil) : UserDefinedSegmentMatcher.new(segment)
        when 'WHITELIST'
          final_matcher = WhitelistMatcher.new(condition.matcher_whitelist)
        when 'EQUAL_TO'
          final_matcher = EqualToMatcher.new(condition.matcher_equal)
        when 'GREATER_THAN_OR_EQUAL_TO'
          final_matcher = GreaterThanOrEqualToMatcher.new(condition.matcher_greater_than_or_equal)
        when 'LESS_THAN_OR_EQUAL_TO'
          final_matcher = LessThanOrEqualToMatcher.new(condition.matcher_less_than_or_equal)
        when 'BETWEEN'
          final_matcher = BetweenMatcher.new(condition.matcher_between)
        else
          @logger.error('Invalid matcher type')
      end

      final_matcher
    end

  end

end