module Account::States
  extend ActiveSupport::Concern

  included do
    include AfterCommitQueue

    # XXX: This state machine smells. The :created state does not feel right. I seems it would
    # be better to start in :pending state, but then it's difficult to send the confirmed emails,
    # because hooking them to after_created won't work since there is no user to send it to yet.

    state_machine :initial => :created do

      state :created
      state :pending
      state :approved
      state :rejected

      state :suspended # only for providers now

      around_transition do |account, _transition, block|
        previous_state = account.state

        block.call

        account.publish_state_changed_event(previous_state)
      end

      before_transition :to => :pending do |account|
        account.deliver_confirmed_notification
      end

      before_transition :to => :approved do |account|
        account.deliver_approved_notification
      end

      before_transition :to => :rejected do |account|
        account.deliver_rejected_notification
      end

      after_transition to: :suspended do |account|
        account.run_after_commit(:notify_account_suspended)
      end

      after_transition to: :approved, from: :suspended do |account|
        account.run_after_commit(:notify_account_resumed)
      end

      event :make_pending do
        transition :created => :pending
        transition :approved => :pending
        transition :rejected => :pending
      end

      event :approve do
        transition :created => :approved
        transition :pending => :approved
        transition :rejected => :approved
      end

      event :reject do
        transition :created => :rejected
        transition :pending => :rejected
        transition :approved => :rejected
      end

      event :suspend do
        transition :approved => :suspended, if: :provider?
      end

      event :resume do
        transition :suspended => :approved, if: :provider?
      end
    end

    scope :by_state, ->(state) do
      where(:state => state.to_s) if state.to_s != "all"
    end
  end

  module ClassMethods
    def pending
      by_state(:pending)
    end

    def approved
      by_state(:approved)
    end

    def rejected
      by_state(:rejected)
    end
  end

  def upgrade_state!
    if buyer? && approval_required?
      make_pending!
    else
      approve!
    end
  end

  def publish_state_changed_event(previous_state)
    if previous_state != state
      event = Accounts::AccountStateChangedEvent.create(self, previous_state)
      Rails.application.config.event_store.publish_event(event)
    end
  end

  def deliver_confirmed_notification
    if admins.present? && provider_account
      run_after_commit do
        AccountMessenger.new_signup(self).deliver
        AccountMailer.confirmed(self).deliver
      end
    end
  end

  def deliver_approved_notification
    if buyer? && approval_required?
      unless admins.empty? || admins.first.created_by_provider_signup?
        run_after_commit do
          AccountMailer.approved(self).deliver
        end
      end
    end
  end

  def deliver_rejected_notification
    unless admins.empty? || self.provider_account.nil?
      run_after_commit do
        AccountMailer.rejected(self).deliver
      end
    end
  end

  def notify_account_suspended
    ThreeScale::Analytics.track_account(self, 'Account Suspended')
    ThreeScale::Analytics.group(self)
    ReverseProviderKeyWorker.enqueue(self)
  end

  def notify_account_resumed
    ThreeScale::Analytics.track_account(self, 'Account Resumed')
    ThreeScale::Analytics.group(self)
    ReverseProviderKeyWorker.enqueue(self)
  end
end
