require "spec_helper"

describe BookmarksHelper do
  let(:bookmarker) { create(:user) }
  let(:external_work_bookmark) { create(:external_work_bookmark, pseud: bookmarker.default_pseud) }
  let(:series_bookmark) { create(:series_bookmark, pseud: bookmarker.default_pseud) }
  let(:work_bookmark) { create(:bookmark, pseud: bookmarker.default_pseud) }
  let(:external_work) { external_work_bookmark.bookmarkable }
  let(:series) { series_bookmark.bookmarkable }
  let(:series_creator) { series.users.first }
  let(:work) { work_bookmark.bookmarkable }
  let(:work_creator) { work.users.first }

  describe "#css_classes_for_bookmark_blurb" do
    let(:default_classes) { "bookmark blurb group" }

    context "when bookmarkable is nil" do
      it "returns string with default classes and bookmarker info" do
        work.destroy!
        result = helper.css_classes_for_bookmark_blurb(work_bookmark.reload)
        expect(result).to eq("#{default_classes} user-#{bookmarker.id}")
      end
    end

    context "when bookmarkable is ExternalWork" do
      it "returns string with default classes, creation info, and bookmarker info" do
        result = helper.css_classes_for_bookmark_blurb(external_work_bookmark)
        expect(result).to eq("#{default_classes} external-work-#{external_work.id} user-#{bookmarker.id}")
      end

      context "when bookmark is updated" do
        it "returns same string with different cache key" do
          original_cache_key = "#{external_work.cache_key_with_version}_#{external_work_bookmark.cache_key}/blurb_css_classes"
          expected_classes = "#{default_classes} external-work-#{external_work.id} user-#{bookmarker.id}"
          expect(helper.css_classes_for_bookmark_blurb(external_work_bookmark)).to eq(expected_classes)

          travel(1.day)
          external_work_bookmark.update!(bookmarker_notes: "New note")
          expect(helper.css_classes_for_bookmark_blurb(external_work_bookmark.reload)).to eq(expected_classes)
          expect(original_cache_key).not_to eq("#{external_work.cache_key_with_version}_#{external_work_bookmark.cache_key}/blurb_css_classes")
          travel_back
        end
      end
    end

    context "when bookmarkable is Series" do
      it "returns string with default classes, creation and creator info, and bookmarker info" do
        result = helper.css_classes_for_bookmark_blurb(series_bookmark)
        expect(result).to eq("#{default_classes} series-#{series.id} user-#{series_creator.id} user-#{bookmarker.id}")
      end

      context "when bookmarker is also series creator" do
        before do
          series.creatorships.first.update!(pseud_id: bookmarker.default_pseud_id)
        end

        it "only includes the user id once" do
          result = helper.css_classes_for_bookmark_blurb(series_bookmark)
          expect(result).to eq("#{default_classes} series-#{series.id} user-#{bookmarker.id}")
        end
      end

      context "when series is updated" do
        context "when new creator is added" do
          let(:series_creator2) { create(:user) }

          it "returns updated string" do
            original_cache_key = "#{series.cache_key_with_version}_#{series_bookmark.cache_key}/blurb_css_classes"
            original_classes = "#{default_classes} series-#{series.id} user-#{series_creator.id} user-#{bookmarker.id}"
            expect(helper.css_classes_for_bookmark_blurb(series_bookmark)).to eq(original_classes)

            travel(1.day)
            new_classes = "#{default_classes} series-#{series.id} user-#{series_creator.id} user-#{series_creator2.id} user-#{bookmarker.id}"
            series.creatorships.find_or_create_by(pseud_id: series_creator2.default_pseud_id)
            expect(helper.css_classes_for_bookmark_blurb(series_bookmark.reload)).to eq(new_classes)
            expect(original_cache_key).not_to eq("#{series.cache_key_with_version}_#{series_bookmark.cache_key}/blurb_css_classes")
            travel_back
          end
        end
      end

      context "when bookmark is updated" do
        it "returns same string with different cache key" do
          original_cache_key = "#{series.cache_key_with_version}_#{series_bookmark.cache_key}/blurb_css_classes"
          expected_classes = "#{default_classes} series-#{series.id} user-#{series_creator.id} user-#{bookmarker.id}"
          expect(helper.css_classes_for_bookmark_blurb(series_bookmark)).to eq(expected_classes)

          travel(1.day)
          series_bookmark.update!(bookmarker_notes: "New note")
          expect(helper.css_classes_for_bookmark_blurb(series_bookmark.reload)).to eq(expected_classes)
          expect(original_cache_key).not_to eq("#{series.cache_key_with_version}_#{series_bookmark.cache_key}/blurb_css_classes")
          travel_back
        end
      end
    end

    context "when bookmarkable is Work" do
      it "returns string with default classes, creation and creator info, and bookmarker info" do
        result = helper.css_classes_for_bookmark_blurb(work_bookmark)
        expect(result).to eq("#{default_classes} work-#{work.id} user-#{work_creator.id} user-#{bookmarker.id}")
      end

      context "when bookmarker is also work creator" do
        before do
          work.creatorships.first.update!(pseud_id: bookmarker.default_pseud_id)
        end

        it "only includes the user id once" do
          result = helper.css_classes_for_bookmark_blurb(work_bookmark)
          expect(result).to eq("#{default_classes} work-#{work.id} user-#{bookmarker.id}")
        end
      end

      context "when work is updated" do
        context "when new creator is added" do
          let(:work_creator2) { create(:user) }

          it "returns updated string" do
            original_cache_key = "#{work.cache_key_with_version}_#{work_bookmark.cache_key}/blurb_css_classes"
            original_classes = "#{default_classes} work-#{work.id} user-#{work_creator.id} user-#{bookmarker.id}"
            expect(helper.css_classes_for_bookmark_blurb(work_bookmark)).to eq(original_classes)

            travel(1.day)
            new_classes = "#{default_classes} work-#{work.id} user-#{work_creator.id} user-#{work_creator2.id} user-#{bookmarker.id}"
            work.creatorships.find_or_create_by(pseud_id: work_creator2.default_pseud_id)
            expect(helper.css_classes_for_bookmark_blurb(work_bookmark.reload)).to eq(new_classes)
            expect(original_cache_key).not_to eq("#{work.cache_key_with_version}_#{work_bookmark.cache_key}/blurb_css_classes")
            travel_back
          end
        end
      end

      context "when bookmark is updated" do
        it "returns same string with different cache key" do
          original_cache_key = "#{work.cache_key_with_version}_#{work_bookmark.cache_key}/blurb_css_classes"
          expected_classes = "#{default_classes} work-#{work.id} user-#{work_creator.id} user-#{bookmarker.id}"
          expect(helper.css_classes_for_bookmark_blurb(work_bookmark)).to eq(expected_classes)

          travel(1.day)
          work_bookmark.update!(bookmarker_notes: "New note")
          expect(helper.css_classes_for_bookmark_blurb(work_bookmark.reload)).to eq(expected_classes)
          expect(original_cache_key).not_to eq("#{work.cache_key_with_version}_#{work_bookmark.cache_key}/blurb_css_classes")
          travel_back
        end
      end
    end
  end

  describe "#css_classes_for_bookmarkable_blurb" do
    let(:default_classes) { "bookmark blurb group" }

    context "when bookmarkable is nil" do
      it "returns a string with default classes" do
        result = helper.css_classes_for_bookmarkable_blurb(nil)
        expect(result).to eq(default_classes)
      end
    end

    context "when bookmarkable is ExternalWork" do
      it "returns string with default classes and creation info" do
        result = helper.css_classes_for_bookmarkable_blurb(external_work)
        expect(result).to eq("#{default_classes} external-work-#{external_work.id}")
      end
    end

    context "when bookmarkable is Series" do
      it "returns string with default classes and creation and creator info" do
        result = helper.css_classes_for_bookmarkable_blurb(series)
        expect(result).to eq("#{default_classes} series-#{series.id} user-#{series_creator.id}")
      end
    end

    context "when bookmarkable is Work" do
      it "returns string with default classes and creation and creator info" do
        result = helper.css_classes_for_bookmarkable_blurb(work)
        expect(result).to eq("#{default_classes} work-#{work.id} user-#{work_creator.id}")
      end
    end
  end

  describe "#css_classes_for_bookmark_blurb_short" do 
    let(:default_classes) { "user short blurb group" }

    context "when logged in as bookmarker" do
      before do
        allow(helper).to receive(:current_user).and_return(bookmarker)
      end

      context "when bookmarkable is ExternalWork" do
        it "returns string with default classes, bookmarker info, and ownership indicator" do
          result = helper.css_classes_for_bookmark_blurb_short(external_work_bookmark)
          expect(result).to eq("own #{default_classes} user-#{bookmarker.id}")
        end
      end

      context "when bookmarkable is Series" do
        it "returns string with default classes, bookmarker info, and ownership indicator" do
          result = helper.css_classes_for_bookmark_blurb_short(series_bookmark)
          expect(result).to eq("own #{default_classes} user-#{bookmarker.id}")
        end
      end

      context "when bookmarkable is Work" do
        it "returns string with default classes, bookmarker info, and ownership indicator" do
          result = helper.css_classes_for_bookmark_blurb_short(work_bookmark)
          expect(result).to eq("own #{default_classes} user-#{bookmarker.id}")
        end
      end
    end

    context "when not logged in as bookmarker" do
      before do
        allow(helper).to receive(:current_user)
      end

      context "when bookmarkable is ExternalWork" do
        it "returns string with default classes and bookmarker info" do
          result = helper.css_classes_for_bookmark_blurb_short(external_work_bookmark)
          expect(result).to eq("#{default_classes} user-#{bookmarker.id}")
        end
      end

      context "when bookmarkable is Series" do
        it "returns string with default classes and bookmarker info" do
          result = helper.css_classes_for_bookmark_blurb_short(series_bookmark)
          expect(result).to eq("#{default_classes} user-#{bookmarker.id}")
        end
      end

      context "when bookmarkable is Work" do
        it "returns string with default classes and bookmarker info" do
          result = helper.css_classes_for_bookmark_blurb_short(work_bookmark)
          expect(result).to eq("#{default_classes} user-#{bookmarker.id}")
        end
      end
    end
  end
  
  describe "#bookmark_if_exists" do
    before do
      allow(helper).to receive(:current_user) { user.reload }
      allow(helper).to receive(:logged_in?) { user.present? }
    end

    let(:work) { create(:work) }

    context "when logged out" do
      let(:user) { nil }

      it "returns nil" do
        expect(helper.bookmark_if_exists(work)).to eq(nil)
      end
    end

    context "when logged in" do
      let(:user) { create(:user) }
      let(:default) { user.default_pseud }
      let(:pseud) { create(:pseud, user: user) }

      context "when the user has no bookmarks" do
        it "returns nil" do
          expect(helper.bookmark_if_exists(work)).to eq(nil)
        end
      end

      context "when the user has a bookmark for another work" do
        let!(:bookmark) { create(:bookmark, pseud: default) }

        it "returns nil" do
          expect(helper.bookmark_if_exists(work)).to eq(nil)
        end
      end

      context "when the user has one bookmark for the work" do
        let!(:bookmark) do
          create(:bookmark, bookmarkable: work, pseud: default)
        end

        it "returns the bookmark" do
          expect(helper.bookmark_if_exists(work)).to eq(bookmark)
        end
      end

      context "when the user has multiple bookmarks for the same work" do
        context "when one of the bookmarks is by the user's default pseud" do
          let!(:default_bookmark) do
            create(:bookmark, bookmarkable: work, pseud: default)
          end

          let!(:other_bookmark) do
            create_invalid(:bookmark, bookmarkable: work, pseud: pseud)
          end

          it "returns the bookmark by the default pseud" do
            expect(helper.bookmark_if_exists(work)).to eq(default_bookmark)
          end
        end

        context "when all of the bookmarks are by the user's default pseud" do
          let!(:older_bookmark) do
            create(:bookmark, bookmarkable: work, pseud: default)
          end

          let!(:newer_bookmark) do
            create_invalid(:bookmark, bookmarkable: work, pseud: default)
          end

          it "returns the most recent bookmark" do
            expect(helper.bookmark_if_exists(work)).to eq(newer_bookmark)
          end
        end

        context "when none of the bookmarks are by the user's default pseud" do
          let!(:older_bookmark) do
            create(:bookmark, bookmarkable: work, pseud: pseud)
          end

          let!(:newer_bookmark) do
            create_invalid(:bookmark, bookmarkable: work, pseud: pseud)
          end

          it "returns the most recent bookmark" do
            expect(helper.bookmark_if_exists(work)).to eq(newer_bookmark)
          end
        end
      end
    end
  end
end
