# frozen_string_literal: true

class UserNotificationScheduleProcessor

  attr_accessor :schedule, :user, :timezone_name

  def initialize(schedule)
    @schedule = schedule
    @user = schedule.user
    @timezone_name = user.user_option.timezone
  end

  def create_do_not_disturb_timings
    local_time = Time.now.in_time_zone(timezone_name)

    create_timings_for(local_time, days: 2)
  end

  def self.create_do_not_disturb_timings_for(schedule)
    processor = UserNotificationScheduleProcessor.new(schedule)
    processor.create_do_not_disturb_timings
  end

  private

  def create_timings_for(local_time, days: 0, previous_timing: nil)
    weekday = transform_wday(local_time.wday)
    start_minute = schedule["day_#{weekday}_start_time"]
    end_minute = schedule["day_#{weekday}_end_time"]

    previous_timing = find_previous_timing(local_time) if previous_timing.nil? && start_minute != 0

    if start_minute > 0
      previous_timing.ends_at = utc_time_at_minute(local_time, start_minute - 1)
      if previous_timing.id
        previous_timing.save
      else
        user.do_not_disturb_timings.find_or_create_by(previous_timing.attributes.except("id"))
      end

      next_timing = user.do_not_disturb_timings.new(
        starts_at: utc_time_at_minute(local_time, end_minute),
        scheduled: true
      )
      save_timing_and_continue(local_time, next_timing, days)
    else
      save_timing_and_continue(local_time, previous_timing, days)
    end
  end

  private

  def find_previous_timing(local_time)
    # Try and find a previously scheduled dnd timing that we can extend if the
    # ends_at is at the previous midnight. fallback to a new timing if not.
    previous = user.do_not_disturb_timings.find_by(
      ends_at: (local_time - 1.day).end_of_day.utc,
      scheduled: true
    )
    previous || user.do_not_disturb_timings.new(
      starts_at: local_time.beginning_of_day.utc,
      scheduled: true
    )
  end

  def save_timing_and_continue(local_time, timing, days)
    if days == 0
      if timing
        timing.ends_at = local_time.end_of_day.utc
        user.do_not_disturb_timings.find_or_create_by(timing.attributes.except("id"))
      end
      user.publish_do_not_disturb(ends_at: user.do_not_disturb_until)
    else
      create_timings_for(local_time + 1.day, days: days - 1, previous_timing: timing)
    end
  end

  def utc_time_at_minute(base_time, total_minutes)
    hour = total_minutes / 60
    minute = total_minutes % 60
    Time.new(base_time.year, base_time.month, base_time.day, hour, minute, 0, base_time.formatted_offset).utc
  end

  def transform_wday(wday)
    wday == 0 ? 6 : wday - 1
  end
end
