require 'test_helper'

class UsersCreditsManagerTest < ActiveSupport::TestCase
  setup do
    @machine = Machine.find(6)
    @training = Training.find(2)
    @plan = Plan.find(3)
    @user = User.joins(statistic_profile: [:subscriptions]).find_by(subscriptions: { plan_id: @plan.id })
    @user.users_credits.destroy_all
    @availability = @machine.availabilities.first
    @reservation_machine = Reservation.new(statistic_profile: @user.statistic_profile, reservable: @machine)
    @reservation_training = Reservation.new(
      statistic_profile: @user.statistic_profile,
      reservable: @training,
      slots_reservations_attributes: [{ slot_id: @training.availabilities.first.slots.first.id }]
    )
  end

  ## context machine reservation
  test 'machine reservation from user without subscribed plan' do
    @user.subscriptions.destroy_all

    @reservation_machine.assign_attributes(slots_reservations_attributes: [{ slot_id: @availability.slots.first }])
    manager = UsersCredits::Manager.new(reservation: @reservation_machine)

    assert_equal false, manager.will_use_credits?
    assert_equal 0, manager.free_hours_count

    assert_no_difference 'UsersCredit.count' do
      manager.update_credits
    end
  end

  test 'machine reservation without credit associated' do
    Credit.where(creditable: @machine).destroy_all

    @reservation_machine.assign_attributes(slots_reservations_attributes: [{ slot_id: @availability.slots.first }])
    manager = UsersCredits::Manager.new(reservation: @reservation_machine)

    assert_equal false, manager.will_use_credits?
    assert_equal 0, manager.free_hours_count

    assert_no_difference 'UsersCredit.count' do
      manager.update_credits
    end

    assert_raise UsersCredits::AlreadyUpdatedError do
      manager.update_credits
    end
  end

  test 'machine reservation with credit associated and user never used his credit' do
    credit = Credit.find_by!(creditable: @machine, plan: @plan)
    credit.update!(hours: 2)
    @user.users_credits.destroy_all

    @reservation_machine.assign_attributes(slots_reservations_attributes: [{ slot_id: @availability.slots.first }])
    manager = UsersCredits::Manager.new(reservation: @reservation_machine)

    assert_equal true, manager.will_use_credits?
    assert_equal 1, manager.free_hours_count

    assert_difference 'UsersCredit.count' do
      manager.update_credits
    end

    assert_raise UsersCredits::AlreadyUpdatedError do
      manager.update_credits
    end
  end

  test 'machine reservation with credit associated and user already used partially his credit' do
    credit = Credit.find_by!(creditable: @machine, plan: @plan)
    credit.update!(hours: 2)
    users_credit = @user.users_credits.create!(credit: credit, hours_used: 1)

    @reservation_machine.assign_attributes(slots_reservations_attributes:
                                             [{ slot_id: @availability.slots.first },
                                              { slot_id: @availability.slots.last }])

    manager = UsersCredits::Manager.new(reservation: @reservation_machine)

    assert_equal true, manager.will_use_credits?
    assert_equal 1, manager.free_hours_count

    assert_no_difference 'UsersCredit.count' do
      manager.update_credits
    end

    users_credit.reload
    assert_equal 2, users_credit.hours_used
  end

  test 'machine reservation with credit associated and user already used all credit' do
    credit = Credit.find_by!(creditable: @machine, plan: @plan)
    users_credit = @user.users_credits.create!(credit: credit, hours_used: 1)

    @reservation_machine.assign_attributes(slots_reservations_attributes:
                                             [{ slot_id: @availability.slots.first },
                                              { slot_id: @availability.slots.last }])
    manager = UsersCredits::Manager.new(reservation: @reservation_machine)

    assert_equal false, manager.will_use_credits?
    assert_equal 0, manager.free_hours_count

    assert_no_difference 'UsersCredit.count' do
      manager.update_credits
    end

    users_credit.reload
    assert_equal 1, users_credit.hours_used
  end

  # context training reservation

  test 'training reservation from user without subscribed plan' do
    @user.subscriptions.destroy_all

    manager = UsersCredits::Manager.new(reservation: @reservation_training)

    assert_equal false, manager.will_use_credits?

    assert_no_difference 'UsersCredit.count' do
      manager.update_credits
    end
  end

  test 'training reservation without credit associated' do
    Credit.where(creditable: @training).destroy_all

    manager = UsersCredits::Manager.new(reservation: @reservation_training)

    assert_equal false, manager.will_use_credits?

    assert_no_difference 'UsersCredit.count' do
      manager.update_credits
    end

    assert_raise UsersCredits::AlreadyUpdatedError do
      manager.update_credits
    end
  end

  test 'training reservation with credit associated and user didnt use his credit yet' do
    Credit.find_or_create_by!(creditable: @training, plan: @plan)
    @user.users_credits.destroy_all

    manager = UsersCredits::Manager.new(reservation: @reservation_training)

    assert_equal true, manager.will_use_credits?

    assert_difference 'UsersCredit.count' do
      manager.update_credits
    end
  end

  test 'training reservation with credit associated but user already used all his credits' do
    @user.users_credits.destroy_all
    another_training = Training.where.not(id: @training.id).first
    credit = Credit.find_or_create_by!(creditable: another_training, plan: @plan)
    @user.users_credits.find_or_create_by!(credit: credit)
    @plan.update(training_credit_nb: 1)

    manager = UsersCredits::Manager.new(reservation: @reservation_training)

    assert_equal false, manager.will_use_credits?

    assert_no_difference 'UsersCredit.count' do
      manager.update_credits
    end
  end

  # context reset user credits

  test 'use UsersCredit::Manager to reset users_credits' do
    credit = Credit.find_by!(creditable: @machine, plan: @plan)
    @user.users_credits.create!(credit: credit, hours_used: 1)

    assert_not_empty @user.users_credits

    manager = UsersCredits::Manager.new(user: @user)
    manager.reset_credits

    assert_empty @user.users_credits.reload
  end
end