module JamRuby
  class Teacher < ActiveRecord::Base
    include HtmlSanitize
    html_sanitize strict: [:biography, :website]
    attr_accessor :validate_introduction, :validate_basics, :validate_pricing
    attr_accessible :genres, :teacher_experiences, :experiences_teaching, :experiences_education, :experiences_award
    has_many :genres, :class_name => "JamRuby::Genre", :through => :teachers_genres # , :order => "description"
    has_many :teachers_genres, :class_name => "JamRuby::TeacherGenre"
    has_many :instruments, :class_name => "JamRuby::Instrument", through: :teachers_instruments # , :order => "description"
    has_many :teachers_instruments, class_name: "JamRuby::TeacherInstrument"
    has_many :subjects, :class_name => "JamRuby::Subject", :through => :teachers_subjects # , :order => "description"
    has_many :teachers_subjects, class_name: "JamRuby::TeacherSubject"
    has_many :languages, :class_name => "JamRuby::Language", :through => :teachers_languages # , :order => "description"
    has_many :teachers_languages, class_name: "JamRuby::TeacherLanguage"
    has_many :teacher_experiences, :class_name => "JamRuby::TeacherExperience"
    has_many :experiences_teaching, -> { where(experience_type: 'teaching') }, :class_name => "JamRuby::TeacherExperience"
    has_many :experiences_education, -> { where(experience_type: 'education') }, :class_name => "JamRuby::TeacherExperience"
    has_many :experiences_award, -> { where(experience_type: 'award') }, :class_name => "JamRuby::TeacherExperience"
    has_many :reviews, :class_name => "JamRuby::Review", as: :target
    has_many :lesson_sessions, :class_name => "JamRuby::LessonSession"
    has_many :lesson_package_purchases, :class_name => "JamRuby::LessonPackagePurchase"
    has_one :review_summary, :class_name => "JamRuby::ReviewSummary", as: :target
    has_one :user, :class_name => 'JamRuby::User', foreign_key: :teacher_id, inverse_of: :teacher
    belongs_to :school, :class_name => "JamRuby::School", inverse_of: :teachers
    belongs_to :retailer, :class_name => "JamRuby::Retailer", inverse_of: :teachers

    validates :user, :presence => true
    validates :biography, length: {minimum: 5, maximum: 4096}, :if => :validate_introduction
    validates :introductory_video, :format => {:with => /(?:https?:\/\/)?(?:www\.)?youtu(?:\.be|be\.com)\/(?:watch\?v=)?([\w-]{10,})/, message: "is not a valid youtube URL"}, :allow_blank => true, :if => :validate_introduction
    validates :years_teaching, :presence => true, :if => :validate_introduction
    validates :years_playing, :presence => true, :if => :validate_introduction
    validates :teaches_test_drive, inclusion: {in: [true, false]}, :if => :validate_pricing
    validates :top_rated, inclusion: {in: [true, false]}
    validates :test_drives_per_week, numericality: {only_integer: true, minimum: 2, maximum: 10}, :if => :validate_pricing
    validates :instruments, :length => {minimum: 1, message: "At least one instrument or subject is required"}, if: :validate_basics, unless: ->(teacher) { teacher.subjects.length>0 }
    validates :subjects, :length => {minimum: 1, message: "At least one instrument or subject is required"}, if: :validate_basics, unless: ->(teacher) { teacher.instruments.length>0 }
    validates :genres, :length => {minimum: 1, message: "At least one genre is required"}, if: :validate_basics
    validates :languages, :length => {minimum: 1, message: "At least one language is required"}, if: :validate_basics

    validate :offer_pricing, :if => :validate_pricing
    validate :offer_duration, :if => :validate_pricing
    validate :teaches_ages, :if => :validate_basics

    #default_scope { includes(:genres).order('created_at desc') }

    after_save :update_profile_pct

    def update_profile_pct
      result = pct_complete
      self.profile_pct = result[:pct]
      self.profile_pct_summary = result.to_json
      Teacher.where(id: id).update_all(profile_pct: self.profile_pct, profile_pct_summary: self.profile_pct_summary)
    end

    def is_guitar_center?
      return school && school.is_guitar_center?
    end

    def self.index(user, params = {})
      limit = params[:per_page]
      limit ||= 20
      limit = limit.to_i

      query = User.unscoped.joins(:teacher)

      # only show teachers with ready for session set to true
      query = query.where('teachers.ready_for_session_at IS NOT NULL').where('teachers.is_searchable = TRUE')

      # always force GuitarCenter users to see only their school's teachers, regardless of what they picked
      if user && (user.is_guitar_center_student? || (params[:onlyMySchool] && params[:onlyMySchool] != 'false' && user.school_id))
        query = query.where("teachers.school_id = ?", user.school_id)
      end

      instruments = params[:instruments]
      if instruments && !instruments.blank? && instruments.length > 0
        query = query.joins("inner JOIN teachers_instruments AS tinst ON tinst.teacher_id = teachers.id")
                    .where("tinst.instrument_id IN (?)", instruments)
      end

      subjects = params[:subjects]
      if subjects && !subjects.blank? && subjects.length > 0
        query = query.joins("inner JOIN teachers_subjects AS tsubjs ON tsubjs.teacher_id = teachers.id")
                    .where('tsubjs.subject_id IN (?)', subjects)
      end

      genres = params[:genres]
      if genres && !genres.blank? && genres.length > 0
        query = query.joins("inner JOIN teachers_genres AS tgenres ON tgenres.teacher_id = teachers.id")
                    .where('tgenres.genre_id IN (?)', genres)
      end

      country = params[:country]
      if country && country.length > 0
        query = query.where(country: country)
      end

      region = params[:region]
      if region && region.length > 0
        query = query.where(state: region)
      end

      languages = params[:languages]
      if languages && !languages.blank? && languages.length > 0
        query= query.joins("inner JOIN teachers_languages AS tlang ON tlang.teacher_id = teachers.id")
                   .where('tlang.language_id IN (?)', languages)
      end

      years_teaching = params[:years_teaching].to_i
      if params[:years_teaching] && years_teaching > 0
        query = query.where('years_teaching >= ?', years_teaching)
      end

      teaches_beginner = params[:teaches_beginner]
      teaches_intermediate = params[:teaches_intermediate]
      teaches_advanced = params[:teaches_advanced]

      if teaches_beginner.present? || teaches_intermediate.present? || teaches_advanced.present?

        clause = ''

        if teaches_beginner == true
          clause << 'teaches_beginner = true'
        end
        if teaches_intermediate == true
          if clause.length > 0
            clause << ' OR '
          end
          clause << 'teaches_intermediate = true'
        end
        if teaches_advanced == true
          if clause.length > 0
            clause << ' OR '
          end
          clause << 'teaches_advanced = true'
        end
        query = query.where(clause)
      end

      student_age = params[:student_age].to_i
      if params[:student_age] && student_age > 0
        query = query.where("teaches_age_lower  <= ? AND (CASE WHEN teaches_age_upper = 0 THEN true ELSE teaches_age_upper >= ? END)", student_age, student_age)
      end

      # don't show phantom teachers that teach 'bass guitar', 'acoustic guitar', 'electric guitar'
      query = query.where("((select count(checkgt.instrument_id) from teachers_instruments checkgt where checkgt.teacher_id = teachers.id AND checkgt.instrument_id IN ('bass guitar', 'acoustic guitar', 'electric guitar') ) = 0 AND phantom = true) OR phantom = false")

      # order in this way: https://jamkazam.atlassian.net/browse/VRFS-4058
      # Real teachers who are marked as top.
      # Real teachers who are not marked as top.
      # Phantom teachers.
      query = query.order("phantom ASC, random_order ASC")

      current_page = params[:page].nil? ? 1 : params[:page].to_i
      next_page = current_page + 1

      # will_paginate gem
      query = query.paginate(:page => current_page, :per_page => limit)

      if query.length == 0 # no more results
        {query: query, next_page: nil}
      elsif query.length < limit # no more results
        {query: query, next_page: nil}
      else
        {query: query, next_page: next_page}
      end
    end

    def self.match_teacher(user)
      instruments = user.instruments
      if !instruments
        instruments = []
      end

      instrument_ids = instruments.map(&:id)

      if instrument_ids.length == 1 && instrument_ids[0] == 'other'
        instrument_ids << 'electric guitar'
      end


      teacher = Teacher
                    .joins(:user)
                    .joins(:teachers_instruments)
                    .where('users.phantom IS FALSE')
                    .where('teachers.ready_for_session_at IS NOT NULL')
                    .where('teachers_instruments.instrument_id in (?)', instrument_ids)
                    .order('RANDOM()').limit(1).first

      if teacher.nil?
        teacher = Teacher
                      .joins(:user)
                      .joins(:teachers_instruments)
                      .where('users.phantom IS FALSE')
                      .where('teachers.ready_for_session_at IS NOT NULL')
                      .where('teachers_instruments.instrument_id in (?)', ['electric guitar'])
                      .order('RANDOM()').limit(1).first
      else
        teacher
      end

      if teacher.nil?
        teacher = Teacher
                      .joins(:user)
                      .where('teachers.ready_for_session_at IS NOT NULL')
                      .where('users.phantom IS FALSE')
                      .order('RANDOM()').limit(1).first
      else
        teacher
      end
      teacher

    end
    def self.randomize_order
      self.connection.execute("update teachers set random_order = sub.row_number * random() * 1000 from (select id, row_number()  over () from teachers) as sub ;")
    end

    def self.save_teacher(user, params)
      teacher = nil
      Teacher.transaction do
        teacher = build_teacher(user, params)
        if teacher.save
          # flag the user as a teacher
          teacher.user.is_a_teacher = true
          teacher.user.save(validate: false)
        end

        if teacher.errors.any?
          raise ActiveRecord::Rollback
        end
      end
      teacher

    end

    def self.build_teacher(user, params)
      # ensure person creating this Teacher is a Musician
      unless user && user.musician?
        raise JamPermissionError, "must be a musician"
      end

      teacher = user.teacher
      teacher ||= Teacher.new
      teacher.user = user

      teacher.website = params[:website] if params.key?(:website)
      teacher.biography = params[:biography] if params.key?(:biography)
      teacher.introductory_video = params[:introductory_video] if params.key?(:introductory_video)
      teacher.years_teaching = params[:years_teaching] if params.key?(:years_teaching)
      teacher.years_playing = params[:years_playing] if params.key?(:years_playing)
      teacher.teaches_age_lower = params[:teaches_age_lower] if params.key?(:teaches_age_lower)
      teacher.teaches_age_upper = params[:teaches_age_upper] if params.key?(:teaches_age_upper)
      teacher.teaches_beginner = params[:teaches_beginner] if params.key?(:teaches_beginner)
      teacher.teaches_intermediate = params[:teaches_intermediate] if params.key?(:teaches_intermediate)
      teacher.teaches_advanced = params[:teaches_advanced] if params.key?(:teaches_advanced)
      teacher.prices_per_lesson = params[:prices_per_lesson] if params.key?(:prices_per_lesson)
      teacher.prices_per_month = params[:prices_per_month] if params.key?(:prices_per_month)
      teacher.lesson_duration_30 = params[:lesson_duration_30] if params.key?(:lesson_duration_30)
      teacher.lesson_duration_45 = params[:lesson_duration_45] if params.key?(:lesson_duration_45)
      teacher.lesson_duration_60 = params[:lesson_duration_60] if params.key?(:lesson_duration_60)
      teacher.lesson_duration_90 = params[:lesson_duration_90] if params.key?(:lesson_duration_90)
      teacher.lesson_duration_120 = params[:lesson_duration_120] if params.key?(:lesson_duration_120)
      teacher.price_per_lesson_30_cents = params[:price_per_lesson_30_cents] if params.key?(:price_per_lesson_30_cents)
      teacher.price_per_lesson_45_cents = params[:price_per_lesson_45_cents] if params.key?(:price_per_lesson_45_cents)
      teacher.price_per_lesson_60_cents = params[:price_per_lesson_60_cents] if params.key?(:price_per_lesson_60_cents)
      teacher.price_per_lesson_90_cents = params[:price_per_lesson_90_cents] if params.key?(:price_per_lesson_90_cents)
      teacher.price_per_lesson_120_cents = params[:price_per_lesson_120_cents] if params.key?(:price_per_lesson_120_cents)
      teacher.price_per_month_30_cents = params[:price_per_month_30_cents] if params.key?(:price_per_month_30_cents)
      teacher.price_per_month_45_cents = params[:price_per_month_45_cents] if params.key?(:price_per_month_45_cents)
      teacher.price_per_month_60_cents = params[:price_per_month_60_cents] if params.key?(:price_per_month_60_cents)
      teacher.price_per_month_90_cents = params[:price_per_month_90_cents] if params.key?(:price_per_month_90_cents)
      teacher.price_per_month_120_cents = params[:price_per_month_120_cents] if params.key?(:price_per_month_120_cents)
      teacher.teaches_test_drive = params[:teaches_test_drive] if params.key?(:teaches_test_drive)
      teacher.test_drives_per_week = params[:test_drives_per_week] if params.key?(:test_drives_per_week)
      teacher.is_searchable = params[:is_searchable] if params.key?(:is_searchable)
      teacher.test_drives_per_week = 10 if !params.key?(:test_drives_per_week) # default to 10 in absence of others
      if params.key?(:school_id)
        teacher.school_id = params[:school_id]
        if !teacher.joined_school_at
          teacher.joined_school_at = Time.now
        end
      end
      if params.key?(:retailer_id)
        teacher.retailer_id = params[:retailer_id]
        if !teacher.joined_retailer_at
          teacher.joined_retailer_at = Time.now
        end
      end

      # How to validate:
      teacher.validate_introduction = !!params[:validate_introduction]
      teacher.validate_pricing = !!params[:validate_pricing]


      initial_save = teacher.save

      teacher.validate_basics = !!params[:validate_basics]

      if initial_save
        # Many-to-many relations:
        if params.key?(:genres)
          genres = params[:genres]
          genres = [] if genres.nil?
          teacher.genres.clear
          genres.each do |genre_id|
            teacher.genres << Genre.find(genre_id)
          end
        end
        if params.key?(:instruments)
          instruments = params[:instruments]
          instruments = [] if instruments.nil?
          teacher.instruments.clear
          instruments.each do |instrument_id|
            teacher.instruments << Instrument.find(instrument_id)
          end
        end
        if params.key?(:subjects)
          subjects = params[:subjects]
          subjects = [] if subjects.nil?
          teacher.subjects.clear
          subjects.each do |subject_id|
            teacher.subjects << Subject.find(subject_id)
          end
        end
        if params.key?(:languages)
          languages = params[:languages]
          languages = [] if languages.nil?
          teacher.languages.clear
          languages.each do |language_id|
            teacher.languages << Language.find(language_id)
          end
        end


        # Experience:
        [:teaching, :education, :award].each do |experience_type|
          key = "experiences_#{experience_type}".to_sym
          if params.key?(key)
            list = params[key]
            list = [] if list.nil?
            experiences = list.collect do |exp|
              TeacherExperience.new(
                  name: exp[:name],
                  experience_type: experience_type,
                  organization: exp[:organization],
                  start_year: exp[:start_year],
                  end_year: exp[:end_year]
              )
            end # collect

            # we blindly destroy/recreate on every resubmit
            previous = teacher.send("#{key.to_s}")
            previous.destroy_all

            # Dynamically call the appropriate method (just setting the
            # value doesn't result in the behavior we need)
            teacher.send("#{key.to_s}=", experiences)
          end # if
        end # do
      end


      return teacher
    end

    def booking_price(lesson_length, single)
      price = nil
      if single
        price = self["price_per_lesson_#{lesson_length}_cents"]
      else
        price = self["price_per_month_#{lesson_length}_cents"]
      end

      if !price.nil?
        price / 100.0
      else
        price
      end
    end

    def booking_price_table
      table = {single:[], monthly:[]}
      durations_allowed = []
      [30, 45, 60, 90, 120].each do |i|
        durations_allowed << i if self["lesson_duration_#{i}"]
      end

      durations_allowed.each do |i|
        if self["price_per_lesson_#{i}_cents"]
          table[:single] << {minutes: i, price: self["price_per_lesson_#{i}_cents"]}
        end
        if self["price_per_month_#{i}_cents"]
          table[:monthly] << {minutes: i, price: self["price_per_month_#{i}_cents"]}
        end
      end
      table
    end

    def offer_pricing
      unless prices_per_lesson.present? || prices_per_month.present?
        errors.add(:offer_pricing, "Must choose to price per lesson or per month")
      end
    end

    def offer_duration
      unless lesson_duration_30.present? || lesson_duration_45.present? || lesson_duration_60.present? || lesson_duration_90.present? || lesson_duration_120.present?
        errors.add(:offer_duration, "Must offer at least one duration")
      end
    end

    def teaches_ages
      if teaches_age_lower > 0 && teaches_age_upper > 0 && (teaches_age_upper < teaches_age_lower)
        errors.add(:ages_taught, "Age range is backwards")
      end
    end

    def recent_reviews
      reviews.order('created_at desc').limit(20)
    end

    def mark_background_checked(time)
      self.background_check_at = time
      self.save!
    end

    def mark_session_ready
      self.ready_for_session_at = Time.now
      self.save!
    end

    def mark_not_session_ready
      self.ready_for_session_at = nil
      self.save!
    end

    def mark_top_rated
      self.top_rated = true
      self.save!
    end

    def mark_not_top_rated
      self.top_rated = false
      self.save!
    end

    def has_experiences_teaching?
      experiences_teaching.count > 0
    end

    def has_experiences_education?
      experiences_education.count > 0
    end

    def has_experiences_award?
      experiences_award.count > 0
    end

    def has_stripe_billing?
      user.has_stripe_connect?
    end

    def has_instruments_or_subject?
      instruments.count > 0 || subjects.count > 0
    end

    def has_genres?
      genres.count > 0
    end

    def has_languages?
      languages.count > 0
    end

    def teaches_ages_specified?
      (!teaches_age_lower.nil? && teaches_age_lower > 0) || (!teaches_age_upper.nil? && teaches_age_upper > 0)
    end

    def teaching_level_specified?
      teaches_beginner || teaches_intermediate || teaches_advanced
    end

    def has_pricing_specified?
      specified = false
      durations_allowed = []
      [30, 45, 60, 90, 120].each do |i|
        durations_allowed << i if self["lesson_duration_#{i}"]
      end

      durations_allowed.each do |i|
        if self["price_per_lesson_#{i}_cents"] || self["price_per_month_#{i}_cents"]
          specified = true
          break
        end
      end
      specified
    end

    def has_name_specified?
      !user.anonymous?
    end

    def stripe_account_id
      user.stripe_auth.uid if user.has_stripe_connect?
    end

    ## !!!! this is only valid for tests
    def stripe_account_id=(new_acct_id)
      user.stripe_account_id = new_acct_id
    end

    # how complete is their profile?
    def pct_complete
      @part_complete ||= {
          name_specified: has_name_specified?,
          experiences_teaching: has_experiences_teaching?,
          experiences_education: has_experiences_education?,
          experiences_award: has_experiences_award?,
          has_stripe_account: has_stripe_billing?,
          has_teacher_bio: !biography.nil?,
          intro_video: !introductory_video.nil?,
          years_teaching: years_teaching > 0,
          years_playing: years_playing > 0,
          instruments_or_subject: has_instruments_or_subject?,
          genres: genres.count > 0,
          languages: languages.count > 0,
          teaches_ages_specified: teaches_ages_specified?,
          teaching_level_specified: teaching_level_specified?,
          has_pricing_specified: has_pricing_specified?
      }

      done = 0
      @part_complete.each do |k, v|
        if v
          done += 1
        end
      end

      complete = 100.0 * done.to_f / @part_complete.length.to_f

      @part_complete[:pct] = complete.round
      @part_complete
    end

    def teaches
      if instruments.length == 0
        return ''
      elsif instruments.length == 2
        return 'Teaches ' + instruments[0].description + ' and ' + instruments[1].description
      else
        return 'Teaches ' + instruments.map { |i| i.description }.join(', ')
      end
    end
  end
end
