# frozen_string_literal: true

module PubSubModelSync
  class MessageProcessor
    attr_accessor :data, :attrs, :settings

    # @param data (Hash): any hash value to deliver
    # @param settings (optional): { id: id_val }
    def initialize(data, klass, action, settings = {})
      @data = data
      @settings = settings
      @attrs = settings.merge(klass: klass, action: action)
    end

    def process
      log 'processing message'
      listeners = filter_listeners
      eval_message(listeners) if listeners.any?
      log 'processed message'
    end

    private

    def eval_message(listeners)
      listeners.each do |listener|
        if listener[:direct_mode]
          call_class_listener(listener)
        else
          call_listener(listener)
        end
      end
    end

    def call_class_listener(listener)
      model_class = listener[:klass].constantize
      model_class.send(listener[:action], data)
    rescue => e
      log("Error listener (#{listener}): #{e.message}", :error)
    end

    # support for: create, update, destroy
    def call_listener(listener)
      model = find_model(listener)
      if attrs[:action].to_sym == :destroy
        model.destroy!
      else
        populate_model(model, listener)
        model.save!
      end
    rescue => e
      log("Error listener (#{listener}): #{e.message}", :error)
    end

    def find_model(listener)
      model_class = listener[:klass].constantize
      identifier = listener[:settings][:id] || :id
      model_class.where(identifier => attrs[:id]).first ||
        model_class.new(identifier => attrs[:id])
    end

    def populate_model(model, listener)
      values = data.slice(*listener[:settings][:attrs])
      values.each do |attr, value|
        model.send("#{attr}=", value)
      end
    end

    def filter_listeners
      listeners = PubSubModelSync::Config.listeners
      listeners.select do |listener|
        listener[:as_klass].to_s == attrs[:klass].to_s &&
          listener[:as_action].to_s == attrs[:action].to_s
      end
    end

    def log(message, kind = :info)
      PubSubModelSync::Config.log "#{message} ==> #{[data, attrs]}", kind
    end
  end
end