module JamRuby
  class EmailBatch < ActiveRecord::Base
    self.table_name = "email_batches"

    has_many :email_batch_sets, :class_name => 'JamRuby::EmailBatchSet'

    attr_accessible :from_email, :subject, :test_emails, :body
    attr_accessible :lock_version, :opt_in_count, :sent_count, :started_at, :completed_at

    default_scope { order('created_at DESC') }

    VAR_FIRST_NAME = '@FIRSTNAME'
    VAR_LAST_NAME = '@LASTNAME'

    DEFAULT_SENDER = "JamKazam <noreply@jamkazam.com>"
    BATCH_SIZE = 500

    BODY_TEMPLATE =<<FOO
Hello #{VAR_FIRST_NAME},

<p>Paragraph 1 ... newline whitespace is significant for plain text conversions</p>

<p>Paragraph 2 ... "#{VAR_FIRST_NAME}" will be replaced by users first name</p>

<p>Thanks for using JamKazam!</p>
FOO

    include AASM
    aasm do
      state :pending, :initial => true
      state :testing
      state :tested
      state :delivering
      state :delivered
      state :disabled
      state :snapshot

      event :enable do
        transitions :from => :disabled, :to => :pending
      end
      event :reset, :after => :did_reset do
        transitions :from => [:disabled, :testing, :tested, :delivering, :delivered, :pending], :to => :pending
      end
      event :do_test_run, :before => :will_run_tests do
        transitions :from => [:pending, :tested, :delivered], :to => :testing
      end
      event :did_test_run, :after => :ran_tests do
        transitions :from => :testing, :to => :tested
      end
      event :do_batch_run, :before => :will_run_batch do
        transitions :from => [:tested, :pending, :delivered], :to => :delivering
      end
      event :did_batch_run, :after => :ran_batch do
        transitions :from => :delivering, :to => :delivered
      end
      event :disable do
        transitions :from => [:pending, :tested, :delivered], :to => :disabled
      end
      event :snapshoting, :after => :take_snapshot do
        transitions :from => [:pending], :to => :snapshot
      end
    end

    def self.new(*args)
      oo = super
      oo.body = BODY_TEMPLATE
      oo
    end

    def self.create_with_params(params)
      obj = self.new
      params.each { |kk,vv| vv.strip! }
      obj.update_with_conflict_validation(params)
      obj
    end

    def can_run_batch?
      self.tested? || self.pending?
    end

    def can_run_test?
      self.test_emails.present? && (self.tested? || self.pending?)
    end

    def deliver_batch_sets!
      User.email_opt_in.find_each do |user|
        bset = EmailBatchSet.sent_email(self, user.id)
        if 'test' == Rails.env
          BatchMailer.send_batch_email(self.id, bset.user_id).deliver!
        else
          BatchMailer.send_batch_email(self.id, bset.user_id).deliver_now
        end
      end
    end

    def deliver_batch
      self.perform_event('do_batch_run!')
      self.deliver_batch_sets!
    end

    def deliver_batch_async
      BatchEmailJob.enqueue(self.id)
    end

    def test_count
      self.test_emails.split(',').count
    end

    def test_users
      self.test_emails.split(',').collect do |ee|
        ee.strip!
        uu = User.new
        uu.email = ee
        uu.first_name = ee.match(/^(.*)@/)[1].to_s
        uu.last_name = 'Test'
        uu
      end
    end

    def send_test_batch
      self.perform_event('do_test_run!')
      if 'test' == Rails.env
        BatchMailer.send_batch_email_test(self.id).deliver!
      else
        BatchMailer.send_batch_email_test(self.id).deliver_now
      end
    end

    def merged_body(user)
      body.gsub(VAR_FIRST_NAME, user.first_name).gsub(VAR_LAST_NAME, user.last_name)
    end

    def did_send(emails)
      self.update_with_conflict_validation({ :sent_count => self.sent_count + emails.size })

      if self.sent_count >= self.opt_in_count
        if delivering?
          self.perform_event('did_batch_run!')
        elsif testing?
          self.perform_event('did_test_run!')
        end
      end
    end

    def perform_event(event_name)
      num_try = 0
      self.send(event_name)
    rescue ActiveRecord::StaleObjectError
      num_try += 1
      if 5 > num_try
        self.reload
        retry
      end
    end

    def update_with_conflict_validation(*args)
      num_try = 0
      begin
        update_attributes(*args)
      rescue ActiveRecord::StaleObjectError
        num_try += 1
        if 5 > num_try
          self.reload
          sleep(0.25)
          retry
        end
      end
    end

    def will_run_batch
      self.update_with_conflict_validation({:opt_in_count => User.email_opt_in.count,
                                             :sent_count => 0,
                                             :started_at => Time.now
                                           })
    end

    def will_run_tests
      self.update_with_conflict_validation({:opt_in_count => self.test_count,
                                             :sent_count => 0
                                           })
    end

    def ran_tests
      self.update_with_conflict_validation({ :completed_at => Time.now })
    end

    def ran_batch
      self.update_with_conflict_validation({ :completed_at => Time.now })
    end

    def take_snapshot
    end

    def clone
      bb = EmailBatch.new
      bb.subject = self.subject
      bb.body = self.body
      bb.from_email = self.from_email
      bb.test_emails = self.test_emails
      bb.save!
      bb
    end

    def opting_in_count
      0 < opt_in_count ? opt_in_count : User.email_opt_in.count
    end

    def did_reset
      self.email_batch_sets.map(&:destroy)
      self.update_with_conflict_validation({ 
                                             :opt_in_count => 0,
                                             :sent_count => 0,
                                             :started_at => nil,
                                             :completed_at => nil,
                                           })
    end

    def batch_substitutions(users=[])
      {}
    end
    
    def body_for_users(users=[])
      self.body
    end

  end
end
