class RecurringTodo < ApplicationRecord
  belongs_to :context
  belongs_to :project
  belongs_to :user

  has_many :todos

  scope :active,    -> { where state: 'active' }
  scope :completed, -> { where state: 'completed' }

  include IsTaggable

  include AASM
  aasm :column => :state do
    state :active, :initial => true, :before_enter => Proc.new { self.occurrences_count = 0 }
    state :completed, :before_enter => Proc.new { self.completed_at = Time.zone.now }, :before_exit => Proc.new { self.completed_at = nil }

    event :complete do
      transitions :to => :completed, :from => [:active]
    end

    event :activate do
      transitions :to => :active, :from => [:completed]
    end
  end

  validates :description, presence: true, length: { maximum: 100 }
  validates :notes, length: { maximum: 60_000, allow_nil: true }
  validates :recurring_period, presence: true
  validates :target, presence: true
  validates :ends_on, presence: true
  validates :context, presence: true

  validate :period_validation
  validate :pattern_specific_validations

  def pattern_specific_validations
    if pattern
      pattern.validate
    else
      errors[:recurring_todo] << "Invalid recurrence period '#{recurring_period}'"
    end
  end

  def valid_period?
    %W[daily weekly monthly yearly].include?(recurring_period)
  end

  def period_validation
    errors.add(:recurring_period, "is an unknown recurrence pattern: '#{recurring_period}'") unless valid_period?
  end

  # the following recurrence patterns can be stored:
  #
  # daily todos - recurrence_period = 'daily'
  #   every nth day - nth stored in every_other1
  #   every work day - only_work_days = true
  #   tracks will choose between both options using only_work_days
  # weekly todos - recurrence_period = 'weekly'
  #   every nth week on a specific day -
  #      nth stored in every_other1 and the specific day is stored in every_day
  # monthly todos - recurrence_period = 'monthly'
  #   every day x of nth month - x stored in every_other1 and nth is stored in every_other2
  #   the xth y-day of every nth month (the forth tuesday of every 2 months) -
  #      x stored in every_other3, y stored in every_count, nth stored in every_other2
  #   choosing between both options is done on recurrence_selector where 0 is
  #   for first type and 1 for second type
  # yearly todos - recurrence_period = 'yearly'
  #   every day x of month y - x is stored in every_other1, y is stored in every_other2
  #   the x-th day y of month z (the forth tuesday of september) -
  #     x is stored in every_other3, y is stored in every_count, z is stored in every_other2
  #   choosing between both options is done on recurrence_selector where 0 is
  #   for first type and 1 for second type

  def pattern
    if valid_period?
      @pattern = eval("RecurringTodos::#{recurring_period.capitalize}RecurrencePattern.new(user)", binding, __FILE__, __LINE__)
      @pattern.build_from_recurring_todo(self)
    end
    @pattern
  end

  def recurrence_pattern
    pattern.recurrence_pattern
  end

  def recurring_target_as_text
    pattern.recurring_target_as_text
  end

  def starred?
    has_tag?(Todo::STARRED_TAG_NAME)
  end

  def get_due_date(previous)
    pattern.get_due_date(previous)
  end

  def get_show_from_date(previous)
    pattern.get_show_from_date(previous)
  end

  def done?(end_date)
    !continues_recurring?(end_date)
  end

  def toggle_completion!
    completed? ? activate! : complete!
  end

  def toggle_star!
    if starred?
      _remove_tags(Todo::STARRED_TAG_NAME)
    else
      _add_tags(Todo::STARRED_TAG_NAME)
    end
    tags.reload
    starred?
  end

  def remove_from_project!
    self.project = nil
    save
  end

  def clear_todos_association
    unless todos.nil?
      todos.each do |t|
        t.recurring_todo = nil
        t.save
      end
    end
  end

  def increment_occurrences
    self.occurrences_count += 1
    save
  end

  def continues_recurring?(previous)
    pattern.continues_recurring?(previous)
  end
end
