# represenst the type of lesson package
module JamRuby
  class LessonBookingSlot < ActiveRecord::Base

    include HtmlSanitize
    html_sanitize strict: [:message, :accept_message, :cancel_message]

    @@log = Logging.logger[LessonBookingSlot]

    belongs_to :lesson_booking, class_name: "JamRuby::LessonBooking"
    belongs_to :lesson_session, class_name: "JamRuby::LessonSession"
    belongs_to :proposer, class_name: "JamRuby::User", inverse_of: :proposed_slots, foreign_key: :proposer_id
    has_one :defaulted_booking, class_name: "JamRuby::LessonBooking", foreign_key: :default_slot_id, inverse_of: :default_slot
    has_one :countered_booking, class_name: "JamRuby::LessonBooking", foreign_key: :counter_slot_id, inverse_of: :counter_slot
    has_one :countered_lesson, class_name: "JamRuby::LessonSession", foreign_key: :counter_slot_id, inverse_of: :counter_slot

    SLOT_TYPE_SINGLE = 'single'
    SLOT_TYPE_RECURRING = 'recurring'

    SLOT_TYPES = [SLOT_TYPE_SINGLE, SLOT_TYPE_RECURRING]

    validates :proposer, presence: true
    validates :slot_type, inclusion: {in: SLOT_TYPES}
    #validates :preferred_day
    validates :day_of_week, numericality: {only_integer: true}, allow_blank: true # 0 = sunday - 6 = saturday
    validates :hour, numericality: {only_integer: true}
    validates :minute, numericality: {only_integer: true}
    validates :timezone, presence: true # example: 'America/New_York'
    validates :update_all, inclusion: {in: [true, false]}

    validate :validate_slot_type
    validate :validate_slot_minimum_time, on: :create
    validate :validate_proposer
    before_validation :before_validation

    def is_recurring?
      slot_type == SLOT_TYPE_RECURRING
    end
    def before_validation
      if proposer.nil?
        self.proposer = container.student
      end
    end

    def container
      if lesson_booking
        lesson_booking
      else
        lesson_session
      end
    end

    def is_teacher_created?
      self.proposer == container.teacher
    end

    def is_student_created?
      !is_teacher_created?
    end

    def is_teacher_approved?
      !is_teacher_created?
    end

    def recipient
      if is_teacher_created?
        container.student
      else
        container.teacher
      end
    end

    def create_minimum_booking_time
      (Time.now + APP_CONFIG.minimum_lesson_booking_hrs * 60 * 60)
    end

    # create a canned slot for a TestDrivePackage.  The most important thing here is that it expires in 30 days
    def self.packaged_slots
      slot = LessonBookingSlot.new
      slot.from_package = true
      slot.preferred_day = Date.today + 30
      slot.slot_type = LessonBookingSlot::SLOT_TYPE_SINGLE
      slot.hour = 1
      slot.minute = 0
      slot.timezone = 'America/Chicago'
      [slot]
    end

    def scheduled_times(needed_sessions, minimum_start_time)

      #puts "NEEDED SESSIONS #{needed_sessions} #{minimum_start_time}"
      times = []
      week_offset = 0

      needed_sessions.times do |i|
        candidate = scheduled_time(i + week_offset)

        #puts "#{i}: candidate #{candidate} week_offset:#{week_offset}"
        #puts "DAY_OF_WEEK #{day_of_week}"
        if day_of_week && candidate <= minimum_start_time
          # move it up a week
          week_offset += 1
          candidate = scheduled_time(i + week_offset)

          #puts "retry #1 #{candidate}"
          # sanity check
          if candidate <= minimum_start_time
            week_offset += 1
            candidate = scheduled_time(i + week_offset)

            #puts "retry #2 #{candidate}"
            if candidate <= minimum_start_time

              week_offset += 1
              candidate = scheduled_time(i + week_offset)

              #puts "retry #3 #{candidate}"
              if candidate <= minimum_start_time

                week_offset += 1
                candidate = scheduled_time(i + week_offset)

                #puts "retry #4 #{candidate}"
                if candidate <= minimum_start_time
                  raise "candidate time less than minimum start time even after scoot: #{lesson_booking.id} #{self.id}"
                end
              end
            end

          end
        end
        times << candidate
      end

      times
    end

    def next_day
      date = Date.today
      date += ((day_of_week - date.wday) % 7).abs
    end

    # weeks is the number of weeks in the future to compute the time for
    def scheduled_time(weeks)

      # get the timezone of the slot, so we can compute times
      tz = TZInfo::Timezone.get(timezone)

      if preferred_day
        time = tz.local_to_utc(Time.new(preferred_day.year, preferred_day.month, preferred_day.day, hour, minute, 0))
      else
        adjusted = next_day + (weeks * 7)
        # day of the week adjustment
        time = tz.local_to_utc(Time.new(adjusted.year, adjusted.month, adjusted.day, hour, minute, 0))
      end

      time
    end

    def lesson_length
      safe_lesson_booking.lesson_length
    end

    def safe_lesson_booking
      found = lesson_booking
      found ||= lesson_session.lesson_booking
    end

    def pretty_scheduled_start(with_timezone = true, user_tz = nil)

      start_time = scheduled_time(0)

      tz_identifier = user_tz || self.timezone

      begin
        tz = TZInfo::Timezone.get(tz_identifier)
      rescue Exception => e
        @@log.error("unable to find timezone=#{tz_identifier}, e=#{e}")
      end

      if tz
        begin
          start_time = tz.utc_to_local(start_time)
        rescue Exception => e
          @@log.error("unable to convert #{scheduled_start} to #{tz}, e=#{e}")
          puts "unable to convert #{e}"
        end
      end


      duration = lesson_length * 60 # convert from minutes to seconds
      end_time = start_time + duration
      if with_timezone
        "#{start_time.strftime("%A, %B %e")}, #{start_time.strftime("%l:%M").strip}-#{end_time.strftime("%l:%M %p").strip} (#{tz.pretty_name})"
      else
        "#{start_time.strftime("%A, %B %e")} - #{start_time.strftime("%l:%M%P").strip}"
      end
    end

    def pretty_timezone
      begin
        tz = TZInfo::Timezone.get(timezone)
        tz.pretty_name
      rescue Exception => e
        @@log.error("unable to find timezone=#{tz_identifier}, e=#{e}")
        'UTC'
      end
    end

    def pretty_start_time(with_timezone = true, user_tz = nil)

      start_time = scheduled_time(0)

      tz_identifier = user_tz || self.timezone

      begin
        tz = TZInfo::Timezone.get(tz_identifier)
      rescue Exception => e
        @@log.error("unable to find timezone=#{tz_identifier}, e=#{e}")
      end

      if tz
        begin
          start_time = tz.utc_to_local(start_time)
        rescue Exception => e
          @@log.error("unable to convert #{scheduled_start} to #{tz}, e=#{e}")
          puts "unable to convert #{e}"
        end
      end

      duration = lesson_length * 60 # convert from minutes to seconds
      end_time = start_time + duration
      if with_timezone
        "#{start_time.strftime("%a, %b %e")} at #{start_time.strftime("%l:%M%P").strip} #{tz.pretty_name}"
      else
        "#{start_time.strftime("%a, %b %e")} at #{start_time.strftime("%l:%M%P").strip}"
      end
    end

    def validate_proposer
      if proposer && (proposer != container.student && proposer != container.teacher)
        errors.add(:proposer, "must be either the student or teacher")
      end
    end

    def validate_slot_type
      if slot_type == SLOT_TYPE_SINGLE
        if preferred_day.nil?
          errors.add(:preferred_day, "must be specified")
        end
      end

      if slot_type == SLOT_TYPE_RECURRING
        if day_of_week.nil?
          errors.add(:day_of_week, "must be specified")
        end
      end
    end

    def validate_slot_minimum_time

      # this code will fail miserably if the slot is malformed
      if errors.any?
        return
      end

      if is_teacher_created?
        return # the thinking is that a teacher can propose much tighter to the time; since they only counter; maybe they talked to the student
      end

      #
      # minimum_start_time = create_minimum_booking_time
      minimum_start_time = Time.now

      if day_of_week
        # this is recurring; it will sort itself out
      else
        time = scheduled_time(0)

        if time <= minimum_start_time
          #errors.add(:base, "must be at least #{APP_CONFIG.minimum_lesson_booking_hrs} hours in the future")
          errors.add(:preferred_day, "can not be in the past")
        end
      end

    end
  end
end
