require 'spec_helper'

describe ChaptersController do
  include LoginMacros
  include RedirectExpectationHelper

  let(:user) { create(:user) }
  let!(:work) { create(:work, authors: [user.pseuds.first]) }
  let(:unposted_work) { create(:draft, authors: [user.pseuds.first]) }

  let(:co_creator) { create(:user) }

  let(:banned_user) { create(:user, banned: true) }
  let(:banned_users_work) do
    banned_user.update!(banned: false)
    work = create(:work, authors: [banned_user.pseuds.first, co_creator.pseuds.first])
    banned_user.update!(banned: true)
    work
  end
  let(:banned_users_work_chapter2) do
    banned_user.update!(banned: false)
    chapter = create(:chapter, work: banned_users_work, position: 2, authors: [banned_user.pseuds.first, co_creator.pseuds.first])
    banned_user.update!(banned: true)
    chapter
  end

  let(:suspended_user) { create(:user, suspended: true, suspended_until: 1.week.from_now) }
  let(:suspended_users_work) do
    suspended_user.update!(suspended: false, suspended_until: nil)
    work = create(:work, authors: [suspended_user.pseuds.first, co_creator.pseuds.first])
    suspended_user.update!(suspended: true, suspended_until: 1.week.from_now)
    work
  end
  let(:suspended_users_work_chapter2) do
    suspended_user.update!(suspended: false, suspended_until: nil)
    chapter = create(:chapter, work: suspended_users_work, position: 2, authors: [suspended_user.pseuds.first, co_creator.pseuds.first])
    suspended_user.update!(suspended: true, suspended_until: 1.week.from_now)
    chapter
  end

  describe "index" do
    it "redirects to work" do
      get :index, params: { work_id: work.id }
      it_redirects_to work_path(work.id)
    end
  end

  describe "manage" do
    context "when user is logged out" do
      it "errors and redirects to login" do
        get :manage, params: { work_id: work.id }
        it_redirects_to_with_error(new_user_session_path, "Sorry, you don't have permission to access the page you were trying to reach. Please log in.")
      end
    end

    context "when work owner is logged in" do
      before do
        fake_login_known_user(user)
      end

      it "errors and redirects to root path if work does not exist" do
        get :manage, params: { work_id: 0 }
        it_redirects_to_with_error(root_path, "Sorry, we couldn't find the work you were looking for.")
      end

      it "renders manage template" do
        get :manage, params: { work_id: work.id }
        expect(response).to render_template(:manage)
      end

      it "assigns @chapters to include draft chapters" do
        chapter = create(:chapter, :draft, work: work, position: 2)
        get :manage, params: { work_id: work.id }
        expect(assigns[:chapters]).to eq([work.chapters.first, chapter])
      end

      it "assigns @chapters to chapters in order" do
        chapter = create(:chapter, work: work, position: 2)
        get :manage, params: { work_id: work.id }
        expect(assigns[:chapters]).to eq([work.chapters.first, chapter])
      end
    end

    context "when other user is logged in" do
      it "errors and redirects to work" do
        fake_login
        get :manage, params: { work_id: work.id }
        it_redirects_to_with_error(work_path(work.id), "Sorry, you don't have permission to access the page you were trying to reach.")
      end
    end
  end

  describe "show" do
    context "when user is logged out" do
      it "renders show template" do
        get :show, params: { work_id: work.id, id: work.chapters.first }
        expect(response).to render_template(:show)
      end

      it "errors and redirects to login when work is restricted" do
        restricted_work = create(:work, restricted: true)
        get :show, params: { work_id: restricted_work.id, id: restricted_work.chapters.first }
        it_redirects_to(new_user_session_path(restricted: true))
      end

      it "assigns @chapters to only posted chapters" do
        chapter = create(:chapter, :draft, work: work)
        get :show, params: { work_id: work.id, id: chapter.id }
        expect(assigns[:chapters]).to eq([work.chapters.first])
      end

      it "errors and redirects to login when trying to view unposted chapter" do
        chapter = create(:chapter, :draft, work: work)
        get :show, params: { work_id: work.id, id: chapter.id }
        it_redirects_to_with_error(new_user_session_path, "Sorry, you don't have permission to access the page you were trying to reach. Please log in.")
      end
    end

    context "when work is adult" do
      render_views

      before do
        allow_any_instance_of(Work).to receive(:adult?).and_return true
      end

      it "stores adult preference in sessions when given" do
        get :show, params: { work_id: work.id, id: work.chapters.first, view_adult: true }
        expect(cookies[:view_adult]).to eq "true"
      end

      it "renders _adults template if work is adult and adult permission has not been given" do
        get :show, params: { work_id: work.id, id: work.chapters.first }
        expect(response).to render_template("works/_adult")
      end

      it "does not render _adults template if work is adult and adult permission has been given" do
        get :show, params: { work_id: work.id, id: work.chapters.first, view_adult: true }
        expect(response).not_to render_template("works/_adult")
      end
    end

    context "when work is not adult" do
      render_views
      it "does not render _adults template if work is not adult" do
        get :show, params: { work_id: work.id, id: work.chapters.first }
        expect(response).not_to render_template("works/_adult")
      end
    end

    it "redirects to chapter with selected_id" do
      chapter = create(:chapter, work: work, position: 2)
      get :show, params: { work_id: work.id, id: work.chapters.first, selected_id: chapter.id }
      it_redirects_to work_chapter_path(work_id: work.id, id: chapter.id)
    end

    it "errors and redirects to work if chapter is not found" do
      chapter = create(:chapter)
      get :show, params: { work_id: work.id, id: chapter.id }
      it_redirects_to_with_error(work_path(work), "Sorry, we couldn't find the chapter you were looking for.")
    end

    it "assigns @chapters to chapters in order" do
      chapter = create(:chapter, work: work, position: 2)
      get :show, params: { work_id: work.id, id: chapter.id }
      expect(assigns[:chapters]).to eq([work.chapters.first, chapter])
    end

    it "assigns @previous_chapter when not on first chapter" do
      chapter = create(:chapter, work: work, position: 2)
      get :show, params: { work_id: work.id, id: chapter.id }
      expect(assigns[:previous_chapter]).to eq(work.chapters.first)
    end

    it "does not assign @previous_chapter when on first chapter" do
      create(:chapter, work: work, position: 2)
      get :show, params: { work_id: work.id, id: work.chapters.first.id }
      expect(assigns[:previous_chapter]).to be_nil
    end

    it "assigns @next_chapter when not on last chapter" do
      chapter = create(:chapter, work: work, position: 2)
      get :show, params: { work_id: work.id, id: work.chapters.first.id }
      expect(assigns[:next_chapter]).to eq(chapter)
    end

    it "does not assign @next_chapter when on last chapter" do
      chapter = create(:chapter, work: work, position: 2)
      get :show, params: { work_id: work.id, id: chapter.id }
      expect(assigns[:next_chapter]).to be_nil
    end

    it "assigns @page_title with fandom, author name, work title, and chapter" do
      expect_any_instance_of(ChaptersController).to receive(:get_page_title).with("Testing", user.pseuds.first.name, "My title is long enough - Chapter 1").and_return("page title")
      get :show, params: { work_id: work.id, id: work.chapters.first.id }
      expect(assigns[:page_title]).to eq("page title")
    end

    it "assigns @page_title with unrevealed work" do
      allow_any_instance_of(Work).to receive(:unrevealed?).and_return(true)
      get :show, params: { work_id: work.id, id: work.chapters.first.id }
      expect(assigns[:page_title]).to eq("Mystery Work - Chapter 1")
    end

    it "assigns @page_title with anonymous work" do
      allow_any_instance_of(Work).to receive(:anonymous?).and_return(true)
      expect_any_instance_of(ChaptersController).to receive(:get_page_title).with("Testing", "Anonymous", "My title is long enough - Chapter 1").and_return("page title")
      get :show, params: { work_id: work.id, id: work.chapters.first.id }
      expect(assigns[:page_title]).to eq("page title")
    end

    context "when work has no fandom" do
      it "assigns @page_title with a placeholder for the fandom" do
        allow_any_instance_of(Work).to receive(:tag_groups).and_return("Fandom" => [])
        expect_any_instance_of(ChaptersController).to receive(:get_page_title).with("No fandom specified", user.pseuds.first.name, "#{work.title} - Chapter 1").and_return("page title")
        get :show, params: { work_id: work.id, id: work.chapters.first.id }
        expect(response).to have_http_status(:ok)
        expect(assigns[:page_title]).to eq("page title")
      end
    end

    it "assigns @kudos to non-anonymous kudos" do
      kudo = create(:kudo, commentable: work, user: create(:user))
      create(:kudo, commentable: work)
      get :show, params: { work_id: work.id, id: work.chapters.first.id }
      expect(assigns[:kudos]).to eq [kudo]
    end

    it "assigns instance variables correctly" do
      second_chapter = create(:chapter, work: work, position: 2)
      third_chapter = create(:chapter, work: work, position: 3)
      kudo = create(:kudo, commentable: work, user: create(:user))
      tag = create(:fandom)
      expect_any_instance_of(Work).to receive(:tag_groups).and_return("Fandom" => [tag])
      expect_any_instance_of(ChaptersController).to receive(:get_page_title).with(tag.name, user.pseuds.first.name, "My title is long enough - Chapter 2").and_return("page title")
      get :show, params: { work_id: work.id, id: second_chapter.id }
      expect(assigns[:work]).to eq work
      expect(assigns[:tag_groups]).to eq "Fandom" => [tag]
      expect(assigns[:chapter]).to eq second_chapter
      expect(assigns[:chapters]).to eq [work.chapters.first, second_chapter, third_chapter]
      expect(assigns[:previous_chapter]).to eq work.chapters.first
      expect(assigns[:next_chapter]).to eq third_chapter
      expect(assigns[:page_title]).to eq "page title"
      expect(assigns[:kudos]).to eq [kudo]
      expect(assigns[:subscription]).to be_nil
    end

    context "when work owner is logged in" do
      before do
        fake_login_known_user(user)
      end

      it "assigns @chapters to all chapters" do
        chapter = create(:chapter, :draft, work: work, position: 2)
        get :show, params: { work_id: work.id, id: chapter.id }
        expect(assigns[:chapters]).to eq([work.chapters.first, chapter])
      end
    end

    context "when other user is logged in" do
      before do
        fake_login
      end

      it "assigns @chapters to only posted chapters" do
        chapter = create(:chapter, :draft, work: work)
        get :show, params: { work_id: work.id, id: chapter.id }
        expect(assigns[:chapters]).to eq([work.chapters.first])
      end

      it "assigns @subscription to user's subscription when user is subscribed to work" do
        subscription = create(:subscription, subscribable: work, user: controller.current_user)
        get :show, params: { work_id: work, id: work.chapters.first.id }
        expect(assigns[:subscription]).to eq(subscription)
      end

      it "assigns @subscription to unsaved subscription when user is not subscribed to work" do
        get :show, params: { work_id: work, id: work.chapters.first.id }
        expect(assigns[:subscription]).to be_new_record
      end

      it "updates the reading history" do
        expect(Reading).to receive(:update_or_create).with(work, controller.current_user)
        get :show, params: { work_id: work.id, id: work.chapters.first.id }
      end
    end
  end

  describe "new" do
    context "when user is logged out" do
      it "errors and redirects to login" do
        get :new, params: { work_id: work.id }
        it_redirects_to_with_error(new_user_session_path, "Sorry, you don't have permission to access the page you were trying to reach. Please log in.")
      end
    end

    context "when work owner is logged in" do
      before do
        fake_login_known_user(user)
      end

      it "renders new template" do
        get :new, params: { work_id: work.id }
        expect(response).to render_template(:new)
      end

      it "errors and redirects to user page when user is suspended" do
        fake_login_known_user(suspended_user)
        get :new, params: { work_id: suspended_users_work.id }
        it_redirects_to_simple(user_path(suspended_user))
        expect(flash[:error]).to include("Your account has been suspended")
      end

      it "errors and redirects to user page when user is banned" do
        fake_login_known_user(banned_user)
        get :new, params: { work_id: banned_users_work.id }
        it_redirects_to_simple(user_path(banned_user))
        expect(flash[:error]).to include("Your account has been banned.")
      end
    end

    context "when other user is logged in" do
      before do
        fake_login
      end

      it "errors and redirects to work" do
        get :new, params: { work_id: work.id }
        it_redirects_to_with_error(work_path(work), "Sorry, you don't have permission to access the page you were trying to reach.")
      end
    end
  end

  describe "edit" do
    context "when user is logged out" do
      it "errors and redirects to login" do
        get :edit, params: { work_id: work.id, id: work.chapters.first.id }
        it_redirects_to_with_error(new_user_session_path, "Sorry, you don't have permission to access the page you were trying to reach. Please log in.")
      end
    end

    context "when logged in user owns the chapter" do
      before do
        fake_login_known_user(user)
      end

      it "renders edit template" do
        get :edit, params: { work_id: work.id, id: work.chapters.first.id }
        expect(response).to render_template(:edit)
      end

      it "errors and redirects to user page when user is suspended" do
        fake_login_known_user(suspended_user)
        get :edit, params: { work_id: suspended_users_work.id, id: suspended_users_work.chapters.first.id }
        it_redirects_to_simple(user_path(suspended_user))
        expect(flash[:error]).to include("Your account has been suspended")
      end

      it "renders edit template when user is banned" do
        fake_login_known_user(banned_user)
        get :edit, params: { work_id: banned_users_work.id, id: banned_users_work.chapters.first.id }
        expect(response).to render_template(:edit)
      end
    end

    context "when logged in user does not own the chapter" do
      before do
        fake_login
      end

      it "errors and redirects to work" do
        get :edit, params: { work_id: work.id, id: work.chapters.first.id }
        it_redirects_to_with_error(work_path(work), "Sorry, you don't have permission to access the page you were trying to reach.")
      end
    end
  end

  describe "PATCH #remove_user_creatorship" do
    context "when work is multichaptered and co-created" do
      let!(:co_created_chapter) { create(:chapter, work: work, authors: [user.pseuds.first, co_creator.pseuds.first]) }

      context "when logged in user also owns other chapters" do
        before do
          fake_login_known_user(user)
        end

        it "removes user from chapter, gives notice, and redirects to work" do
          patch :remove_user_creatorship, params: { id: co_created_chapter.id }

          expect(co_created_chapter.reload.pseuds).to eq [co_creator.pseuds.first]
          expect(work.reload.pseuds).to eq [user.pseuds.first, co_creator.pseuds.first]

          it_redirects_to_with_notice(work_path(work), "You have been removed as a creator from the chapter.")
        end
      end

      context "when logged in user only owns this chapter" do
        before do
          fake_login_known_user(co_creator)
        end

        it "removes user from chapter and work" do
          patch :remove_user_creatorship, params: { id: co_created_chapter.id }

          expect(co_created_chapter.reload.pseuds).to eq [user.pseuds.first]
          expect(work.reload.pseuds).to eq [user.pseuds.first]
        end
      end

      context "when the logged in user is suspended" do
        before do
          fake_login_known_user(suspended_user)
        end

        it "errors and redirects to user page" do
          suspended_users_work
          patch :remove_user_creatorship, params: { id: suspended_users_work_chapter2.id }

          expect(flash[:error]).to include("Your account has been suspended")
        end
      end

      context "when the logged in user is banned" do
        before do
          fake_login_known_user(banned_user)
        end

        it "removes user from chapter, gives notice, and redirects to work" do
          banned_users_work
          patch :remove_user_creatorship, params: { id: banned_users_work_chapter2.id }

          expect(banned_users_work_chapter2.reload.pseuds).to eq [co_creator.pseuds.first]
          expect(banned_users_work.reload.pseuds).to eq [co_creator.pseuds.first, banned_user.pseuds.first]

          it_redirects_to_with_notice(work_path(banned_users_work), "You have been removed as a creator from the chapter.")
        end
      end
    end
  end

  describe "create" do
    let(:chapter_attributes) { { content: "This doesn't matter" } }

    context "when user is logged out" do
      it "errors and redirects to login" do
        post :create, params: { work_id: work.id, chapter: chapter_attributes }
        it_redirects_to_with_error(new_user_session_path, "Sorry, you don't have permission to access the page you were trying to reach. Please log in.")
      end
    end

    context "when work owner is logged in" do
      before do
        fake_login_known_user(user)
        chapter_attributes[:author_attributes] = { ids: [user.pseuds.first.id] }
      end

      it "errors and redirects to user page when user is suspended" do
        fake_login_known_user(suspended_user)
        post :create, params: { work_id: suspended_users_work.id, chapter: chapter_attributes }
        it_redirects_to_simple(user_path(suspended_user))
        expect(flash[:error]).to include("Your account has been suspended")
      end

      it "errors and redirects to user page when user is banned" do
        fake_login_known_user(banned_user)
        post :create, params: { work_id: banned_users_work.id, chapter: chapter_attributes }
        it_redirects_to_simple(user_path(banned_user))
        expect(flash[:error]).to include("Your account has been banned.")
      end

      it "does not allow a user to submit only a pseud that is not theirs" do
        user2 = create(:user)
        chapter_attributes[:author_attributes] = { ids: [user2.pseuds.first.id] }
        expect { post :create, params: { work_id: work.id, chapter: chapter_attributes } }.not_to change { Chapter.count }
        expect(response).to render_template("new")
        expect(assigns[:chapter].errors.full_messages).to \
          include "You're not allowed to use that pseud."
      end

      it "adds a new chapter" do
        expect { post :create, params: { work_id: work.id, chapter: chapter_attributes } }.to change { Chapter.count }
        expect(work.chapters.count).to eq 2
      end

      it "updates the works wip length when given" do
        chapter_attributes[:wip_length] = 3
        expect(work.wip_length).to eq 1
        post :create, params: { work_id: work.id, chapter: chapter_attributes }
        expect(assigns[:work].wip_length).to eq 3
      end

      it "renders new if chapter has invalid pseuds" do
        chapter_attributes[:author_attributes] = { byline: "*impossible*" }
        post :create, params: { work_id: work.id, chapter: chapter_attributes }
        expect(response).to render_template(:new)
        expect(assigns[:chapter].errors.full_messages).to \
          include("Invalid creator: Could not find a pseud *impossible*.")
      end

      it "renders new if chapter has ambiguous pseuds" do
        create(:pseud, name: "ambiguous")
        create(:pseud, name: "ambiguous")
        chapter_attributes[:author_attributes] = { byline: "ambiguous" }
        post :create, params: { work_id: work.id, chapter: chapter_attributes }
        expect(response).to render_template(:new)
        expect(assigns[:chapter].errors.full_messages).to \
          include("Invalid creator: The pseud ambiguous is ambiguous.")
      end

      it "renders new if the edit button has been clicked" do
        post :create, params: { work_id: work.id, chapter: chapter_attributes, edit_button: true }
        expect(response).to render_template(:new)
      end

      it "redirects if the cancel button has been clicked" do
        post :create, params: { work_id: work.id, chapter: chapter_attributes, cancel_button: true }
        expect(response).to have_http_status :redirect
      end

      context "when neither posting nor previewing is explicitly specified" do
        it "does not update the work's version" do
          expect do
            post :create, params: { work_id: work.id, chapter: chapter_attributes }
          end.to avoid_changing { work.reload.major_version }
            .and avoid_changing { work.reload.minor_version }
        end
      end

      context "when the post button is clicked" do
        context "when the chapter and work are valid" do
          it "posts the chapter" do
            post :create, params: { work_id: work.id, chapter: chapter_attributes, post_without_preview_button: true }
            expect(assigns[:chapter].posted).to be true
          end

          it "updates cached chapter counts" do
            expect do
              post :create, params: { work_id: work.id, chapter: chapter_attributes, post_without_preview_button: true }
            end.to change { work.number_of_chapters }
              .from(1).to(2)
              .and change { work.number_of_posted_chapters }
              .from(1).to(2)
          end

          it "updates the work's major version" do
            expect do
              post :create, params: { work_id: work.id, chapter: chapter_attributes, post_without_preview_button: true }
            end.to change { work.reload.major_version }
              .from(1).to(2)
              .and avoid_changing { work.reload.minor_version }
          end

          it "posts the work if the work was not posted before" do
            post :create, params: { work_id: unposted_work.id, chapter: chapter_attributes, post_without_preview_button: true }
            expect(assigns[:work].posted).to be true
          end

          it "gives a notice and redirects to the posted chapter" do
            post :create, params: { work_id: work.id, chapter: chapter_attributes, post_without_preview_button: true }
            it_redirects_to_with_notice(work_chapter_path(work_id: work.id, id: assigns[:chapter].id), "Chapter has been posted!")
          end
        end

        context "when the chapter or work is not valid" do
          it "does not add a chapter" do
            expect { post :create, params: { work_id: work.id, chapter: { content: "" }, post_without_preview_button: true } }.to_not change(Chapter, :count)
          end

          it "renders new" do
            post :create, params: { work_id: work.id, chapter: { content: "" }, post_without_preview_button: true }
            expect(response).to render_template(:new)
          end
        end

        it "updates the work's revision date" do
          travel_to(1.day.ago) do
            work.touch
          end

          old_updated_at = work.updated_at

          post :create, params: { work_id: work.id, chapter: chapter_attributes, post_without_preview_button: true }
          expect(assigns[:work].updated_at).not_to eq(old_updated_at)
        end
      end

      context "when the preview button is clicked" do
        context "when the chapter and work are valid" do
          it "does not post the chapter" do
            post :create, params: { work_id: work.id, chapter: chapter_attributes, preview_button: true }
            expect(assigns[:chapter].posted).to be false
          end

          it "updates cached chapter counts" do
            expect do
              post :create, params: { work_id: work.id, chapter: chapter_attributes, preview_button: true }
            end.to change { work.number_of_chapters }
              .from(1).to(2)
              .and avoid_changing { work.number_of_posted_chapters }
          end

          it "does not update the work's version" do
            expect do
              post :create, params: { work_id: work.id, chapter: chapter_attributes, preview_button: true }
            end.to avoid_changing { work.reload.major_version }
              .and avoid_changing { work.reload.minor_version }
          end

          it "gives a notice that the chapter is a draft and redirects to the chapter preview" do
            post :create, params: { work_id: work.id, chapter: chapter_attributes, preview_button: true }
            it_redirects_to_with_notice(preview_work_chapter_path(work_id: work.id, id: assigns[:chapter].id), "This is a draft chapter in a posted work. It will be kept unless the work is deleted.")
          end

          it "gives a notice that the work and chapter are drafts and redirects to the chapter preview" do
            post :create, params: { work_id: unposted_work.id, chapter: chapter_attributes, preview_button: true }
            it_redirects_to_simple(preview_work_chapter_path(work_id: unposted_work.id, id: assigns[:chapter].id))
            expect(flash[:notice]).to include("This is a draft chapter in an unposted work")
          end
        end

        context "when the chapter or work is not valid" do
          it "does not add a chapter" do
            expect { post :create, params: { work_id: work.id, chapter: { content: "" }, preview_button: true } }.to_not change(Chapter, :count)
          end

          it "renders new" do
            post :create, params: { work_id: work.id, chapter: { content: "" }, preview_button: true }
            expect(response).to render_template(:new)
          end
        end
      end
    end

    context "when other user is logged in" do
      before do
        fake_login
      end

      context "when the user tries to add themselves as a coauthor" do
        before do
          chapter_attributes[:author_attributes] = { ids: [user.pseuds.first.id, controller.current_user.pseuds.first.id] }
        end

        it "errors and redirects to work" do
          post :create, params: { work_id: work.id, chapter: chapter_attributes }
          it_redirects_to_with_error(work_path(work), "Sorry, you don't have permission to access the page you were trying to reach.")
        end
      end
    end
  end

  describe "update" do
    let(:chapter_attributes) { { content: "This doesn't matter" } }

    context "when user is logged out" do
      it "errors and redirects to login" do
        put :update, params: { work_id: work.id, id: work.chapters.first.id, chapter: chapter_attributes }
        it_redirects_to_with_error(new_user_session_path, "Sorry, you don't have permission to access the page you were trying to reach. Please log in.")
      end
    end

    context "when work owner is logged in" do
      before do
        fake_login_known_user(user)
      end

      it "errors and redirects to user page when user is suspended" do
        fake_login_known_user(suspended_user)
        put :update, params: { work_id: suspended_users_work.id, id: suspended_users_work.chapters.first.id, chapter: chapter_attributes }
        it_redirects_to_simple(user_path(suspended_user))
        expect(flash[:error]).to include("Your account has been suspended")
      end

      it "errors and redirects to user page when user is banned" do
        fake_login_known_user(banned_user)
        put :update, params: { work_id: banned_users_work.id, id: banned_users_work.chapters.first.id, chapter: chapter_attributes }
        it_redirects_to_simple(user_path(banned_user))
        expect(flash[:error]).to include("Your account has been banned.")
      end

      it "does not allow a user to submit only a pseud that is not theirs" do
        user2 = create(:user)
        chapter_attributes[:author_attributes] = { ids: [user2.pseuds.first.id] }
        put :update, params: { work_id: work.id, id: work.chapters.first.id, chapter: chapter_attributes }
        expect(response).to render_template("edit")
        expect(assigns[:chapter].errors.full_messages).to \
          include "You're not allowed to use that pseud."
      end

      it "updates the work's wip length when given" do
        chapter_attributes[:wip_length] = 3
        expect(work.wip_length).to eq 1
        put :update, params: { work_id: work.id, id: work.chapters.first.id, chapter: chapter_attributes }
        expect(assigns[:work].wip_length).to eq 3
      end

      it "renders edit if chapter has invalid pseuds" do
        chapter_attributes[:author_attributes] = { byline: "*impossible*" }
        put :update, params: { work_id: work.id, id: work.chapters.first.id, chapter: chapter_attributes }
        expect(response).to render_template(:edit)
        expect(assigns[:chapter].errors.full_messages).to \
          include("Invalid creator: Could not find a pseud *impossible*.")
      end

      it "renders edit if chapter has ambiguous pseuds" do
        create(:pseud, name: "ambiguous")
        create(:pseud, name: "ambiguous")
        chapter_attributes[:author_attributes] = { byline: "ambiguous" }
        put :update, params: { work_id: work.id, id: work.chapters.first.id, chapter: chapter_attributes }
        expect(response).to render_template(:edit)
        expect(assigns[:chapter].errors.full_messages).to \
          include("Invalid creator: The pseud ambiguous is ambiguous.")
      end

      context "when the preview button is clicked" do
        it "assigns preview_mode to true" do
          put :update, params: { work_id: work.id, id: work.chapters.first.id, chapter: chapter_attributes, preview_button: true }
          expect(assigns[:preview_mode]).to be true
        end

        it "gives a notice if the chapter has been posted and renders preview" do
          put :update, params: { work_id: work.id, id: work.chapters.first.id, chapter: chapter_attributes, preview_button: true }
          expect(response).to render_template(:preview)
          expect(flash[:notice]).to include "This is a preview of what this chapter will look like after your changes have been applied."
        end

        it "gives a notice if the chapter has not been posted and renders preview" do
          unposted_chapter = create(:chapter, :draft, work: work, authors: [user.pseuds.first])
          put :update, params: { work_id: work.id, id: unposted_chapter.id, chapter: chapter_attributes, preview_button: true }
          expect(response).to render_template(:preview)
          expect(flash[:notice]).to include "This is a draft chapter in a posted work."
        end
      end

      it "redirects if the cancel button has been clicked" do
        put :update, params: { work_id: work.id, id: work.chapters.first.id, chapter: chapter_attributes, cancel_button: true }
        expect(response).to have_http_status :redirect
      end

      it "renders edit if the edit button has been clicked" do
        put :update, params: { work_id: work.id, id: work.chapters.first.id, chapter: chapter_attributes, edit_button: true }
        expect(response).to render_template(:edit)
      end

      it "updates the work's minor version" do
        expect do
          put :update, params: { work_id: work.id, id: work.chapters.first.id, chapter: chapter_attributes }
        end.to change { work.reload.minor_version }
          .from(0).to(1)
          .and avoid_changing { work.reload.major_version }
      end

      context "when the post button is clicked" do
        context "when the chapter and work are valid" do
          it "posts the chapter" do
            put :update, params: { work_id: work.id, id: work.chapters.first.id, chapter: chapter_attributes, post_button: true }
            expect(assigns[:chapter].posted).to be true
          end

          it "posts the work if the work was not posted before" do
            pending "multi-chapter works should post when chapter is posted"
            put :update, params: { work_id: unposted_work.id, id: unposted_work.chapters.first.id, chapter: chapter_attributes, post_button: true }
            expect(assigns[:work].posted).to be true
          end

          it "gives a notice if the chapter was already posted and redirects to the posted chapter" do
            put :update, params: { work_id: work.id, id: work.chapters.first.id, chapter: chapter_attributes, post_button: true }
            it_redirects_to_with_notice(work_chapter_path(work_id: work.id, id: work.chapters.first.id), "Chapter was successfully updated.")
          end

          it "gives a notice if the chapter was not already posted and redirects to the posted chapter" do
            unposted_chapter = create(:chapter, :draft, work: work, authors: [user.pseuds.first])
            put :update, params: { work_id: work.id, id: unposted_chapter.id, chapter: chapter_attributes, post_button: true }
            it_redirects_to_with_notice(work_chapter_path(work_id: work.id, id: unposted_chapter.id), "Chapter was successfully posted.")
          end

          it "updates the work's minor version if the chapter was already posted" do
            expect do
              put :update, params: { work_id: work.id, id: work.chapters.first.id, chapter: chapter_attributes, post_button: true }
            end.to change { work.reload.minor_version }
              .from(0).to(1)
              .and avoid_changing { work.reload.major_version }
          end

          it "updates the work's major version if the chapter was not already posted" do
            unposted_chapter = create(:chapter, :draft, work: work, authors: [user.pseuds.first])
            expect do
              put :update, params: { work_id: work.id, id: unposted_chapter.id, chapter: chapter_attributes, post_button: true }
            end.to change { work.reload.major_version }
              .from(1).to(2)
              .and avoid_changing { work.reload.minor_version }
          end
        end

        context "when the chapter or work is not valid" do
          it "does not update the chapter" do
            put :update, params: { work_id: work.id, id: work.chapters.first.id, chapter: { content: "" }, post_button: true }
            expect(assigns[:chapter]).to eq work.chapters.first
          end

          it "renders edit" do
            put :update, params: { work_id: work.id, id: work.chapters.first.id, chapter: { content: "" }, post_button: true }
            expect(response).to render_template(:edit)
          end
        end

        it "updates the work's revision date" do
          travel_to(1.day.ago) do
            work.touch
          end

          old_updated_at = work.updated_at

          put :update, params: { work_id: work.id, id: work.chapters.first.id, chapter: chapter_attributes, post_button: true }
          expect(assigns[:work].updated_at).not_to eq(old_updated_at)
        end
      end

      context "when the post button is clicked" do
        it "posts the chapter" do
          put :update, params: { work_id: work.id, id: work.chapters.first.id, chapter: chapter_attributes, post_without_preview_button: true }
          expect(assigns[:chapter].posted).to be true
        end
      end
    end

    context "when other user is logged in" do
      before do
        fake_login
      end

      context "when the user tries to add themselves as a coauthor" do
        before do
          chapter_attributes[:author_attributes] = { ids: [user.pseuds.first.id, controller.current_user.pseuds.first.id] }
        end

        it "errors and redirects to work" do
          put :update, params: { work_id: work.id, id: work.chapters.first.id, chapter: chapter_attributes }
          it_redirects_to_with_error(work_path(work), "Sorry, you don't have permission to access the page you were trying to reach.")
        end
      end
    end
  end

  describe "update_positions" do
    let(:chapter1) { work.chapters.first }
    let!(:chapter2) { create(:chapter, :draft, work: work, position: 2, authors: [user.pseuds.first]) }
    let!(:chapter3) { create(:chapter, work: work, position: 3, authors: [user.pseuds.first]) }
    let!(:chapter4) { create(:chapter, work: work, position: 4, authors: [user.pseuds.first]) }

    context "when user is logged out" do
      it "errors and redirects to login" do
        post :update_positions, params: { work_id: work.id, chapter: [chapter1, chapter3, chapter2, chapter4] }
        it_redirects_to_with_error(new_user_session_path, "Sorry, you don't have permission to access the page you were trying to reach. Please log in.")
      end
    end

    context "when work owner is logged in" do
      before do
        fake_login_known_user(user)
      end

      context "when passing params[:chapters]" do
        it "updates the positions of the chapters" do
          post :update_positions, params: { work_id: work.id, chapters: [1, 3, 2, 4] }
          expect(chapter1.reload.position).to eq(1)
          expect(chapter2.reload.position).to eq(3)
          expect(chapter3.reload.position).to eq(2)
          expect(chapter4.reload.position).to eq(4)
        end

        it "preserves ordering if order values are all empty" do
          post :update_positions, params: { work_id: work.id, chapters: ["", "", "", ""] }
          expect(chapter1.reload.position).to eq(1)
          expect(chapter2.reload.position).to eq(2)
          expect(chapter3.reload.position).to eq(3)
          expect(chapter4.reload.position).to eq(4)
        end

        it "preserves ordering for empty values" do
          post :update_positions, params: { work_id: work.id, chapters: ["", "", "", 1] }
          expect(chapter1.reload.position).to eq(2)
          expect(chapter2.reload.position).to eq(3)
          expect(chapter3.reload.position).to eq(4)
          expect(chapter4.reload.position).to eq(1)
        end

        it "gives a notice and redirects to work" do
          post :update_positions, params: { work_id: work.id, chapters: [1, 3, 2, 4] }
          it_redirects_to_with_notice(work, "Chapter order has been successfully updated.")
        end
      end

      context "when passing params[:chapter]" do
        it "updates the positions of the chapters" do
          post :update_positions, params: { work_id: work.id, chapter: [chapter1, chapter3, chapter2, chapter4], format: :js }
          expect(chapter1.reload.position).to eq(1)
          expect(chapter2.reload.position).to eq(3)
          expect(chapter3.reload.position).to eq(2)
          expect(chapter4.reload.position).to eq(4)
        end
      end

      context "when the logged in user is suspended" do
        before do
          fake_login_known_user(suspended_user)
        end

        it "errors and redirects to user page" do
          post :update_positions, params: { work_id: suspended_users_work.id, chapter: [suspended_users_work_chapter2, suspended_users_work.chapters.first] }
          expect(flash[:error]).to include("Your account has been suspended")
        end
      end

      context "when the logged in user is banned" do
        before do
          fake_login_known_user(banned_user)
        end

        it "errors and redirects to user page" do
          post :update_positions, params: { work_id: banned_users_work.id, chapter: [banned_users_work_chapter2, banned_users_work.chapters.first] }
          expect(flash[:error]).to include("Your account has been banned")
        end
      end
    end
  end

  describe "preview" do
    context "when user is logged out" do
      it "errors and redirects to login" do
        get :preview, params: { work_id: work.id, id: work.chapters.first.id }
        it_redirects_to_with_error(new_user_session_path, "Sorry, you don't have permission to access the page you were trying to reach. Please log in.")
      end
    end

    context "when work owner is logged in" do
      before do
        fake_login_known_user(user)
      end

      it "renders preview template" do
        get :preview, params: { work_id: work.id, id: work.chapters.first.id }
        expect(response).to render_template(:preview)
      end

      it "assigns instance variables correctly" do
        get :preview, params: { work_id: work.id, id: work.chapters.first.id }
        expect(assigns[:work]).to eq work
        expect(assigns[:chapter]).to eq work.chapters.first
        expect(assigns[:preview_mode]).to be true
      end
    end

    context "when other user is logged in" do
      before do
        fake_login
      end

      it "errors and redirects to work" do
        get :preview, params: { work_id: work.id, id: work.chapters.first.id }
        it_redirects_to_with_error(work_path(work), "Sorry, you don't have permission to access the page you were trying to reach.")
      end
    end
  end

  describe "post" do
    before do
      @chapter_to_post = create(:chapter, :draft, work: work, authors: [user.pseuds.first], position: 2)
    end

    context "when user is logged out" do
      it "errors and redirects to login" do
        post :post, params: { work_id: work.id, id: @chapter_to_post.id }
        it_redirects_to_with_error(new_user_session_path, "Sorry, you don't have permission to access the page you were trying to reach. Please log in.")
      end
    end

    context "when work owner is logged in" do
      before do
        fake_login_known_user(user)
      end

      context "when the chapter and work are valid" do
        it "posts the chapter and redirects to work" do
          post :post, params: { work_id: work.id, id: @chapter_to_post.id }
          expect(assigns[:chapter].posted).to be true
          it_redirects_to_with_notice(work, "Chapter has been posted!")
        end

        it "updates the work's major version when posted" do
          expect do
            post :post, params: { work_id: work.id, id: @chapter_to_post.id }
          end.to change { work.reload.major_version }
            .from(1).to(2)
            .and avoid_changing { work.reload.minor_version }
        end

        it "posts the work if the work was not posted before" do
          post :post, params: { work_id: unposted_work.id, id: unposted_work.chapters.first.id }
          expect(assigns[:work].posted).to be true
        end
      end

      context "when the chapter or work is not valid" do
        before do
          allow_any_instance_of(Chapter).to receive(:save).and_return(false)
        end

        it "does not update the chapter" do
          post :post, params: { work_id: work.id, id: @chapter_to_post.id }
          expect(assigns[:chapter]).to eq @chapter_to_post
        end

        it "renders preview" do
          post :post, params: { work_id: work.id, id: @chapter_to_post.id }
          expect(response).to render_template(:preview)
        end
      end

      it "updates the work's revision date" do
        travel_to(1.day.ago) do
          work.touch
        end

        old_updated_at = work.updated_at

        post :post, params: { work_id: work.id, id: @chapter_to_post.id }
        expect(assigns[:work].updated_at).not_to eq(old_updated_at)
      end
    end

    context "when other user is logged in" do
      before do
        fake_login
      end

      it "errors and redirects to work" do
        post :post, params: { work_id: work.id, id: @chapter_to_post.id }
        it_redirects_to_with_error(work_path(work), "Sorry, you don't have permission to access the page you were trying to reach.")
      end
    end
  end

  describe "confirm_delete" do
    context "when user is logged out" do
      it "errors and redirects to work" do
        get :confirm_delete, params: { work_id: work.id, id: work.chapters.first.id }
        it_redirects_to_with_error(work_path(work), "Sorry, you don't have permission to access the page you were trying to reach. Please log in.")
      end
    end

    context "when work owner is logged in" do
      before do
        fake_login_known_user(user)
      end

      it "renders confirm delete template" do
        get :confirm_delete, params: { work_id: work.id, id: work.chapters.first.id }
        expect(response).to render_template(:confirm_delete)
      end

      it "assigns instance variables correctly" do
        get :confirm_delete, params: { work_id: work.id, id: work.chapters.first.id }
        expect(assigns[:work]).to eq work
        expect(assigns[:chapter]).to eq work.chapters.first
      end
    end

    context "when other user is logged in" do
      before do
        fake_login
      end

      it "errors and redirects to work" do
        get :confirm_delete, params: { work_id: work.id, id: work.chapters.first.id }
        it_redirects_to_with_error(work_path(work), "Sorry, you don't have permission to access the page you were trying to reach.")
      end
    end
  end

  describe "destroy" do
    context "when user is logged out" do
      it "errors and redirects to login" do
        pending "clean up chapter filters"
        delete :destroy, params: { work_id: work.id, id: work.chapters.first.id }
        it_redirects_to_with_error(new_user_session_path, "Sorry, you don't have permission to access the page you were trying to reach. Please log in.")
      end
    end

    context "when work owner is logged in" do
      before do
        fake_login_known_user(user)
      end

      context "when work has one chapter" do
        it "redirects to edit work" do
          delete :destroy, params: { work_id: work.id, id: work.chapters.first.id }
          it_redirects_to_with_error(edit_work_path(work), "You can't delete the only chapter in your work. If you want to delete the work, choose \"Delete Work\".")
        end
      end

      context "when work has more than one chapter" do
        let!(:chapter2) { create(:chapter, work: work, position: 2, authors: [user.pseuds.first]) }

        it "updates the work's minor version" do
          expect do
            delete :destroy, params: { work_id: work.id, id: chapter2.id }
          end.to change { work.reload.minor_version }
            .from(0).to(1)
            .and avoid_changing { work.reload.major_version }
        end

        it "updates the work's revision date" do
          travel_to(1.day.ago) do
            work.touch
          end

          old_updated_at = work.updated_at

          delete :destroy, params: { work_id: work.id, id: chapter2.id }
          expect(assigns[:work].updated_at).not_to eq(old_updated_at)
        end

        it "updates cached chapter counts" do
          expect do
            delete :destroy, params: { work_id: work.id, id: chapter2.id }
          end.to change { work.number_of_chapters }
            .from(2).to(1)
            .and change { work.number_of_posted_chapters }
            .from(2).to(1)
        end

        it "gives a notice that the chapter was deleted and redirects to work" do
          delete :destroy, params: { work_id: work.id, id: chapter2.id }
          it_redirects_to_with_notice(work, "The chapter was successfully deleted.")
        end

        it "gives a notice that the draft chapter was deleted if the chapter was a draft and redirects to work" do
          chapter2.posted = false
          chapter2.save
          delete :destroy, params: { work_id: work.id, id: chapter2.id }
          it_redirects_to_with_notice(work, "The chapter draft was successfully deleted.")
        end

        it "errors and redirects to work when chapter is not deleted" do
          allow_any_instance_of(Chapter).to receive(:destroy).and_return(false)
          delete :destroy, params: { work_id: work.id, id: chapter2.id }
          it_redirects_to_with_error(work, "Something went wrong. Please try again.")
        end

        it "does not reorder chapters when deleting the last chapter" do
          chapter1 = work.chapters.first
          delete :destroy, params: { work_id: work.id, id: chapter2.id }
          expect(work.reload.chapters_in_order).to eq([chapter1])
          expect(work.reload.chapters_in_order.first.position).to eq(1)
        end

        it "updates chapter positions when deleting the first chapter of a two chapter work" do
          delete :destroy, params: { work_id: work.id, id: work.chapters.first.id }
          expect(work.reload.chapters_in_order).to eq([chapter2])
          expect(work.reload.chapters_in_order.first.position).to eq(1)
        end

        it "maintains chapter order when deleting the first chapter of a >3 chapter work" do
          chapter3 = create(:chapter, work: work, position: 3, authors: [user.pseuds.first])
          chapter4 = create(:chapter, :draft, work: work, position: 4, authors: [user.pseuds.first])
          chapter5 = create(:chapter, work: work, position: 5, authors: [user.pseuds.first])
          delete :destroy, params: { work_id: work.id, id: work.chapters.first.id }
          work.reload
          posted_chapters = work.chapters_in_order
          expect(posted_chapters).to eq([chapter2, chapter3, chapter5])
          expect(posted_chapters.map(&:position)).to eq([1, 2, 4])

          all_chapters = work.chapters_in_order(include_drafts: true)
          expect(all_chapters).to eq([chapter2, chapter3, chapter4, chapter5])
          expect(all_chapters.map(&:position)).to eq([1, 2, 3, 4])
        end

        it "reorders chapters properly when deleting a mid-work chapter" do
          chapter1 = work.chapters.first
          chapter3 = create(:chapter, :draft, work: work, position: 3, authors: [user.pseuds.first])
          chapter4 = create(:chapter, work: work, position: 4, authors: [user.pseuds.first])
          delete :destroy, params: { work_id: work.id, id: chapter2.id }
          work.reload
          posted_chapters = work.chapters_in_order
          expect(posted_chapters).to eq([chapter1, chapter4])
          expect(posted_chapters.map(&:position)).to eq([1, 3])

          all_chapters = work.chapters_in_order(include_drafts: true)
          expect(all_chapters).to eq([chapter1, chapter3, chapter4])
          expect(all_chapters.map(&:position)).to eq([1, 2, 3])
        end
      end

      context "when work has more than one chapter and one is a draft" do
        let!(:chapter2) { create(:chapter, work: work, posted: false, position: 2, authors: [user.pseuds.first]) }

        it "updates cached chapter counts" do
          expect do
            delete :destroy, params: { work_id: work.id, id: chapter2.id }
          end.to change { work.number_of_chapters }
            .from(2).to(1)
            .and avoid_changing { work.number_of_posted_chapters }
        end

        it "does not update the work's version" do
          expect do
            delete :destroy, params: { work_id: work.id, id: chapter2.id }
          end.to avoid_changing { work.reload.major_version }
            .and avoid_changing { work.reload.minor_version }
        end
      end

      context "when work has more than one chapter and all but one are drafts" do
        let!(:chapter2) { create(:chapter, work: work, posted: false, position: 2, authors: [user.pseuds.first]) }

        it "cannot delete the posted chapter" do
          delete :destroy, params: { work_id: work.id, id: work.chapters.first.id }
          it_redirects_to_with_error(edit_work_path(work), "You can't delete the only chapter in your work. If you want to delete the work, choose \"Delete Work\".")
        end
      end

      context "when the logged in user is suspended" do
        before do
          fake_login_known_user(suspended_user)
        end

        it "errors and redirects to user page" do
          delete :destroy, params: { work_id: suspended_users_work.id, id: suspended_users_work_chapter2.id }
          expect(flash[:error]).to include("Your account has been suspended")
        end
      end

      context "when the logged in user is banned" do
        before do
          fake_login_known_user(banned_user)
        end

        it "gives a notice that the chapter was deleted and redirects to work" do
          delete :destroy, params: { work_id: banned_users_work.id, id: banned_users_work_chapter2.id }
          it_redirects_to_with_notice(banned_users_work, "The chapter was successfully deleted.")
        end
      end
    end

    context "when other user is logged in" do
      before do
        fake_login
      end

      it "errors and redirects to work" do
        delete :destroy, params: { work_id: work.id, id: work.chapters.first.id }
        it_redirects_to_with_error(work_path(work), "Sorry, you don't have permission to access the page you were trying to reach.")
      end
    end
  end
end
