require 'spec_helper'

describe "Monthly Recurring Lesson Flow" do

  let(:user) { FactoryGirl.create(:user, remaining_test_drives: 0) }

  let(:teacher_user) { FactoryGirl.create(:teacher_user) }
  let(:teacher) { teacher_user.teacher }
  let(:lesson_booking_slot_single1) { FactoryGirl.build(:lesson_booking_slot_single) }
  let(:lesson_booking_slot_single2) { FactoryGirl.build(:lesson_booking_slot_single) }
  let(:lesson_booking_slot_recurring1) { FactoryGirl.build(:lesson_booking_slot_recurring) }
  let(:lesson_booking_slot_recurring2) { FactoryGirl.build(:lesson_booking_slot_recurring) }
  let(:valid_single_slots) { [lesson_booking_slot_single1, lesson_booking_slot_single2] }
  let(:valid_recurring_slots) { [lesson_booking_slot_recurring1, lesson_booking_slot_recurring2] }
  let(:affiliate_partner) { FactoryGirl.create(:affiliate_partner) }
  let(:affiliate_partner2) { FactoryGirl.create(:affiliate_partner, lesson_rate: 0.30) }
  let(:school) {FactoryGirl.create(:school)}



  after {Timecop.return}

  before {
    pending "lessons paused"

    teacher.stripe_account_id = stripe_account1_id
    teacher.save!
  }
  it "works normal" do

    # if it's later in the month, we'll make 2 lesson_package_purchases (prorated one, and next month's), which can throw off some assertions later on
    Timecop.travel(Date.new(2016, 3, 20))

    # user has no test drives, no credit card on file, but attempts to book a lesson
    booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_MONTHLY, 60)
    booking.errors.any?.should be_false
    booking.card_presumed_ok.should be_false
    booking.user.should eql user
    booking.card_presumed_ok.should be_false
    booking.same_school.should be_false
    booking.same_school_free.should be_false
    booking.should eql user.unprocessed_normal_lesson
    booking.sent_notices.should be_false
    booking.booked_price.should eql 30.00

    ##########  Need validate their credit card
    token = create_stripe_token
    result = user.payment_update({token: token, zip: '78759', normal: true, booking_id: booking.id})
    booking.reload
    booking.card_presumed_ok.should be_true
    booking.errors.any?.should be_false
    booking = result[:lesson]
    lesson = booking.lesson_sessions[0]
    lesson.errors.any?.should be_false

    booking.sent_notices.should be_true
    lesson.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
    lesson.amount_charged.should be 0.0
    lesson.reload

    user.reload
    user.stripe_customer_id.should_not be nil
    user.remaining_test_drives.should eql 0
    user.lesson_purchases.length.should eql 0

    customer = Stripe::Customer.retrieve(user.stripe_customer_id)
    customer.email.should eql user.email

    booking.lesson_sessions.length.should eql 1
    lesson_session = booking.lesson_sessions[0]
    lesson_session.status.should eql LessonBooking::STATUS_REQUESTED
    booking.status.should eql LessonBooking::STATUS_REQUESTED

    ######### Teacher counters with new slot
    teacher_countered_slot = FactoryGirl.build(:lesson_booking_slot_recurring, hour: 14, update_all: true)
    UserMailer.deliveries.clear
    lesson_session.counter({proposer: teacher_user, slot: teacher_countered_slot, message: 'Does this work?'})
    booking.reload
    booking.errors.any?.should be false
    lesson_session.lesson_booking.errors.any?.should be false
    lesson_session.lesson_booking_slots.length.should eql 1
    lesson_session.lesson_booking_slots[0].proposer.should eql teacher_user
    teacher_counter = lesson_session.lesson_booking_slots.order(:created_at).last
    teacher_counter.should eql teacher_countered_slot
    teacher_counter.proposer.should eql teacher_user
    booking.lesson_booking_slots.length.should eql 3
    UserMailer.deliveries.length.should eql 1
    chat = ChatMessage.unscoped.order(:created_at).last
    chat.channel.should eql ChatMessage::CHANNEL_LESSON
    chat.message.should eql 'Does this work?'
    chat.user.should eql teacher_user
    chat.target_user.should eql user
    notification = Notification.unscoped.order(:created_at).last
    notification.session_id.should eql lesson_session.music_session.id
    notification.student_directed.should eql true
    notification.purpose.should eql 'counter'
    notification.description.should eql NotificationTypes::LESSON_MESSAGE

    ######### Student counters with new slot
    student_countered_slot = FactoryGirl.build(:lesson_booking_slot_recurring, hour: 16, update_all: true)
    UserMailer.deliveries.clear
    lesson_session.counter({proposer: user, slot: student_countered_slot, message: 'Does this work better?'})
    lesson_session.errors.any?.should be false
    lesson_session.lesson_booking.errors.any?.should be false
    #lesson_session.lesson_booking_slots.length.should eql 2
    student_counter = booking.lesson_booking_slots.order(:created_at).last
    student_counter.proposer.should eql user
    booking.reload
    booking.lesson_booking_slots.length.should eql 4
    UserMailer.deliveries.length.should eql 1
    chat = ChatMessage.unscoped.order(:created_at).last
    chat.message.should eql 'Does this work better?'
    chat.channel.should eql ChatMessage::CHANNEL_LESSON
    chat.user.should eql user
    chat.target_user.should eql teacher_user
    notification = Notification.unscoped.order(:created_at).last
    notification.session_id.should eql lesson_session.music_session.id
    notification.student_directed.should eql false
    notification.purpose.should eql 'counter'
    notification.description.should eql NotificationTypes::LESSON_MESSAGE

    ######## Teacher accepts slot
    UserMailer.deliveries.clear
    lesson_session.accept({message: 'Yeah I got this', slot: student_counter.id, update_all: false, accepter: teacher_user})
    UserMailer.deliveries.each do |del|
    #  puts del.inspect
    end
    # get acceptance emails, as well as 'your stuff is accepted'
    UserMailer.deliveries.length.should eql 2
    lesson_session.errors.any?.should be_false
    lesson_session.reload
    lesson_session.slot.should eql student_counter
    lesson_session.status.should eql LessonSession::STATUS_APPROVED
    booking.reload
    booking.default_slot.should eql student_counter
    lesson_session.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
    booking.status.should eql LessonBooking::STATUS_APPROVED

    UserMailer.deliveries.length.should eql 2
    chat = ChatMessage.unscoped.order(:created_at).last
    chat.message.should eql 'Yeah I got this'
    chat.purpose.should eql 'Lesson Approved'
    chat.channel.should eql ChatMessage::CHANNEL_LESSON
    chat.user.should eql teacher_user
    chat.target_user.should eql user
    notification = Notification.unscoped.order(:created_at).last
    notification.session_id.should eql lesson_session.music_session.id
    notification.student_directed.should eql true
    notification.purpose.should eql 'accept'
    notification.description.should eql NotificationTypes::LESSON_MESSAGE


    # teacher & student get into session
    start = lesson_session.scheduled_start
    end_time = lesson_session.scheduled_start + (60 * lesson_session.duration)
    uh2 = FactoryGirl.create(:music_session_user_history, user: teacher_user, history: lesson_session.music_session, created_at: start, session_removed_at: end_time)
    # artificially end the session, which is covered by other background jobs
    lesson_session.music_session.session_removed_at = end_time
    lesson_session.music_session.save!

    Timecop.travel(end_time + 1)

    LessonSession.hourly_check
    lesson_session.reload
    lesson_session.analysed.should be_true
    analysis = lesson_session.analysis
    analysis["reason"].should eql LessonSessionAnalyser::STUDENT_FAULT
    analysis["student"].should eql LessonSessionAnalyser::NO_SHOW
    if lesson_session.billing_error_detail
      puts "monthly recurring lesson flow #{lesson_session.billing_error_detail}" # this should not occur, but helps a great deal if a regression occurs and running all the tests
    end

    # let user pay for it
    LessonBooking.hourly_check

    booked_price = booking.booked_price
    prorated  = booked_price / 2
    prorated_cents = (prorated * 100).to_i
    user.reload
    user.lesson_purchases.length.should eql 1
    lesson_purchase = user.lesson_purchases[0]
    lesson_purchase.price.should eql prorated
    lesson_purchase.lesson_package_type.is_normal?.should eql true
    lesson_purchase.price_in_cents.should eql prorated_cents
    teacher_distribution = lesson_purchase.teacher_distribution
    teacher_distribution.amount_in_cents.should eql prorated_cents
    teacher_distribution.ready.should be_true
    teacher_distribution.distributed.should be_false
    user.sales.length.should eql 1
    sale = user.sales.first
    sale.stripe_charge_id.should_not be_nil
    sale.recurly_tax_in_cents.should eql (100 * prorated * 0.0825).round.to_i
    sale.recurly_total_in_cents.should eql ((prorated * 100 * 0.0825).round + 100 * prorated).to_i
    sale.recurly_subtotal_in_cents.should eql prorated_cents
    sale.recurly_currency.should eql 'USD'
    sale.stripe_charge_id.should_not be_nil
    line_item = sale.sale_line_items[0]
    line_item.quantity.should eql 1
    line_item.product_type.should eql SaleLineItem::LESSON
    line_item.product_id.should eq LessonPackageType.single.id
    line_item.lesson_package_purchase.should eql lesson_purchase
    lesson_purchase.sale_line_item.should eql line_item

    TeacherPayment.count.should eql 0
    TeacherPayment.hourly_check
    teacher_distribution.reload
    teacher_distribution.distributed.should be_true
    TeacherPayment.count.should eql 1
    payment = TeacherPayment.first
    payment.amount_in_cents.should eql prorated_cents
    payment.fee_in_cents.should eql (prorated_cents * 0.28).round
    payment.teacher_payment_charge.amount_in_cents.should eql (payment.real_distribution_in_cents + payment.real_distribution_in_cents * APP_CONFIG.stripe[:ach_pct]).round
    payment.teacher_payment_charge.fee_in_cents.should eql (prorated_cents * 0.28).round
    payment.teacher.should eql teacher_user
    payment.teacher_distribution.should eql teacher_distribution



    # teacher & student get into session
    start = lesson_session.scheduled_start
    end_time = lesson_session.scheduled_start + (60 * lesson_session.duration)
    uh2 = FactoryGirl.create(:music_session_user_history, user: teacher_user, history: lesson_session.music_session, created_at: start, session_removed_at: end_time)
    # artificially end the session, which is covered by other background jobs
    lesson_session.music_session.session_removed_at = end_time
    lesson_session.music_session.save!


    UserMailer.deliveries.clear
    # background code comes around and analyses the session
    LessonSession.hourly_check
    lesson_session.reload
    lesson_session.analysed.should be_true
    analysis = lesson_session.analysis
    analysis["reason"].should eql LessonSessionAnalyser::STUDENT_FAULT
    analysis["student"].should eql LessonSessionAnalyser::NO_SHOW
    if lesson_session.billing_error_detail
      puts "monthly recurring lesson flow #{lesson_session.billing_error_detail}" # this should not occur, but helps a great deal if a regression occurs and running all the tests
    end

    lesson.amount_charged.should eql 0.0
    lesson_session.billing_error_reason.should be_nil
    lesson_session.sent_billing_notices.should be nil
    user.reload
    user.remaining_test_drives.should eql 0
    UserMailer.deliveries.length.should eql 0 # one for student

    booking.remaining_roll_forward_amount_in_cents.should eql 0
    lesson_purchase.remaining_roll_forward_amount_in_cents.should be_nil
    lesson_purchase.expected_session_times.should eql 2
    lesson_purchase.actual_session_times.should be_nil

    Timecop.travel(Date.new(2016, 4, 1))
    LessonBooking.adjust_for_missed_sessions
    # we should find remaining/totals in lesson purcahse as well as booking rolled up
    booking.reload
    lesson_purchase.reload
    lesson_purchase.actual_session_times.should eql 1
    lesson_purchase.expected_session_times.should eql 2
    booking.remaining_roll_forward_amount_in_cents.should eql (lesson_purchase.price * 100 * (1.0/2.0)).round
    lesson_purchase.remaining_roll_forward_amount_in_cents.should eql (lesson_purchase.price * 100 * (1.0/2.0)).round
    lesson_purchase.total_roll_forward_amount_in_cents.should eql (lesson_purchase.price * 100 * (1.0/2.0)).round

    # but once we bill out, we'll not credit as heavily
    LessonBooking.bill_monthlies
    booking.reload
    lesson_purchase.reload
    lesson_purchase.actual_session_times.should eql 1
    lesson_purchase.expected_session_times.should eql 2
    booking.remaining_roll_forward_amount_in_cents.should eql 0
    lesson_purchase.remaining_roll_forward_amount_in_cents.should eql 0
    lesson_purchase.total_roll_forward_amount_in_cents.should eql (lesson_purchase.price * 100 * (1.0/2.0)).round


    booking.lesson_package_purchases.count.should eql 2
    next_purchase = booking.lesson_package_purchases.order(:created_at)[1]
    next_purchase.remaining_roll_forward_amount_in_cents.should be nil
    next_purchase.total_roll_forward_amount_in_cents.should be nil
    next_purchase.expected_session_times.should eql 4
    next_purchase.actual_session_times.should be_nil

    next_purchase.teacher_distributions.count.should eql 1
    distribution = next_purchase.teacher_distributions[0]
    distribution.amount_in_cents.should eql (3000 - 750) # booked price is 30.  one lesson is 7.50
    distribution.teacher_fee_in_cents.should eql ((3000 - 750) * 0.28).round # booked price is 30.  one lesson is 7.50. take out .28
    distribution.reduced_roll_forward_amount_in_cents.should eql 750
    next_purchase.reduced_roll_forward_amount_in_cents.should eql 750
  end

  it "works (school on school education)" do

    # make sure teacher can get payments
    teacher.stripe_account_id = stripe_account1_id
    school.user.stripe_account_id = stripe_account2_id

    # get user and teacher into same school
    school.education = true
    school.save!
    user.school = school
    user.save!
    teacher.school = school
    teacher.save!


    # if it's later in the month, we'll make 2 lesson_package_purchases (prorated one, and next month's), which can throw off some assertions later on
    Timecop.travel(Date.new(2016, 3, 20))

    # user has no test drives, no credit card on file, but attempts to book a lesson
    booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_MONTHLY, 60)
    booking.errors.any?.should be_false
    booking.card_presumed_ok.should be_false
    booking.user.should eql user
    booking.card_presumed_ok.should be_false
    booking.same_school.should be_true
    booking.same_school_free.should be_false
    booking.should eql user.unprocessed_normal_lesson
    booking.sent_notices.should be_false
    booking.booked_price.should eql 30.00

    ##########  Need validate their credit card
    token = create_stripe_token
    result = user.payment_update({token: token, zip: '78759', normal: true, booking_id: booking.id})
    booking.reload
    booking.card_presumed_ok.should be_true
    booking.errors.any?.should be_false
    booking = result[:lesson]
    lesson = booking.lesson_sessions[0]
    lesson.errors.any?.should be_false

    booking.sent_notices.should be_true
    lesson.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
    lesson.amount_charged.should be 0.0
    lesson.reload

    user.reload
    user.stripe_customer_id.should_not be nil
    user.remaining_test_drives.should eql 0
    user.lesson_purchases.length.should eql 0

    customer = Stripe::Customer.retrieve(user.stripe_customer_id)
    customer.email.should eql user.email

    booking.lesson_sessions.length.should eql 1
    lesson_session = booking.lesson_sessions[0]
    lesson_session.status.should eql LessonBooking::STATUS_REQUESTED
    booking.status.should eql LessonBooking::STATUS_REQUESTED

    ######### Teacher counters with new slot
    teacher_countered_slot = FactoryGirl.build(:lesson_booking_slot_recurring, hour: 14, update_all: true)
    UserMailer.deliveries.clear
    lesson_session.counter({proposer: teacher_user, slot: teacher_countered_slot, message: 'Does this work?'})
    booking.reload
    booking.errors.any?.should be false
    lesson_session.lesson_booking.errors.any?.should be false
    lesson_session.lesson_booking_slots.length.should eql 1
    lesson_session.lesson_booking_slots[0].proposer.should eql teacher_user
    teacher_counter = lesson_session.lesson_booking_slots.order(:created_at).last
    teacher_counter.should eql teacher_countered_slot
    teacher_counter.proposer.should eql teacher_user
    booking.lesson_booking_slots.length.should eql 3
    UserMailer.deliveries.length.should eql 1
    chat = ChatMessage.unscoped.order(:created_at).last
    chat.channel.should eql ChatMessage::CHANNEL_LESSON
    chat.message.should eql 'Does this work?'
    chat.user.should eql teacher_user
    chat.target_user.should eql user
    notification = Notification.unscoped.order(:created_at).last
    notification.session_id.should eql lesson_session.music_session.id
    notification.student_directed.should eql true
    notification.purpose.should eql 'counter'
    notification.description.should eql NotificationTypes::LESSON_MESSAGE

    ######### Student counters with new slot
    student_countered_slot = FactoryGirl.build(:lesson_booking_slot_recurring, hour: 16, update_all: true)
    UserMailer.deliveries.clear
    lesson_session.counter({proposer: user, slot: student_countered_slot, message: 'Does this work better?'})
    lesson_session.errors.any?.should be false
    lesson_session.lesson_booking.errors.any?.should be false
    #lesson_session.lesson_booking_slots.length.should eql 2
    student_counter = booking.lesson_booking_slots.order(:created_at).last
    student_counter.proposer.should eql user
    booking.reload
    booking.lesson_booking_slots.length.should eql 4
    UserMailer.deliveries.length.should eql 1
    chat = ChatMessage.unscoped.order(:created_at).last
    chat.message.should eql 'Does this work better?'
    chat.channel.should eql ChatMessage::CHANNEL_LESSON
    chat.user.should eql user
    chat.target_user.should eql teacher_user
    notification = Notification.unscoped.order(:created_at).last
    notification.session_id.should eql lesson_session.music_session.id
    notification.student_directed.should eql false
    notification.purpose.should eql 'counter'
    notification.description.should eql NotificationTypes::LESSON_MESSAGE

    ######## Teacher accepts slot
    UserMailer.deliveries.clear
    lesson_session.accept({message: 'Yeah I got this', slot: student_counter.id, update_all: false, accepter: teacher_user})
    UserMailer.deliveries.each do |del|
      #  puts del.inspect
    end
    # get acceptance emails, as well as 'your stuff is accepted'
    UserMailer.deliveries.length.should eql 2
    lesson_session.errors.any?.should be_false
    lesson_session.reload
    lesson_session.slot.should eql student_counter
    lesson_session.status.should eql LessonSession::STATUS_APPROVED
    booking.reload
    booking.default_slot.should eql student_counter
    lesson_session.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
    booking.status.should eql LessonBooking::STATUS_APPROVED

    UserMailer.deliveries.length.should eql 2
    chat = ChatMessage.unscoped.order(:created_at).last
    chat.message.should eql 'Yeah I got this'
    chat.purpose.should eql 'Lesson Approved'
    chat.channel.should eql ChatMessage::CHANNEL_LESSON
    chat.user.should eql teacher_user
    chat.target_user.should eql user
    notification = Notification.unscoped.order(:created_at).last
    notification.session_id.should eql lesson_session.music_session.id
    notification.student_directed.should eql true
    notification.purpose.should eql 'accept'
    notification.description.should eql NotificationTypes::LESSON_MESSAGE


    # teacher & student get into session
    start = lesson_session.scheduled_start
    end_time = lesson_session.scheduled_start + (60 * lesson_session.duration)
    uh2 = FactoryGirl.create(:music_session_user_history, user: teacher_user, history: lesson_session.music_session, created_at: start, session_removed_at: end_time)
    # artificially end the session, which is covered by other background jobs
    lesson_session.music_session.session_removed_at = end_time
    lesson_session.music_session.save!

    Timecop.travel(end_time + 1)

    LessonSession.hourly_check
    lesson_session.reload
    lesson_session.analysed.should be_true
    analysis = lesson_session.analysis
    analysis["reason"].should eql LessonSessionAnalyser::STUDENT_FAULT
    analysis["student"].should eql LessonSessionAnalyser::NO_SHOW
    if lesson_session.billing_error_detail
      puts "monthly recurring lesson flow #{lesson_session.billing_error_detail}" # this should not occur, but helps a great deal if a regression occurs and running all the tests
    end

    # let user pay for it
    LessonBooking.hourly_check

    booked_price = booking.booked_price
    prorated  = booked_price / 2
    prorated_cents = (prorated * 100).to_i
    user.reload
    user.lesson_purchases.length.should eql 1
    lesson_purchase = user.lesson_purchases[0]
    lesson_purchase.price.should eql prorated
    lesson_purchase.lesson_package_type.is_normal?.should eql true
    lesson_purchase.price_in_cents.should eql prorated_cents
    teacher_distribution = lesson_purchase.teacher_distribution
    teacher_distribution.amount_in_cents.should eql prorated_cents
    teacher_distribution.ready.should be_true
    teacher_distribution.distributed.should be_false
    education_distribution = lesson_purchase.education_distribution
    education_distribution.amount_in_cents.should eql (prorated_cents * 0.0625).round
    education_distribution.ready.should be_true
    education_distribution.distributed.should be_false
    user.sales.length.should eql 1
    sale = user.sales.first
    sale.stripe_charge_id.should_not be_nil
    sale.recurly_tax_in_cents.should eql (100 * prorated * 0.0825).round.to_i
    sale.recurly_total_in_cents.should eql ((prorated * 100 * 0.0825).round + 100 * prorated).to_i
    sale.recurly_subtotal_in_cents.should eql prorated_cents
    sale.recurly_currency.should eql 'USD'
    sale.stripe_charge_id.should_not be_nil
    line_item = sale.sale_line_items[0]
    line_item.quantity.should eql 1
    line_item.product_type.should eql SaleLineItem::LESSON
    line_item.product_id.should eq LessonPackageType.single.id
    line_item.lesson_package_purchase.should eql lesson_purchase
    lesson_purchase.sale_line_item.should eql line_item

    TeacherPayment.count.should eql 0
    TeacherPayment.hourly_check
    teacher_distribution.reload
    teacher_distribution.distributed.should be_true
    TeacherPayment.count.should eql 2
    payment = teacher_distribution.teacher_payment
    payment.amount_in_cents.should eql prorated_cents
    payment.fee_in_cents.should eql (prorated_cents * (school.base_rate + APP_CONFIG.stripe[:charge_fee])).round
    payment.teacher_payment_charge.amount_in_cents.should eql (payment.real_distribution_in_cents + payment.real_distribution_in_cents * APP_CONFIG.stripe[:ach_pct]).round + 1 # we are off by 1 in rounding error somewhere. I'm OK with that
    payment.teacher_payment_charge.fee_in_cents.should eql (prorated_cents * (school.base_rate + 0.03)).round
    payment.teacher.should eql teacher_user
    payment.teacher_distribution.should eql teacher_distribution
    education_distribution.reload
    education_distribution.distributed.should be_true

    education_amt = (prorated_cents * 0.0625).round
    payment = education_distribution.teacher_payment
    payment.amount_in_cents.should eql education_amt
    payment.fee_in_cents.should eql 0
    payment.teacher_payment_charge.amount_in_cents.should eql (education_amt + education_amt * APP_CONFIG.stripe[:ach_pct]).round
    payment.teacher_payment_charge.fee_in_cents.should eql 0
    payment.teacher.should eql teacher_user
    payment.teacher_distribution.should eql education_distribution



    # teacher & student get into session
    start = lesson_session.scheduled_start
    end_time = lesson_session.scheduled_start + (60 * lesson_session.duration)
    uh2 = FactoryGirl.create(:music_session_user_history, user: teacher_user, history: lesson_session.music_session, created_at: start, session_removed_at: end_time)
    # artificially end the session, which is covered by other background jobs
    lesson_session.music_session.session_removed_at = end_time
    lesson_session.music_session.save!


    UserMailer.deliveries.clear
    # background code comes around and analyses the session
    LessonSession.hourly_check
    lesson_session.reload
    lesson_session.analysed.should be_true
    analysis = lesson_session.analysis
    analysis["reason"].should eql LessonSessionAnalyser::STUDENT_FAULT
    analysis["student"].should eql LessonSessionAnalyser::NO_SHOW
    if lesson_session.billing_error_detail
      puts "monthly recurring lesson flow #{lesson_session.billing_error_detail}" # this should not occur, but helps a great deal if a regression occurs and running all the tests
    end

    lesson.amount_charged.should eql 0.0
    lesson_session.billing_error_reason.should be_nil
    lesson_session.sent_billing_notices.should be nil
    user.reload
    user.remaining_test_drives.should eql 0
    UserMailer.deliveries.length.should eql 0 # one for student
  end

  it "works (school on school normal)" do

    # get user and teacher into same school
    user.school = school
    user.save!
    teacher.school = school
    teacher.save!

    # if it's later in the month, we'll make 2 lesson_package_purchases (prorated one, and next month's), which can throw off some assertions later on
    Timecop.travel(Date.new(2016, 3, 20))

    # user has no test drives, no credit card on file, but attempts to book a lesson
    booking = LessonBooking.book_normal(user, teacher_user, valid_recurring_slots, "Hey I've heard of you before.", true, LessonBooking::PAYMENT_STYLE_MONTHLY, 60)
    booking.errors.any?.should be_false
    booking.card_presumed_ok.should be_false
    booking.user.should eql user
    booking.sent_notices.should be_false

    token = create_stripe_token
    result = user.payment_update({token: token, zip: '78759', normal: true, booking_id: booking.id})
    booking.reload
    booking.card_presumed_ok.should be_true
    booking.errors.any?.should be_false

    booking.sent_notices.should be_true
    booking.booked_price.should eql 30.00


    user.reload
    user.stripe_customer_id.should_not be nil
    user.remaining_test_drives.should eql 0
    user.lesson_purchases.length.should eql 0

    booking.lesson_sessions.length.should eql 1
    lesson_session = booking.lesson_sessions[0]
    lesson_session.status.should eql LessonBooking::STATUS_REQUESTED
    booking.status.should eql LessonBooking::STATUS_REQUESTED

    ######### Teacher counters with new slot
    teacher_countered_slot = FactoryGirl.build(:lesson_booking_slot_recurring, hour: 14, update_all: true)
    UserMailer.deliveries.clear
    lesson_session.counter({proposer: teacher_user, slot: teacher_countered_slot, message: 'Does this work?'})
    lesson_session.errors.any?.should be_false
    booking.reload
    booking.errors.any?.should be false
    lesson_session.lesson_booking.errors.any?.should be false
    lesson_session.lesson_booking_slots.length.should eql 1
    lesson_session.lesson_booking_slots[0].proposer.should eql teacher_user
    teacher_counter = lesson_session.lesson_booking_slots.order(:created_at).last
    teacher_counter.should eql teacher_countered_slot
    teacher_counter.proposer.should eql teacher_user
    booking.lesson_booking_slots.length.should eql 3
    UserMailer.deliveries.length.should eql 1
    chat = ChatMessage.unscoped.order(:created_at).last
    chat.channel.should eql ChatMessage::CHANNEL_LESSON
    chat.message.should eql 'Does this work?'
    chat.user.should eql teacher_user
    chat.target_user.should eql user
    notification = Notification.unscoped.order(:created_at).last
    notification.session_id.should eql lesson_session.music_session.id
    notification.student_directed.should eql true
    notification.purpose.should eql 'counter'
    notification.description.should eql NotificationTypes::LESSON_MESSAGE

    ######### Student counters with new slot
    student_countered_slot = FactoryGirl.build(:lesson_booking_slot_recurring, hour: 16, update_all: true)
    UserMailer.deliveries.clear
    lesson_session.counter({proposer: user, slot: student_countered_slot, message: 'Does this work better?'})
    lesson_session.errors.any?.should be false
    lesson_session.lesson_booking.errors.any?.should be false
    #lesson_session.lesson_booking_slots.length.should eql 2
    student_counter = booking.lesson_booking_slots.order(:created_at).last
    student_counter.proposer.should eql user
    booking.reload
    booking.lesson_booking_slots.length.should eql 4
    UserMailer.deliveries.length.should eql 1
    chat = ChatMessage.unscoped.order(:created_at).last
    chat.message.should eql 'Does this work better?'
    chat.channel.should eql ChatMessage::CHANNEL_LESSON
    chat.user.should eql user
    chat.target_user.should eql teacher_user
    notification = Notification.unscoped.order(:created_at).last
    notification.session_id.should eql lesson_session.music_session.id
    notification.student_directed.should eql false
    notification.purpose.should eql 'counter'
    notification.description.should eql NotificationTypes::LESSON_MESSAGE

    ######## Teacher accepts slot
    UserMailer.deliveries.clear
    lesson_session.accept({message: 'Yeah I got this', slot: student_counter.id, update_all: false, accepter: teacher_user})
    UserMailer.deliveries.each do |del|
      #  puts del.inspect
    end
    # get acceptance emails, as well as 'your stuff is accepted'
    UserMailer.deliveries.length.should eql 2
    lesson_session.errors.any?.should be_false
    lesson_session.reload
    lesson_session.slot.should eql student_counter
    lesson_session.status.should eql LessonSession::STATUS_APPROVED
    booking.reload
    booking.default_slot.should eql student_counter
    lesson_session.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
    booking.status.should eql LessonBooking::STATUS_APPROVED

    UserMailer.deliveries.length.should eql 2
    chat = ChatMessage.unscoped.order(:created_at).last
    chat.message.should eql 'Yeah I got this'
    chat.purpose.should eql 'Lesson Approved'
    chat.channel.should eql ChatMessage::CHANNEL_LESSON
    chat.user.should eql teacher_user
    chat.target_user.should eql user
    notification = Notification.unscoped.order(:created_at).last
    notification.session_id.should eql lesson_session.music_session.id
    notification.student_directed.should eql true
    notification.purpose.should eql 'accept'
    notification.description.should eql NotificationTypes::LESSON_MESSAGE


    # teacher & student get into session
    start = lesson_session.scheduled_start
    end_time = lesson_session.scheduled_start + (60 * lesson_session.duration)
    uh2 = FactoryGirl.create(:music_session_user_history, user: teacher_user, history: lesson_session.music_session, created_at: start, session_removed_at: end_time)
    # artificially end the session, which is covered by other background jobs
    lesson_session.music_session.session_removed_at = end_time
    lesson_session.music_session.save!

    Timecop.travel(end_time + 1)

    LessonSession.hourly_check
    lesson_session.reload
    lesson_session.analysed.should be_true
    analysis = lesson_session.analysis
    analysis["reason"].should eql LessonSessionAnalyser::STUDENT_FAULT
    analysis["student"].should eql LessonSessionAnalyser::NO_SHOW
    if lesson_session.billing_error_detail
      puts "monthly recurring lesson flow #{lesson_session.billing_error_detail}" # this should not occur, but helps a great deal if a regression occurs and running all the tests
    end

    # let user pay for it
    LessonBooking.hourly_check

    LessonPaymentCharge.count.should eql 0
    TeacherDistribution.count.should eql 0

    user.reload
    user.lesson_purchases.length.should eql 0
    user.sales.length.should eql 0


    TeacherPayment.count.should eql 0
    TeacherPayment.hourly_check
    TeacherPayment.count.should eql 0


    # teacher & student get into session
    start = lesson_session.scheduled_start
    end_time = lesson_session.scheduled_start + (60 * lesson_session.duration)
    uh2 = FactoryGirl.create(:music_session_user_history, user: teacher_user, history: lesson_session.music_session, created_at: start, session_removed_at: end_time)
    # artificially end the session, which is covered by other background jobs
    lesson_session.music_session.session_removed_at = end_time
    lesson_session.music_session.save!


    UserMailer.deliveries.clear
    # background code comes around and analyses the session
    LessonSession.hourly_check
    lesson_session.reload
    lesson_session.analysed.should be_true
    analysis = lesson_session.analysis
    analysis["reason"].should eql LessonSessionAnalyser::STUDENT_FAULT
    analysis["student"].should eql LessonSessionAnalyser::NO_SHOW
    if lesson_session.billing_error_detail
      puts "monthly recurring lesson flow #{lesson_session.billing_error_detail}" # this should not occur, but helps a great deal if a regression occurs and running all the tests
    end

    lesson_session.amount_charged.should eql 0.0
    lesson_session.billing_error_reason.should be_nil
    lesson_session.sent_billing_notices.should be_nil
    user.reload
    user.remaining_test_drives.should eql 0
    UserMailer.deliveries.length.should eql 0 # one for student

    LessonPaymentCharge.count.should eql 0
    TeacherDistribution.count.should eql 0
  end



  it "affiliate gets their cut" do
    Timecop.travel(2016, 05, 15)
    user.affiliate_referral = affiliate_partner
    user.save!
    teacher_user.affiliate_referral = affiliate_partner2
    teacher_user.save!

    user.lesson_purchases.count.should eql 0
    lesson = monthly_lesson(user, teacher_user, {finish:true, accept:true})

    user.reload

    user.lesson_purchases.count.should eql 1
    lesson_package_purchase = user.lesson_purchases.first
    teacher_distribution = lesson_package_purchase.teacher_distribution


    user.sales.count.should eql 1
    user.sales[0].sale_line_items[0].affiliate_distributions.count.should eql 2
    affiliate_partner.affiliate_distributions.count.should eql 1
    affiliate_partner2.affiliate_distributions.count.should eql 1
    partner1_distribution = affiliate_partner.affiliate_distributions.first
    partner2_distribution = affiliate_partner2.affiliate_distributions.first
    partner1_distribution.sale_line_item.should eql partner2_distribution.sale_line_item
    partner1_distribution.affiliate_referral_fee_in_cents.should eql (teacher_distribution.jamkazam_margin_in_cents * affiliate_partner.lesson_rate).round
    partner2_distribution.affiliate_referral_fee_in_cents.should eql (teacher_distribution.jamkazam_margin_in_cents * affiliate_partner2.lesson_rate).round
  end

  it "school affiliate gets nothing when teacher school is involved" do
    Timecop.travel(2016, 05, 15)
    teacher.school = school
    teacher.save!

    user.affiliate_referral = affiliate_partner
    user.save!

    lesson = monthly_lesson(user, teacher_user, {finish:true, accept:true })

    user.sales.count.should eql 1
    user.sales[0].sale_line_items[0].affiliate_distributions.count.should eql 1
    user.lesson_purchases.count.should eql 1
    lesson_package_purchase = user.lesson_purchases.first
    teacher_distribution = lesson_package_purchase.teacher_distribution


    affiliate_partner.affiliate_distributions.count.should eql 1
    partner1_distribution = affiliate_partner.affiliate_distributions.first
    partner1_distribution.affiliate_referral_fee_in_cents.should eql (teacher_distribution.jamkazam_margin_in_cents * affiliate_partner.lesson_rate).round
    school.affiliate_partner.affiliate_distributions.count.should eql 0
  end

  it "student school affiliates gets cut when student school is involved. so does teacher's" do
    # in the middle of the month so that we don't get the next month's in-advance purchase put on us
    Timecop.travel(2016, 05, 15)
    user.affiliate_referral = school.affiliate_partner
    user.save!

    teacher_user.affiliate_referral = affiliate_partner
    teacher_user.save!

    lesson = monthly_lesson(user, teacher_user, {finish: true, accept: true})

    user.sales.count.should eql 1
    user.sales[0].sale_line_items[0].affiliate_distributions.count.should eql 2
    user.lesson_purchases.count.should eql 1
    lesson_package_purchase = user.lesson_purchases.first
    teacher_distribution = lesson_package_purchase.teacher_distribution


    affiliate_partner.affiliate_distributions.count.should eql 1
    partner1_distribution = affiliate_partner.affiliate_distributions.count.should eql 1
    school.affiliate_partner.affiliate_distributions.count.should eql 1
    school_partner_distribution = school.affiliate_partner.affiliate_distributions.first
    school_partner_distribution.affiliate_referral_fee_in_cents.should eql (teacher_distribution.jamkazam_margin_in_cents * school.affiliate_partner.lesson_rate).round
  end
end
