You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: appointment_scheduler.rb
    path of file: ./repos/agenda-saude/app/services
    
    the code of the file until where you have to start completion: class AppointmentScheduler
  class NoFreeSlotsAhead < StandardError; end

  ROUNDING = 2.minutes # delta to avoid rounding issues

  CONDITIONS_UNMET = :conditions_unmet
  NO_SLOTS = :no_slots
  SUCCESS = :success

  attr_reader :earliest_allowed, :latest_allowed

  # Scheduler for appointments, enabling appointments to be made between +earliest_allowed+ and +latest_allowed+ dates
  def initialize(earliest_allowed:, latest_allowed:)
    @earliest_allowed = earliest_allowed
    @latest_allowed = latest_allowed
  end

  # Schedules an appointment for +patient+ starting +from+ a given timestamp, optionally with a +ubs_id+ (which can be
  # nil in case we want to schedule on any Ubs available to the +patient+).
  # It will schedule on the first possible time until the +latest_allowed+.
  # Checks if th
    
  def cancel_schedule(appointment:, new_appointment: nil)
    log :cancel_schedule, appointment.patient_id, appointment.id

    dose = appointment.follow_up_for_dose

    attributes = { patient: nil, check_in: nil }
    return appointment.update!(attributes) unless dose

    # For follow up we need to suspend the appointment and update the dose follow up appointment
    appointment.update!(attributes.merge(active: false,
                                         suspend_reason: I18n.t('suspend_reasons.follow_up_canceled_by_user')))
    dose.update! follow_up_appointment: new_appointment
  end

  def open_times_per_ubs(from:, to:, filter_ubs_id: nil, reschedule: false)
    from = [from, earliest_allowed].compact.max - ROUNDING

    if reschedule
      appointments = Appointment.waiting.not_scheduled
    else
      appointments = Appointment.available_doses
    end

    appointments = appointments.where(start: rounded_from(from)..to)
                               .order(:start) # in chronological order
                               .includes(ubs: :neighborhood)
                               .select(:ubs_id, :start, :end) # only return what we care with

    appointments = appointments.where(ubs_id: filter_ubs_id) if filter_ubs_id

    appointments.distinct # remove duplicates (same as .uniq in pure Ruby)
                .group_by(&:ubs) # transforms it into a Hash grouped by Ubs
  end

  # Returns how many days ahead there are available appointments
  def days_ahead_with_open_slot(filter_ubs_id: nil, reschedule: false)
    appointments = Appointment.waiting.not_scheduled.where(ubs_id: filter_ubs_id) if reschedule

    appointments = Appointment.available_doses unless reschedule

    next_available_appointment = appointments.where(start: earliest_allowed..latest_allowed)
                                             .order(:start)
                                             .pick(:start)

    raise NoFreeSlotsAhead unless next_available_appointment

    ((next_available_appointment - Time.zone.now.end_of_day) / 1.day).ceil
  end

  protected

  # Checks if we can schedule +from+ that time and then rounds to avoid issues with time conversion
  def rounded_from(from)
    [earliest_allowed, from].compact.max - ROUNDING
  end

  def update_appointment(patient_id:, start:, ubs_id:, rescheduled:)
    # Single SQL query to update the first available record it can find
    # it will either return 0 if no rows could be found, or 1 if it was able to schedule an appointment
    if rescheduled
      appointment = Appointment.waiting.not_scheduled
    else
      appointment = Appointment.available_doses
    end

    appointment = appointment.order(:start)
                             .where(start: start)
                             .limit(1)

    appointment = appointment.where(ubs_id: ubs_id) if ubs_id.present?
    
    # rubocop:disable Rails/SkipsModelValidations
    appointment
      .update_all(patient_id: patient_id, 
                  updated_at: Time.zone.now,
                  active: true)
      .positive? # 0 = false, 1 = true
    # rubocop:enable Rails/SkipsModelValidations
  end