require 'spec_helper'
require 'webmock/rspec'

#GOOD = <40ms, FAIR = 40-60ms, and POOR = >60ms

describe "Musician Filter API", type: :request do
  let(:user) { FactoryGirl.create(:user) }

  let(:user1) { FactoryGirl.create(:user) }
  let(:user2) { FactoryGirl.create(:user) }
  let(:user3) { FactoryGirl.create(:user) }
  let(:user4) { FactoryGirl.create(:user) }
  let(:user5) { FactoryGirl.create(:user) }
  let(:user6) { FactoryGirl.create(:user) }
  let(:user7) { FactoryGirl.create(:user) }
  let(:user8) { FactoryGirl.create(:user) }

  let(:latency_data_uri) { /\S+\/search_users/ }

  let(:response_body) { mock_latency_response([
      { user: user1, ars_total_latency: 1.0, ars_internet_latency: 0.4, audio_latency: 0.6 }, #GOOD
      { user: user2, ars_total_latency: 40.0, ars_internet_latency: 25.0, audio_latency: 15.0 }, #GOOD
      { user: user3, ars_total_latency: 40.1, ars_internet_latency: 25, audio_latency: 15.1 }, #FAIR
      { user: user4, ars_total_latency: 60.0, ars_internet_latency: 30, audio_latency: 30.0 }, #FAIR
      { user: user5, ars_total_latency: 60.1, ars_internet_latency: 30.1, audio_latency: 30 }, #HIGH
      { user: user6, ars_total_latency: 100.0, ars_internet_latency: 50.0, audio_latency: 50.0 }, #HIGH
      { user: user7, ars_total_latency: -2, ars_internet_latency: -1, audio_latency: -1 }, #UNKNOWN
      { user: user8, ars_total_latency: 10, ars_internet_latency: 5, audio_latency: 0 } #GOOD (NOTE: audio_latency from neo4j is 0 here)
      ])
    }

    let(:response_body_pop) { mock_latency_response([
      { user: user1, ars_total_latency: 1.0, ars_internet_latency: 0.4, audio_latency: 0.6 }, #GOOD
      { user: user2, ars_total_latency: 40.0, ars_internet_latency: 25.0, audio_latency: 15.0 }, #GOOD
      { user: user3, ars_total_latency: 40.1, ars_internet_latency: 25, audio_latency: 15.1 }, #FAIR
      ])
    }

    let(:response_body_pop_and_rap) { mock_latency_response([
      { user: user1, ars_total_latency: 1.0, ars_internet_latency: 0.4, audio_latency: 0.6 }, #GOOD
      ])
    }

    let(:response_body_drums_intermediate) { mock_latency_response([
      { user: user1, ars_total_latency: 1.0, ars_internet_latency: 0.4, audio_latency: 0.6 },
      { user: user2, ars_total_latency: 40.0, ars_internet_latency: 25.0, audio_latency: 15.0 }, 
      { user: user3, ars_total_latency: 40.1, ars_internet_latency: 25, audio_latency: 15.1 }, 
      { user: user4, ars_total_latency: 60.0, ars_internet_latency: 30, audio_latency: 30.0 }
      ])
    }

    let(:response_body_drums_violin_expert) { mock_latency_response([
      { user: user2, ars_total_latency: 40.0, ars_internet_latency: 25.0, audio_latency: 15.0 }, 
      { user: user3, ars_total_latency: 40.1, ars_internet_latency: 25, audio_latency: 15.1 }, 
      ])
    }

    let(:response_body_active_within_one_day) { mock_latency_response([
      { user: user4, ars_total_latency: 60.0, ars_internet_latency: 30, audio_latency: 30.0 }, #FAIR
      ])
    }

    let(:response_body_joined_within_one_day) { mock_latency_response([
      { user: user4, ars_total_latency: 60.0, ars_internet_latency: 30, audio_latency: 30.0 }, #FAIR
      { user: user5, ars_total_latency: 60.1, ars_internet_latency: 30.1, audio_latency: 30 }, #HIGH
      ])
    }
  
  let(:pop) { Genre.find_by_id('pop') }
  let(:rap) { Genre.find_by_id('rap') }
  let(:rock) { Genre.find_by_id('rock') }

  before(:each) do
    User.delete_all

    stub_request(:post, latency_data_uri)
      .with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'})
      .to_return( body: response_body, status: 200)

    stub_request(:post, latency_data_uri).
    with(
      body: hash_including({ genres: ["pop"]}),
      :headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'}
      )
      .to_return( body: response_body_pop, status: 200)

    stub_request(:post, latency_data_uri).
      with(
        body: hash_including({ genres: ["pop", "rap"]}),
        :headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'}
        )
        .to_return( body: response_body_pop_and_rap, status: 200)

    stub_request(:post, latency_data_uri).
      with(
        body: hash_including({ instruments: [{id: 'drums', proficiency: 2}]}),
        :headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'}
        )
        .to_return( body: response_body_drums_intermediate, status: 200)

      stub_request(:post, latency_data_uri).
        with(
          body: hash_including({ instruments: [{id: 'drums', proficiency: 3}, {id: 'violin', proficiency: 3}]}),
          :headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'}
          )
          .to_return( body: response_body_drums_violin_expert, status: 200)

      stub_request(:post, latency_data_uri).
        with(
          body: hash_including({ active_within_days: '1'}),
          :headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'}
          )
          .to_return( body: response_body_active_within_one_day, status: 200)

      stub_request(:post, latency_data_uri).
        with(
          body: hash_including({ joined_within_days: '1'}),
          :headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'}
          )
          .to_return( body: response_body_joined_within_one_day, status: 200)
  end

  def login(user)
    post '/sessions', "session[email]" => user.email, "session[password]" => user.password
  end

  before do
   login(user)
  end

  it "get all musicians" do
    get '/api/search/musicians.json?results=true'
    expect(JSON.parse(response.body)["musicians"].size).to eq(8)
  end

  it "filter musicians when no latency option is selected" do
    post '/api/filter.json', { latency_good: false, latency_fair: false, latency_high: false }
    expect(JSON.parse(response.body)["musicians"].size).to eq(8)
    expect(JSON.parse(response.body)["musicians"][0]["latency_data"]).not_to eq(nil)
    expect(JSON.parse(response.body)["musicians"][0]["latency_data"]["audio_latency"]).not_to eq(nil)
    expect(JSON.parse(response.body)["musicians"][0]["latency_data"]["ars_internet_latency"]).not_to eq(nil)
    expect(JSON.parse(response.body)["musicians"][0]["latency_data"]["ars_total_latency"]).not_to eq(nil)
    expect(JSON.parse(response.body)["musicians"][0]["latency_data"]["audio_latency"]).to eq(5)
  end

  it "set audio latency to 5ms when the returned value is 0" do
    post '/api/filter.json', { latency_good: false, latency_fair: false, latency_high: false }
    expect(JSON.parse(response.body)["musicians"][7]["latency_data"]["audio_latency"]).to eq(5)
  end

  it "filter musicians for all latency options" do
    post '/api/filter.json', { latency_good: true, latency_fair: true, latency_high: true }
    expect(JSON.parse(response.body)["musicians"].size).to eq(7)
    expect(JSON.parse(response.body)["musicians"][0]["latency_data"]).not_to eq(nil)
    expect(JSON.parse(response.body)["musicians"][0]["latency_data"]["audio_latency"]).not_to eq(nil)
    expect(JSON.parse(response.body)["musicians"][0]["latency_data"]["ars_internet_latency"]).not_to eq(nil)
    expect(JSON.parse(response.body)["musicians"][0]["latency_data"]["ars_total_latency"]).not_to eq(nil)

    #sort by latency
    expect(JSON.parse(response.body)["musicians"][0]["id"]).to eq(user1.id)
    expect(JSON.parse(response.body)["musicians"][1]["id"]).to eq(user2.id)
    expect(JSON.parse(response.body)["musicians"][2]["id"]).to eq(user3.id)
    expect(JSON.parse(response.body)["musicians"][3]["id"]).to eq(user4.id)
    expect(JSON.parse(response.body)["musicians"][4]["id"]).to eq(user5.id)
    expect(JSON.parse(response.body)["musicians"][5]["id"]).to eq(user6.id)
    expect(JSON.parse(response.body)["musicians"][6]["id"]).to eq(user8.id)
  end
  
  it "filter GOOD latency users" do
    post '/api/filter.json', { latency_good: true, latency_fair: false, latency_high: false }
    expect(response.content_type).to eq("application/json")
    expect(response).to render_template(:filter)
    expect(response).to have_http_status(:created)
    expect(JSON.parse(response.body)["musicians"].size).to eq(3)

    #test latency data
    expect(JSON.parse(response.body)["musicians"][0]["latency_data"]).not_to eq(nil)
    expect(JSON.parse(response.body)["musicians"][0]["latency_data"]["audio_latency"]).not_to eq(nil)
    expect(JSON.parse(response.body)["musicians"][0]["latency_data"]["ars_internet_latency"]).not_to eq(nil)
    expect(JSON.parse(response.body)["musicians"][0]["latency_data"]["ars_total_latency"]).not_to eq(nil)
  end

  it "filter FAIR latency musicians" do
    post '/api/filter.json', { latency_good: false, latency_fair: true, latency_high: false }
    expect(JSON.parse(response.body)["musicians"].size).to eq(2)
  end

  it "filter HIGH latency musicians" do
    post '/api/filter.json', { latency_good: false, latency_fair: false, latency_high: true }
    expect(JSON.parse(response.body)["musicians"].size).to eq(2)
  end

  it "filter GOOD and FAIR latency musicians" do
    post '/api/filter.json', { latency_good: true, latency_fair: true, latency_high: false }
    expect(JSON.parse(response.body)["musicians"].size).to eq(5)
  end

  it "filter GOOD and HIGH latency musicians" do
    post '/api/filter.json', { latency_good: true, latency_fair: false, latency_high: true }
    expect(JSON.parse(response.body)["musicians"].size).to eq(5)
  end

  it "filter GOOD, FAIR and HIGH latency musicians" do
    post '/api/filter.json', { latency_good: true, latency_fair: true, latency_high: true }
    expect(JSON.parse(response.body)["musicians"].size).to eq(7)
  end

  it "filter musicians by genres" do
    post '/api/filter.json', { latency_good: true, latency_fair: true, latency_high: true, genres: ['pop'] }
    expect(JSON.parse(response.body)["musicians"].size).to eq(3)

    post '/api/filter.json', { latency_good: true, latency_fair: true, latency_high: true, genres: ['pop', 'rap'] }
    expect(JSON.parse(response.body)["musicians"].size).to eq(1)
  end

  it "filter musicians by instruments they play" do

    post '/api/filter.json', { latency_good: true, latency_fair: true, latency_high: true, instruments: [{value: "drums", label: "Drums"}], proficiency_intermediate: true }
    expect(JSON.parse(response.body)["musicians"].size).to eq(4)

    post '/api/filter.json', { latency_good: true, latency_fair: true, latency_high: true, instruments: [{value: "drums", label: "Drums"}, {value: 'violin', label: 'Violin'}], proficiency_expert: true }
    expect(JSON.parse(response.body)["musicians"].size).to eq(2)
  end

  fit "filter musicians by days ago that they joined" do
    post '/api/filter.json', { latency_good: true, latency_fair: true, latency_high: true, joined_within_days: 1 }
    expect(JSON.parse(response.body)["musicians"].size).to eq(2)
  end

  it "finds user updated_at is within a day ago" do
    post '/api/filter.json', { latency_good: true, latency_fair: true, latency_high: true, active_within_days: 1 }
    expect(JSON.parse(response.body)["musicians"].size).to eq(1)
  end

end
