RSpec.shared_examples_for "a workflow step" do |workflow_step_name|
  background { i_am_logged_in_as_admin }
  let(:attributes) { Hash.new }
  let(:time) { Time.current }
  let(:workflow_step){ workflow_instance.public_send(workflow_step_name) }
  let(:workflow_instance_factory) { :workflow_instance }
  before { Timecop.freeze time }
  after { Timecop.return }

  context "selecting workflow_instance from workflow instances page", js: true do
    let(:workflow_instance) { create workflow_instance_factory }
    scenario "sunny" do
      return unless workflow_step_setup_is_valid?
      workflow_step.replace_notes! 'Previous notes'

      visit polymorphic_path([workflow_instance.class])

      select_all_workflow_instances

      find(".#{workflow_step.process_name.pluralize}-actions").click
      within '#workflow-step-modal' do
        # Has contractor info
        expect( page ).to have_content "#{workflow_step.class.model_name.human} actions"
      end

      set_all_attributes selector_prefix: '#workflow-step-modal'
      js_click('.workflow-step-process-button')

      reload_models workflow_instance, workflow_step

      expect( workflow_instance ).to be_active

      expect( workflow_step ).to be_processing
      expect( workflow_step.notes ).to start_with <<-NOTES.strip_heredoc.strip
        Previous notes

        #{time.iso8601} #{@user.email}
      NOTES

      expect( workflow_step.notes ).to end_with <<-NOTES.strip_heredoc.strip
        [STATE_CHANGE] pending => processing
      NOTES

      validate_attributes_on_model
    end
  end

  context "use the non collection resource edit path for editing", js: true do
    let(:workflow_instance) { create workflow_instance_factory, :unplanned }
    scenario 'sunny' do
      workflow_step.replace_notes! 'Previous notes'

      visit polymorphic_path([:edit, workflow_step])

      # Set a value for all attributes
      set_all_attributes
      find('.workflow-step-complete-button').click

      sleep 0.3 # thread issue
      reload_models workflow_instance, workflow_step

      expect( workflow_step ).to be_completed
      expect( workflow_step.notes ).to start_with "Previous notes"
      expect( workflow_step.notes ).to end_with "[STATE_CHANGE] pending => completed"

      #change_event = ChangeEvent.last
      #expect( change_event.user ).to eq @user
      #expect( change_event.changes_log['state'] ).to eq %w[pending completed]

      validate_attributes_on_model
    end
  end

  context "edit from the batch page", js: true do
    let(:workflow_instance_batch) { create workflow_instance_factory.to_s.sub('workflow_instance', 'workflow_instance_batch') }
    let(:workflow_instance) { create workflow_instance_factory, workflow_instance_batch: workflow_instance_batch }
    scenario 'sunny' do
      workflow_step.replace_notes! 'Previous notes'
      workflow_instance_not_in_batch = create :workflow_instance

      visit workflow_instance_batch
      find(".#{workflow_step.process_name.pluralize}-actions").click

      # Set a value for all attributes
      set_all_attributes selector_prefix: '#workflow-step-modal'
      js_click('.workflow-step-process-button')

      expect( page.current_path ).to eq polymorphic_path(workflow_instance_batch)

      reload_models workflow_instance, workflow_instance_not_in_batch, workflow_step

      expect( workflow_instance ).to be_active
      expect( workflow_instance_not_in_batch ).to be_unplanned

      expect( workflow_step ).to be_processing
      expect( workflow_step.notes ).to start_with <<-NOTES.strip_heredoc.strip
        Previous notes

        #{time.iso8601} #{@user.email}
      NOTES
      expect( workflow_step.notes ).to end_with "[STATE_CHANGE] pending => processing"

      validate_attributes_on_model
    end
  end

  context "edit from the workflow instance show page", js: true do
    let(:workflow_instance) { create workflow_instance_factory, :planned }
    scenario 'sunny' do
      workflow_step.replace_notes! 'Previous notes'

      visit workflow_instance
      js_click ".#{workflow_step.process_name}.collection-edit"

      # Set a value for all attributes
      set_all_attributes
      find('.workflow-step-complete-button').click

      expect( page.current_path ).to eq polymorphic_path(workflow_instance)
      reload_models workflow_instance, workflow_step

      expect( workflow_instance ).to be_active

      expect( workflow_step ).to be_completed
      expect( workflow_step.notes ).to start_with <<-NOTES.strip_heredoc.strip
        Previous notes

        #{time.iso8601} #{@user.email}
      NOTES
      expect( workflow_step.notes ).to end_with "[STATE_CHANGE] pending => completed"

      validate_attributes_on_model
    end
  end

  context "edit from a state badge show", js: true do
    let(:workflow_instance) { create workflow_instance_factory, :planned }
    scenario 'sunny' do
      workflow_step.replace_notes! 'Previous notes'

      visit polymorphic_path([workflow_instance])
      js_click ".#{workflow_step.process_name}.display-badge"
      js_click "#display-badge-info-popup .#{workflow_step.process_name}.collection-edit"

      # Set a value for all attributes
      set_all_attributes
      find('.workflow-step-complete-button').click

      expect( page.current_path ).to eq polymorphic_path(workflow_instance)
      reload_models workflow_instance, workflow_step

      expect( workflow_instance ).to be_active

      expect( workflow_step ).to be_completed
      expect( workflow_step.notes ).to start_with <<-NOTES.strip_heredoc.strip
        Previous notes

        #{time.iso8601} #{@user.email}
      NOTES
      expect( workflow_step.notes ).to end_with "[STATE_CHANGE] pending => completed"

      validate_attributes_on_model
    end
  end

  def set_all_attributes(options = {})
    # Set a value for all attributes
    attributes.each do |attr, attr_options|
      test_value = attr_options[:test_value]
      case attr_options[:type]
      when :boolean
        toggle_optional_boolean_for attr, options
      when :date
        disable_the_datepicker
        # Problem with uniform_attribute if all are nil at the moment
        #toggle_optional_input_activator_for(attr)
        set_optional_value(attr, test_date.iso8601, options)
      when :string
        set_optional_value attr, test_value || 'teststr', options
      when :window_from
        # Give an existing id, then make it empty
        workflow_step.class.where(id: workflow_step.id).update_all(attr => 8 * 60)
        # Empty value must be possible, this test is important to find situations where empty string is casted to 0 in stead of nil
        set_optional_value attr, '', options
      when :window_to
        set_optional_value attr, '13:00'
      when :integer
        set_optional_value attr, '92', options
      when :float
        set_optional_value attr, '99.9', options
      end
    end
  end

  def validate_attributes_on_model
    # Check if the values are actually set and added to the notes field
    attributes.each do |attr, attr_options|
      human_name = workflow_step.class.human_attribute_name(attr)
      case attr_options[:type]
      when :boolean
        expect( workflow_step[attr] ).to be(true), "attribute :#{attr} is not set"
        expect( workflow_step.notes ).to include "[CHECK_CHANGE] #{human_name} changed to yes"
      when :date
        expect( workflow_step[attr] ).to eq(test_date), "attribute :#{attr} is not set"
        expect( workflow_step.notes ).to include "[CHANGE] #{human_name} changed to #{test_date.iso8601}"
      when :string
        test_value = attr_options[:test_value] || 'teststr'
        expect( workflow_step[attr] ).to eq(test_value), "attribute :#{attr} is not set"
        expect( workflow_step.notes ).to include "[CHANGE] #{human_name} changed to #{test_value}"
      when :window_from
        expect( workflow_step[attr] ).to eq(nil), "attribute :#{attr} is not set"
        expect( workflow_step.notes ).to include "[CHANGE] #{human_name} changed to [EMPTY]"
      when :window_to
        expect( workflow_step[attr] ).to eq('13:00'), "attribute :#{attr} is not set"
        expect( workflow_step.notes ).to include "[CHANGE] #{human_name} changed to 13"
      when :integer
        expect( workflow_step[attr] ).to eq(92), "attribute :#{attr} is not set"
        expect( workflow_step.notes ).to include "[CHANGE] #{human_name} changed to 92"
      when :float
        expect( workflow_step[attr] ).to eq(99.9), "attribute :#{attr} is not set"
        expect( workflow_step.notes ).to include "[CHANGE] #{human_name} changed to 99.9"
      end
    end
  end


  def test_date
    1.year.from_now.to_date
  end

  def workflow_step_setup_is_valid?
    raise "Workflow instance #{workflow_instance.class.name} has no workflow step #{workflow_step_name}" unless workflow_step.present?
    true
  end

end
