# frozen_string_literal: true

require 'test_helper'

module MachineCollectionFireWithValidationsIntegration
  include StateMachines::Integrations::Base

  def self.integration_name
    :custom_validation
  end

  def invalidate(object, _attribute, message, values = [])
    (object.errors ||= []) << generate_message(message, values)
  end

  def reset(object)
    object.errors = []
  end
end

class MachineCollectionFireWithValidationsTest < StateMachinesTest
  def setup
    StateMachines::Integrations.reset
    StateMachines::Integrations.register(MachineCollectionFireWithValidationsIntegration)

    @klass = Class.new do
      attr_accessor :errors

      def initialize
        @errors = []
        super
      end
    end

    @machines = StateMachines::MachineCollection.new
    @machines[:state] = @state = StateMachines::Machine.new(@klass, :state, initial: :parked, integration: :custom_validation)
    @state.event :ignite do
      transition parked: :idling
    end

    @machines[:alarm_state] = @alarm_state = StateMachines::Machine.new(@klass, :alarm_state, initial: :active, namespace: 'alarm', integration: :custom_validation)
    @alarm_state.event :disable do
      transition active: :off
    end

    @object = @klass.new
  end

  def teardown
    StateMachines::Integrations.reset
  end

  def test_should_not_invalidate_if_transitions_exist
    assert @machines.fire_events(@object, :ignite, :disable_alarm)
    assert_empty @object.errors
  end

  def test_should_invalidate_if_no_transitions_exist
    @object.state = 'idling'
    @object.alarm_state = 'off'

    refute @machines.fire_events(@object, :ignite, :disable_alarm)
    assert_equal ['cannot transition via "ignite"', 'cannot transition via "disable"'], @object.errors
  end

  def test_should_run_failure_callbacks_if_no_transitions_exist
    @object.state = 'idling'
    @object.alarm_state = 'off'
    @state_failure_run = @alarm_state_failure_run = false

    @machines[:state].after_failure { @state_failure_run = true }
    @machines[:alarm_state].after_failure { @alarm_state_failure_run = true }

    refute @machines.fire_events(@object, :ignite, :disable_alarm)
    assert @state_failure_run
    assert @alarm_state_failure_run
  end
end
