module Dunlop::WorkflowStepModel::StateMachine
  extend ActiveSupport::Concern
  STATE_ORDER = %w[pending overdue processing rejected completed].freeze

  included do
    state_machine initial: :pending do
      after_transition do: :after_transition_hook
      before_transition do: :before_transition_hook
      event(:pending)    { transition any => :pending    }
      event(:processing) { transition any => :processing }
      event(:overdue)    { transition any => :overdue    } # any could be [:pending, :processing]
      event(:completed)  { transition any => :completed  }
      event(:rejected)   { transition any => :rejected   }
    end
    private *%i[
      pending pending!
      processing processing!
      overdue overdue!
      completed completed!
      rejected rejected!
    ]
  end

  def before_transition_hook(transition)
    return unless self.class.columns_hash['notes'].try(:type) == :text
    changes.except('notes').each do |attr, (from, to)|
      case self.class.columns_hash[attr].type
      when :boolean
        append_note "[CHECK_CHANGE] #{self.class.human_attribute_name(attr)} changed to #{to ? 'yes' : 'no'}"
      else
        attribute_name, value = get_attribute_name_and_value_for(attr, to)
        value = "[EMPTY]" if value == '' or value.nil?
        append_note "[CHANGE] #{attribute_name} changed to #{value}"
      end
    end
    append_note "[STATE_CHANGE] #{transition.from} => #{transition.to}" if transition.from != transition.to
  end

  def after_transition_hook(transition)
  end

  def process!
    return save if processing?
    processing
    workflow_instance.workflow_step_processing!(self)
  end

  def complete!
    return save if completed?
    completed
    workflow_instance.workflow_step_completed!(self)
  end

  def reject!
    return save if rejected?
    rejected
    workflow_instance.workflow_step_rejected!(self)
  end

  def is_overdue!
    return save if overdue?
    overdue
    workflow_instance.workflow_step_overdue!(self)
  end

  module ClassMethods
    def sorted_state_count
      group(arel_table[:state]).count.sort_by{|state, count| STATE_ORDER.index(state) || 99}
    end
  end

  private

  def get_attribute_name_and_value_for(attr, value)
    # Try to give a nice name for associations
    if attr.end_with?('_id')
      association_name = attr[0..-4]
      association = self.class.reflect_on_association(association_name)
      #TODO: Take namespacing into account.
      #  OwnerInitialization::Scenario1.reflect_on_association(:workflow_instance).klass
      # returns WorkflowInstance in stead of WorkflowInstance::Scenario1
      attribute_name = association.klass.model_name.human
      if association = public_send(association.name)
        value = association.title.presence if association.respond_to? :title
        value ||= association.name if association.respond_to? :name
      end
    else
      attribute_name = self.class.human_attribute_name(attr)
    end
    [attribute_name, value]
  end
end
