class EmailCampaign::Recipient < ActiveRecord::Base
  self.table_name = 'email_campaign_recipients'
  
  # attr_accessible :name, :email_address,
  #                 :subscriber_class_name, :subscriber_id,
  #                 :ready, :duplicate, :invalid_email, :unsubscribed,
  #                 :failed, :failed_at, :failure_reason,
  #                 :delivered, :delivered_at
  
  belongs_to :campaign, class_name: 'EmailCampaign::Campaign', foreign_key: 'email_campaign_id'
  
  validates_presence_of :email_address, :subscriber_id
  
  before_create :generate_identifier, :check_for_duplicates, :check_for_unsubscribe
  before_save :check_name, :check_email_address
  
  def self.per_page
    100
  end
  
  def generate_identifier(regenerate = false)
    return identifier if identifier && !regenerate
    
    attempts = 0
    new_identifier = nil
    
    # 28^8 = 378 billion possibilities
    validchars = 'ABCDEFGHJKLMNPQRTUVWXY346789'
    
    while new_identifier.nil? && attempts < 10
      # generate a 8 character identifier
      new_identifier = ''
      8.times { new_identifier << validchars[rand(validchars.length)] }
      
      new_identifier = nil if self.class.count > 0 && self.class.where(identifier: new_identifier).first
      
      attempts += 1
    end
    
    self.identifier = new_identifier
  end
  
  def check_name
    self.name = name.blank? ? nil : name.strip
    
    true
  end
  
  def check_for_duplicates
    if campaign.recipients.where(email_address: email_address).count > 0
      self.ready = false
      self.duplicate = true
    else
      self.duplicate = false
    end
    
    true
  end
  
  def check_email_address
    self.email_address = email_address.blank? ? nil : email_address.strip
    
    if valid_email_address?
      self.invalid_email = false
    else
      self.ready = false
      self.invalid_email = true
    end
    
    true
  end
  
  def check_for_unsubscribe
    if self.class.where(email_address: email_address, unsubscribed: true).count > 0
      self.unsubscribed = true
      self.ready = false
    end
    
    true
  end
  
  def queue
    if !duplicate && !invalid_email && !unsubscribed && !failed
      update_attributes(ready: true)
    else
      false
    end
  end
  
  # resets failed and delivered flags... use sparingly
  def requeue
    update_attributes(failed: false, failed_at: nil, failure_reason: nil,
                      delivered: false, delivered_at: nil)
    queue
  end
  
  def deliver
    if !ready
      puts "Not in ready state"
      return false
    end
    
    if delivered
      puts "Already delivered."
      return true  # not sure yet whether returning true is a good idea, but seems harmless enough
    end
    
    if failed
      puts "Already failed (reason: #{failure_reason}), not going to try again."
      update_attributes(ready: false)
      return false
    end
    
    unless update_column(:attempted, true) && increment(:attempts)
      puts "Could not update 'attempted' flag, will not proceed for fear of sending multiple copies"
      update_attributes(ready: false, failed: true, failed_at: Time.now.utc, failure_reason: "Could not update 'attempted' flag, will not proceed for fear of sending multiple copies")
      return false
    end
    
    if !valid_email_address?
      puts "Invalid email address: #{email_address}"
      update_attributes(ready: false, failed: true, failed_at: Time.now.utc, failure_reason: "Invalid email address")
      return false
    end
    
    begin
      campaign.mailer.constantize.send(campaign.method.to_sym, self).deliver
      update_attributes(ready: false, delivered: true, delivered_at: Time.now.utc)
    rescue Exception => e
      puts e.message
      update_attributes(ready: false, failed: true, failed_at: Time.now.utc, failure_reason: "#{e.class.name}: #{e.message}")
      return false
    end
    
    true
  end
  
  def self.record_open(identifier, params = {})
    r = find_by_identifier(identifier)
    r.record_open(params) if r
  end
  
  def record_open(params = {})
    self.class.transaction do
      self.opened = true
      self.opened_at ||= Time.now.utc
      self.opens += 1
      save
    end
  end
  
  def self.record_click(identifier, params = {})
    r = find_by_identifier(identifier)
    r.record_click(params) if r
  end
  
  def record_click(params = nil)
    self.class.transaction do
      self.opened = true
      self.opened_at ||= Time.now.utc
      self.opens ||= 1
      self.clicked = true
      self.clicked_at ||= Time.now.utc
      self.clicks += 1
      save
    end
  end
  
  def self.unsubscribe(identifier, params = {})
    r = find_by_identifier(identifier)
    r.unsubscribe(params) if r
  end
  
  def unsubscribe(params = nil)
    self.class.transaction do
      self.class.where(email_address: email_address, unsubscribed: false).each do |r|
        r.update_attributes(unsubscribed: true, ready: false)
      end
    end
    save
  end
  
  def self.resubscribe(identifier, params = {})
    r = find_by_identifier(identifier)
    r.resubscribe(params) if r
  end
  
  def resubscribe(params = nil)
    self.class.transaction do
      self.class.where(email_address: email_address, unsubscribed: true).each do |r|
        r.update_attributes(unsubscribed: false)
      end
    end
  end
  
  
  
  def to_s
    name.blank? ? email_address : "#{name} <#{email_address}>"
  end
  
  def valid_email_address?
    value = email_address
    r = false
    begin
      m = Mail::Address.new(value)
      r = m.domain && m.address == value
      if r
        domain_dot_elements = m.domain.split(/\./)
        r &&= domain_dot_elements.none?(&:blank?) && domain_dot_elements.size > 1
      end
    rescue Mail::Field::ParseError
      r = false
    end
    r
  end
  
end
