require 'spec_helper'

describe UserSync do

  let(:user1) {FactoryGirl.create(:user)}
  let(:user2) {FactoryGirl.create(:user)}

  before(:each) do

  end

  it "empty results" do
    UserSync.all.length.should == 0
  end

  describe "index" do
    it "empty results" do
      data = UserSync.index({user_id: user1})
      data[:query].length.should == 0
      data[:next].should be_nil
    end

    describe "backing_tracks" do

      let!(:recording1) {
        recording = FactoryGirl.create(:recording, owner: user1, band: nil, duration:1)
        recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: recording.owner, fully_uploaded:false)
        recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: user2, fully_uploaded:false)
        recording.recorded_backing_tracks << FactoryGirl.create(:recorded_backing_track, recording: recording, user: recording.owner, fully_uploaded:false)
        recording.save!
        recording.reload
        recording
      }

      let(:sorted_tracks) {
        Array.new(recording1.recorded_tracks).sort! {|a, b|
          if a.created_at == b.created_at
            a.id <=> b.id
          else
            a.created_at <=> b.created_at
          end
        }
      }

      # backing tracks should only list download, or upload, for the person who opened it, for legal reasons
      it "lists backing track for opener" do
        data = UserSync.index({user_id: user1.id})
        data[:next].should be_nil
        user_syncs = data[:query]
        user_syncs.count.should eq(3)
        user_syncs[0].recorded_track.should == sorted_tracks[0]
        user_syncs[1].recorded_track.should == sorted_tracks[1]
        user_syncs[2].recorded_backing_track.should == recording1.recorded_backing_tracks[0]
      end

      it "does not list backing track for non-opener" do
        data = UserSync.index({user_id: user2.id})
        data[:next].should be_nil
        user_syncs = data[:query]
        user_syncs.count.should eq(2)
        user_syncs[0].recorded_track.should == sorted_tracks[0]
        user_syncs[1].recorded_track.should == sorted_tracks[1]
      end
    end

    it "one mix and quick mix" do
      mix = FactoryGirl.create(:mix)
      mix.recording.duration = 1
      mix.recording.save!
      quick_mix = FactoryGirl.create(:quick_mix_completed, recording:mix.recording, user: mix.recording.recorded_tracks[0].user, autowire:false)

      data = UserSync.index({user_id: mix.recording.recorded_tracks[0].user.id})
      data[:next].should be_nil
      user_syncs = data[:query]
      user_syncs.length.should == 3
      user_syncs[0].recorded_track.should == mix.recording.recorded_tracks[0]
      user_syncs[0].mix.should be_nil
      user_syncs[0].quick_mix.should be_nil
      user_syncs[1].mix.should == mix
      user_syncs[1].recorded_track.should be_nil
      user_syncs[1].quick_mix.should be_nil
      user_syncs[2].mix.should be_nil
      user_syncs[2].recorded_track.should be_nil
      user_syncs[2].quick_mix.should eq(quick_mix)
    end

    # https://jamkazam.atlassian.net/browse/VRFS-2450
    it "no longer returned after fully uploaded and unclaimed" do
      mix = FactoryGirl.create(:mix)
      mix.recording.duration = 1
      mix.recording.save!
      quick_mix = FactoryGirl.create(:quick_mix_completed, recording:mix.recording, user: mix.recording.recorded_tracks[0].user, autowire:false, fully_uploaded: true)
      mix.recording.recorded_tracks[0].fully_uploaded = true
      mix.recording.recorded_tracks[0].save!
      mix.recording.claimed_recordings[0].discarded = true
      mix.recording.claimed_recordings[0].save!

      data = UserSync.index({user_id: mix.recording.recorded_tracks[0].user.id})
      user_syncs = data[:query]
      user_syncs.length.should == 0
    end

    it "two mixes, one not belonging to querier" do
      mix1 = FactoryGirl.create(:mix)
      mix2 = FactoryGirl.create(:mix)
      mix1.recording.duration = 1
      mix1.recording.save!
      mix2.recording.duration = 1
      mix2.recording.save!

      data = UserSync.index({user_id: mix1.recording.recorded_tracks[0].user.id})
      data[:next].should be_nil
      user_syncs = data[:query]
      user_syncs.length.should == 2
      user_syncs[0].recorded_track.should == mix1.recording.recorded_tracks[0]
      user_syncs[0].mix.should be_nil
      user_syncs[1].mix.should == mix1
      user_syncs[1].recorded_track.should be_nil

      data = UserSync.index({user_id: mix2.recording.recorded_tracks[0].user.id})
      data[:next].should be_nil
      user_syncs = data[:query]
      user_syncs.length.should == 2
      user_syncs[0].recorded_track.should == mix2.recording.recorded_tracks[0]
      user_syncs[0].mix.should be_nil
      user_syncs[1].mix.should == mix2
      user_syncs[1].recorded_track.should be_nil
    end

    describe "one recording with two users" do
      let!(:recording1) {
        recording = FactoryGirl.create(:recording, owner: user1, band: nil, duration:1)
        recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: recording.owner, fully_uploaded:false)
        recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: user2, fully_uploaded:false)
        recording.save!
        recording.reload
        recording
      }

      let(:sorted_tracks) {
        Array.new(recording1.recorded_tracks).sort! {|a, b|
          if a.created_at == b.created_at
            a.id <=> b.id
          else
            a.created_at <=> b.created_at
          end
        }
      }

      it "no claimed_recordings" do
        # both user1 and user2 should be told about each others tracks, because both will need to upload their tracks
        data = UserSync.index({user_id: user1.id})
        data[:next].should be_nil
        user_syncs = data[:query]
        user_syncs.length.should == 2
        user_syncs[0].recorded_track.should == sorted_tracks[0]
        user_syncs[1].recorded_track.should == sorted_tracks[1]

        data = UserSync.index({user_id: user2.id})
        data[:next].should be_nil
        user_syncs = data[:query]
        user_syncs[0].recorded_track.should == sorted_tracks[0]
        user_syncs[1].recorded_track.should == sorted_tracks[1]
      end

      it "recording isn't over" do
        recording1.duration = nil
        recording1.save!

        # nothing should be returned, because the recording isn't over
        data = UserSync.index({user_id: user1.id})
        data[:next].should be_nil
        user_syncs = data[:query]
        user_syncs.length.should == 0

        data = UserSync.index({user_id: user2.id})
        data[:next].should be_nil
        user_syncs = data[:query]
        user_syncs.length.should == 0
      end

      it "one user decides to keep the recording" do
        claimed_recording = FactoryGirl.create(:claimed_recording, user: user1, recording: recording1, discarded:false)
        claimed_recording.recording.should == recording1

        data = UserSync.index({user_id: user1.id})
        data[:next].should be_nil
        user_syncs = data[:query]
        user_syncs.length.should == 2
        user_syncs[0].recorded_track.should == sorted_tracks[0]
        user_syncs[1].recorded_track.should == sorted_tracks[1]

        data = UserSync.index({user_id: user2.id})
        data[:next].should be_nil
        user_syncs = data[:query]
        user_syncs.length.should == 2
        user_syncs[0].recorded_track.should == sorted_tracks[0]
        user_syncs[1].recorded_track.should == sorted_tracks[1]
      end


      it "one user decides to discard the recording" do
        FactoryGirl.create(:claimed_recording, user: user1, recording: recording1, discarded:true)

        data = UserSync.index({user_id: user1.id})
        data[:next].should be_nil
        user_syncs = data[:query]
        user_syncs.length.should == 2
        user_syncs[0].recorded_track.should == sorted_tracks[0]
        user_syncs[1].recorded_track.should == sorted_tracks[1]

        data = UserSync.index({user_id: user2.id})
        data[:next].should be_nil
        user_syncs = data[:query]
        user_syncs.length.should == 2
        user_syncs[0].recorded_track.should == sorted_tracks[0]
        user_syncs[1].recorded_track.should == sorted_tracks[1]
      end

      it "both users decide to discard the recording" do
        recording1.all_discarded = true
        recording1.save!

        data = UserSync.index({user_id: user1.id})
        data[:next].should be_nil
        user_syncs = data[:query]
        user_syncs.length.should == 0

        data = UserSync.index({user_id: user2.id})
        data[:next].should be_nil
        user_syncs = data[:query]
        user_syncs.length.should == 0
      end
    end

    describe "one recording with multi-track users" do
      let!(:recording1) {
        recording = FactoryGirl.create(:recording, owner: user1, band: nil, duration:1)
        recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: recording.owner, fully_uploaded:false)
        recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: recording.owner, fully_uploaded:false)
        recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: user2, fully_uploaded:false)
        recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: user2, fully_uploaded:false)
        recording.save!
        recording.reload
        recording
      }

      let(:sorted_tracks) {
        Array.new(recording1.recorded_tracks).sort! {|a, b|
          if a.created_at == b.created_at
            a.id <=> b.id
          else
            a.created_at <=> b.created_at
          end
        }
      }


      it "one user decides to keep the recording" do
        claimed_recording = FactoryGirl.create(:claimed_recording, user: user1, recording: recording1, discarded:false)
        claimed_recording.recording.should == recording1

        data = UserSync.index({user_id: user1.id})
        data[:next].should be_nil
        user_syncs = data[:query]
        user_syncs.length.should == 4
        user_syncs[0].recorded_track.should == sorted_tracks[0]
        user_syncs[1].recorded_track.should == sorted_tracks[1]
        user_syncs[2].recorded_track.should == sorted_tracks[2]
        user_syncs[3].recorded_track.should == sorted_tracks[3]

        data = UserSync.index({user_id: user2.id})
        data[:next].should be_nil
        user_syncs = data[:query]
        user_syncs.length.should == 4
        user_syncs[0].recorded_track.should == sorted_tracks[0]
        user_syncs[1].recorded_track.should == sorted_tracks[1]
        user_syncs[2].recorded_track.should == sorted_tracks[2]
        user_syncs[3].recorded_track.should == sorted_tracks[3]
      end
    end

  end

  describe "pagination" do
    let!(:recording1) {
      recording = FactoryGirl.create(:recording, owner: user1, band: nil, duration:1)
      recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: recording.owner)
      recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: user2)
      claimed_recording = FactoryGirl.create(:claimed_recording, user: recording.owner, recording: recording, discarded:false)
      recording.save!
      recording.reload
      recording
    }
    let(:sorted_tracks) {
      Array.new(recording1.recorded_tracks).sort! {|a, b|
        if a.created_at == b.created_at
          a.id <=> b.id
        else
          a.created_at <=> b.created_at
        end
      }
    }

    it "tiny page size" do
      # get the 1st track
      data = UserSync.index({user_id: user1.id, offset: 0, limit: 1})
      data[:next].should == 1
      user_syncs = data[:query]
      user_syncs.length.should == 1
      user_syncs[0].recorded_track.should == sorted_tracks[0]
      data[:query].total_entries.should == 2

      # then get the second track, which happens to be on an edge, pagination wise
      data = UserSync.index({user_id: user1.id, offset: 1, limit: 1})
      data[:next].should == 2
      user_syncs = data[:query]
      user_syncs.length.should == 1
      user_syncs[0].recorded_track.should == sorted_tracks[1]
      data[:query].total_entries.should == 2

      # so prove it's on the edge by asking for 2 items instead of 1.
      data = UserSync.index({user_id: user1.id, offset: 0, limit: 3})
      data[:next].should == nil
      user_syncs = data[:query]
      user_syncs.length.should == 2
      user_syncs[0].recorded_track.should == sorted_tracks[0]
      user_syncs[1].recorded_track.should == sorted_tracks[1]
      data[:query].total_entries.should == 2

      data = UserSync.index({user_id: user1.id, offset: 2, limit: 1})
      data[:next].should == nil
      user_syncs = data[:query]
      user_syncs.length.should == 0
      data[:query].total_entries.should == 2
    end
  end

  it "does not return deleted recordings" do
    mix = FactoryGirl.create(:mix)
    mix.recording.duration = 1
    mix.recording.save!
    quick_mix = FactoryGirl.create(:quick_mix_completed, recording:mix.recording, user: mix.recording.recorded_tracks[0].user, autowire:false)

    mix.recording.mark_delete
    data = UserSync.index({user_id: mix.recording.recorded_tracks[0].user.id})
    data[:next].should be_nil
    user_syncs = data[:query]
    user_syncs.length.should == 0
  end

  describe "deletable" do
    describe "one mix and one quick mix" do

      let!(:mix)        { m = FactoryGirl.create(:mix); m.recording.duration = 1; m.recording.save!;   m}
      let!(:quick_mix)  { FactoryGirl.create(:quick_mix_completed, recording:mix.recording, user: mix.recording.recorded_tracks[0].user, autowire:false) }

      it "unknown id" do
        recording_ids = ['1']

        result = UserSync.deletables(user_id: mix.recording.recorded_tracks[0].user.id, recording_ids: recording_ids)

        result.should eq(recording_ids)
      end

      it "unknown ids" do
        recording_ids = ['1', '2', '3']

        result = UserSync.deletables(user_id: mix.recording.recorded_tracks[0].user.id, recording_ids: recording_ids)

        result.should eq(recording_ids)
      end

      it "valid recording id" do
        recording_ids = [mix.recording.id]

        result = UserSync.deletables(user_id: mix.recording.recorded_tracks[0].user.id, recording_ids: recording_ids)

        result.should eq([])
      end

      it "valid recording id" do
        recording_ids = [mix.recording.id]

        result = UserSync.deletables(user_id: mix.recording.recorded_tracks[0].user.id, recording_ids: recording_ids)

        result.should eq([])
      end

      it "valid recording_id mixed with unknown ids" do
        recording_ids = [mix.recording.id, '1']

        result = UserSync.deletables(user_id: mix.recording.recorded_tracks[0].user.id, recording_ids: recording_ids)

        result.should eq(['1'])
      end

      it "null recording_ids allowed" do
        # observed that the client can send null for recording_ids
        result = UserSync.deletables(user_id: mix.recording.recorded_tracks[0].user.id, recording_ids: nil)

        result.should eq([])
      end
    end

    describe "two recordings" do

      let!(:mix)        { m = FactoryGirl.create(:mix); m.recording.duration = 1; m.recording.save!;   m}
      let!(:quick_mix)  { FactoryGirl.create(:quick_mix_completed, recording:mix.recording, user: mix.recording.recorded_tracks[0].user, autowire:false) }

      let!(:mix2)        { m = FactoryGirl.create(:mix); m.recording.duration = 1; m.recording.save!;   m}
      let!(:quick_mix2)  { FactoryGirl.create(:quick_mix_completed, recording:mix2.recording, user: mix2.recording.recorded_tracks[0].user, autowire:false) }

      before(:each) do
        # fix up the user associated with the second mix/recording to be same as 1st
        mix2.recording.owner = mix.recording.owner
        mix2.recording.save!
        mix2.recording.recorded_tracks[0].user = mix.recording.owner
        mix2.recording.recorded_tracks[0].save!
      end

      it "unknown id" do
        recording_ids = ['1']

        result = UserSync.deletables(user_id: mix.recording.recorded_tracks[0].user.id, recording_ids: recording_ids)

        result.should eq(recording_ids)
      end

      it "unknown ids" do
        recording_ids = ['1', '2', '3']

        result = UserSync.deletables(user_id: mix.recording.recorded_tracks[0].user.id, recording_ids: recording_ids)

        result.should eq(recording_ids)
      end

      it "valid recording id" do
        recording_ids = [mix.recording.id, mix2.recording.id]

        result = UserSync.deletables(user_id: mix.recording.recorded_tracks[0].user.id, recording_ids: recording_ids)

        result.should eq([])
      end

      it "valid recording id" do
        recording_ids = [mix.recording.id]

        result = UserSync.deletables(user_id: mix.recording.recorded_tracks[0].user.id, recording_ids: recording_ids)

        result.should eq([])
      end

      it "valid recording_id mixed with unknown ids" do
        recording_ids = [mix.recording.id, '1']

        result = UserSync.deletables(user_id: mix.recording.recorded_tracks[0].user.id, recording_ids: recording_ids)

        result.should eq(['1'])
      end
    end

    it "resolved recordings" do

      # start with a recording with a fully uploaded recorded_track, and no claim
      recording = FactoryGirl.create(:recording_with_track, owner: user1)
      recording.duration = 1
      recording.save!

      recording_ids = [recording.id]
      result = UserSync.deletables(user_id: user1.id, recording_ids: recording_ids)
      result.should eq(recording_ids)

      # set the recorded_track to not fully uploaded, which should make it not deletable
      recording.recorded_tracks[0].fully_uploaded = false
      recording.recorded_tracks[0].save!

      result = UserSync.deletables(user_id: user1.id, recording_ids: recording_ids)
      result.should eq([])

      # mark recording as deleted, which should make it deletable
      recording.deleted = true
      recording.save!

      result = UserSync.deletables(user_id: user1.id, recording_ids: recording_ids)
      result.should eq(recording_ids)

      # mark recording as fully discarded, which should make it deletable
      recording.all_discarded = true
      recording.deleted = false
      recording.save!

      result = UserSync.deletables(user_id: user1.id, recording_ids: recording_ids)
      result.should eq(recording_ids)

      # claim the recording, and make the track not fully uploaded
      recording.recorded_tracks[0].fully_uploaded = false
      recording.all_discarded = false
      claim = FactoryGirl.create(:claimed_recording, user: user1, recording: recording)
      recording.save!

      result = UserSync.deletables(user_id: user1.id, recording_ids: recording_ids)
      result.should eq([])

      # create a mix while still claiming the recording
      mix = FactoryGirl.create(:mix, autowire:false, recording:recording)
      result = UserSync.deletables(user_id: user1.id, recording_ids: recording_ids)
      result.should eq([])

      # now take away the claim, and make sure the track is fully uploaded
      claim.discarded = true
      claim.save!
      # without a claimed recording, make the track fully uploaded, so as to not trigger 'need to upload' logic
      recording.recorded_tracks[0].fully_uploaded = true
      recording.recorded_tracks[0].save!

      result = UserSync.deletables(user_id: user1.id, recording_ids: recording_ids)
      result.should eq(recording_ids)

      # if we make a quick mix, but still have no claim, we still should still need to delete the recording
      quick_mix = FactoryGirl.create(:quick_mix, autowire:false, user: user1, recording: recording, fully_uploaded: true)

      result = UserSync.deletables(user_id: user1.id, recording_ids: recording_ids)
      result.should eq(recording_ids)

      # make the quick_mix be not fully_uploaded, which should make it not be marked for deleting because we need to upload it
      quick_mix.fully_uploaded = false
      quick_mix.save!

      result = UserSync.deletables(user_id: user1.id, recording_ids: recording_ids)
      result.should eq([])

      quick_mix.fully_uploaded = true
      quick_mix.save!
      claim.discarded = false
      claim.save!

      result = UserSync.deletables(user_id: user1.id, recording_ids: recording_ids)
      result.should eq([])
    end
  end
end
