module Dunlop::WorkflowInstanceModel::WorkflowStepHandling
  extend ActiveSupport::Concern

  def workflow_steps
    self.class.scenario_workflow_step_names.map{|workflow_step| public_send workflow_step }.compact
  end

  # Make sure in all views a call to workflow_instance and workflow_instance_id can be made
  def workflow_instance
    self #duh
  end

  # Make sure in all views a call to workflow_instance and workflow_instance_id can be made
  def workflow_instance_id
    id
  end

  # Instantiate workflow_step instances if not already present
  def setup_workflow_steps
    return if self.class == WorkflowInstance # can only be done on scenario class
    self.class.scenario_workflow_step_names.each do |step_name|
      step_class = "#{step_name.to_s.classify}::#{scenario_name.classify}"
      public_send "build_#{step_name}", sti_type: step_class unless public_send(step_name)
    end
  end

  def workflow_step_processing!(workflow_step)
    unless workflow_steps.map(&:rejected?).any?
      activate
    end
  end

  def workflow_step_completed!(workflow_step)
    if workflow_steps.map(&:rejected?).any?
      any_rejected
    else
      workflow_steps.map(&:completed?).all? ? all_completed : activate
    end
    trigger_if_all_workflow_steps_in_batch_are_in_final_state(workflow_step)
  end

  def workflow_step_rejected!(workflow_step)
    any_rejected
    trigger_if_all_workflow_steps_in_batch_are_in_final_state(workflow_step)
  end

  def workflow_step_overdue!(workflow_step)
    overdue unless final_states.include?(state)
  end

  # This method checks if all workflow steps in the same steps
  # are in a final state and triggers a hook if this is the case
  def trigger_if_all_workflow_steps_in_batch_are_in_final_state(workflow_step)
    return unless workflow_instance_batch.present?
    batch_states = workflow_step.workflow_steps_in_batch_scope.distinct.pluck(:state)
    #return unless workflow_step.changes[:state].present?
    if batch_states.any? and (batch_states - workflow_step.class.final_states).empty?
      workflow_step.all_of_batch_are_in_final_state(workflow_instance_batch)
    end
  end

  module ClassMethods
    attr_reader :scenario_workflow_step_names

    # used to setup the workflow step inside a scenario class
    def setup_scenario_workflow_steps(step_names)
      raise "setup_workflow_steps is meant to use inside a scenario class" unless self.name.include?('::')
      if illegal_step_names = (step_names - possible_workflow_step_names).presence
        raise "You cannot specify scenario workflow steps that are not given as possible workflow steps. Extra steps were: #{illegal_step_names.join(', ')}"
      end
      @scenario_workflow_step_names = step_names
    end

    def possible_workflow_step_names
      @@possible_workflow_step_names
    end

    def setup_possible_workflow_steps(possible_workflow_step_names)
      @@possible_workflow_step_names = possible_workflow_step_names
    end

    # Return the the workflow_step classes for the WorkflowInstance.
    # At
    def workflow_step_classes
      scenario_class_name = name.demodulize
      if scenario_class_name == name
        # method invoked on base class
        possible_workflow_step_names.map do |step_name|
          step_name.to_s.classify.constantize
        end
      else
        # method invoked on scenario class
        scenario_workflow_step_names.map do |step_name|
          "#{step_name.to_s.classify}::#{scenario_class_name}".constantize
        end
      end
    end

    #def setup_possible_workflow_steps(step_names)
      #raise "setup_possible_workflow_steps is meant to use inside the workflow_instance base class" if self.name.include?('::')
      #@possible_workflow_step_names = step_names
      #step_names.each do |step_name|
        #has_one step_name, dependent: :destroy
      #end
    #end
  end
end
