require "spec_helper"

describe UserMailer do
  describe "#change_email" do
    let(:login) { "changer" }
    let(:new_email) { "new@example.com" }
    let(:user) { create(:user, login: login) }
    subject(:email) { UserMailer.change_email(user.id, "old@example.com", new_email) }

    # Test the headers
    it_behaves_like "an email with a valid sender"

    it "has the correct subject line" do
      subject = "[#{ArchiveConfig.APP_SHORT_NAME}] Email change request"
      expect(email.subject).to eq(subject)
    end

    # Test both body contents
    it_behaves_like "a multipart email"

    it_behaves_like "a translated email"

    describe "HTML version" do
      it "has the correct content" do
        expect(email).to have_html_part_content("Hi <b")
        expect(email).to have_html_part_content(">#{login}</b>,")
        expect(email).to have_html_part_content("If you made this request, check your email at <a")
        expect(email).to have_html_part_content("#{new_email}</a> within #{ArchiveConfig.DAYS_UNTIL_RESET_PASSWORD_LINK_EXPIRES} days to confirm your email change")

        expect(email).to have_html_part_content("don't understand why you received this email, please <a")
        expect(email).to have_html_part_content(">contact Policy & Abuse</a>.")
      end
    end

    describe "text version" do
      it "has the correct content" do
        expect(email).to have_text_part_content("Hi #{login},")
        expect(email).to have_text_part_content("If you made this request, check your email at #{new_email} within #{ArchiveConfig.DAYS_UNTIL_RESET_PASSWORD_LINK_EXPIRES} days to confirm your email change")

        expect(email).to have_text_part_content("If you don't understand why you received this email, please contact Policy & Abuse")
      end
    end
  end

  describe "#change_username" do
    let(:old_username) { "olduser" }
    let(:new_username) { "newuser" }
    let(:user) { create(:user, login: new_username, renamed_at: Time.zone.parse("2025-01-01 00:00:00")) }
    subject(:email) { UserMailer.change_username(user, old_username) }

    before do
      allow(ArchiveConfig).to receive(:USER_RENAME_LIMIT_DAYS).and_return(5)
    end

    # Test the headers
    it_behaves_like "an email with a valid sender"

    it "has the correct subject line" do
      subject = "[#{ArchiveConfig.APP_SHORT_NAME}] Your username has been changed"
      expect(email.subject).to eq(subject)
    end

    # Test both body contents
    it_behaves_like "a multipart email"

    it_behaves_like "a translated email"

    describe "HTML version" do
      it "has the correct content" do
        expect(email).to have_html_part_content("Hi,")
        expect(email).to have_html_part_content(">#{old_username}</b> has been changed to <b style=\"color:#990000\">#{new_username}</b>")

        expect(email).to have_html_part_content("can only be changed once every 5 days")
        expect(email).to have_html_part_content("You will be able to change your username again on Mon, 06 Jan 2025 00:00:00 +0000")
      end
    end

    describe "text version" do
      it "has the correct content" do
        expect(email).to have_text_part_content("Hi,")
        expect(email).to have_text_part_content("#{old_username} has been changed to #{new_username}")

        expect(email).to have_text_part_content("can only be changed once every 5 days")
        expect(email).to have_text_part_content("You will be able to change your username again on Mon, 06 Jan 2025 00:00:00 +0000")
      end
    end
  end

  describe "creatorship_request" do
    include ActiveJob::TestHelper
    def queue_adapter_for_test
      ActiveJob::QueueAdapters::TestAdapter.new
    end

    subject(:email) { UserMailer.creatorship_request(work_creatorship.id, author.id) }

    let(:author) { create(:user) }
    let(:second_author) { create(:user) }
    let(:work) { create(:work, authors: [author.default_pseud, second_author.default_pseud]) }
    let(:work_creatorship) { Creatorship.find_by(creation_id: work.id, pseud_id: second_author.default_pseud.id) }

    context "when the creation is unavailable" do
      before { work_creatorship.creation.delete }

      include_examples "it retries and fails on", ActionView::Template::Error
    end

    context "when the pseud being invited is unavailable" do
      before { work_creatorship.pseud.delete }

      include_examples "it retries and fails on", NoMethodError
    end

    # Test the headers
    it_behaves_like "an email with a valid sender"

    it "has the correct subject line" do
      subject = "[#{ArchiveConfig.APP_SHORT_NAME}] You've received a request to be a co-creator"
      expect(email).to have_subject(subject)
    end

    # Test both body contents
    it_behaves_like "a multipart email"

    it_behaves_like "a translated email"

    describe "HTML version" do
      it "has the correct content" do
        expect(email).to have_html_part_content("to be listed as a co-creator")
        expect(email).to have_html_part_content(" page.")
      end
    end

    describe "text version" do
      it "has the correct content" do
        expect(email).to have_text_part_content("to be listed as a co-creator")
        expect(email).to have_text_part_content("You can accept or reject this request on your Co-Creator Requests page:")
      end
    end
  end

  describe "#creatorship_notification" do
    include ActiveJob::TestHelper
    def queue_adapter_for_test
      ActiveJob::QueueAdapters::TestAdapter.new
    end

    subject(:email) { UserMailer.creatorship_notification(chapter_creatorship.id, author.id) }

    let(:author) { create(:user) }
    let(:second_author) { create(:user) }
    let(:chapter) { create(:chapter, authors: [author.default_pseud, second_author.default_pseud]) }
    let(:chapter_creatorship) { chapter.creatorships.last }

    context "when the creation is unavailable" do
      before { chapter_creatorship.creation.delete }

      include_examples "it retries and fails on", ActionView::Template::Error
    end

    context "when the pseud being invited is unavailable" do
      before { chapter_creatorship.pseud.delete }

      include_examples "it retries and fails on", NoMethodError
    end

    # Test the headers
    it_behaves_like "an email with a valid sender"

    it "has the correct subject line" do
      subject = "[#{ArchiveConfig.APP_SHORT_NAME}] You've been added as a co-creator"
      expect(email).to have_subject(subject)
    end

    # Test both body contents
    it_behaves_like "a multipart email"

    it_behaves_like "a translated email"

    describe "HTML version" do
      it "has the correct content" do
        expect(email).to have_html_part_content("to be listed as a creator")
        expect(email).to have_html_part_content("remove yourself as creator.")
      end
    end

    describe "text version" do
      it "has the correct content" do
        expect(email).to have_text_part_content("to be listed as a creator")
        expect(email).to have_text_part_content("remove yourself as creator:")
      end
    end
  end

  describe "creatorship_notification_archivist" do
    include ActiveJob::TestHelper
    def queue_adapter_for_test
      ActiveJob::QueueAdapters::TestAdapter.new
    end

    subject(:email) { UserMailer.creatorship_notification_archivist(work_creatorship.id, author.id) }

    let(:author) { create(:user) }
    let(:second_author) { create(:user) }
    let(:work) { create(:work, authors: [author.default_pseud, second_author.default_pseud]) }
    let(:work_creatorship) { Creatorship.find_by(creation_id: work.id, pseud_id: second_author.default_pseud.id) }

    context "when the creation is unavailable" do
      before { work_creatorship.creation.delete }

      include_examples "it retries and fails on", ActionView::Template::Error
    end

    context "when the pseud being invited is unavailable" do
      before { work_creatorship.pseud.delete }

      include_examples "it retries and fails on", NoMethodError
    end

    # Test the headers
    it_behaves_like "an email with a valid sender"

    it "has the correct subject line" do
      subject = "[#{ArchiveConfig.APP_SHORT_NAME}] An archivist has added you as a co-creator"
      expect(email).to have_subject(subject)
    end

    # Test both body contents
    it_behaves_like "a multipart email"

    it_behaves_like "a translated email"

    describe "HTML version" do
      it "has the correct content" do
        expect(email).to have_html_part_content("to be listed as a creator")
        expect(email).to have_html_part_content("remove yourself as creator.")
      end
    end

    describe "text version" do
      it "has the correct content" do
        expect(email).to have_text_part_content("to be listed as a creator")
        expect(email).to have_text_part_content("remove yourself as creator:")
      end
    end
  end

  describe "claim_notification" do
    title = "Façade"
    title2 = Faker::Book.title

    subject(:email) { UserMailer.claim_notification(author.id, [work.id, work2.id]) }

    let(:author) { create(:user) }
    let(:work) { create(:work, title: title, authors: [author.pseuds.first]) }
    let(:work2) { create(:work, title: title2, authors: [author.pseuds.first]) }

    # Shared content tests for both email types
    shared_examples_for "a claim notification" do
      it "contains the text for a claim email" do
        expect(part).to include("You're receiving this e-mail because you had works in a fanworks archive that has been imported")
        expect(part).to include("The Open Doors team")
      end
    end

    # Test the headers
    it_behaves_like "an email with a valid sender"

    it "has the correct subject line" do
      subject = "[#{ArchiveConfig.APP_SHORT_NAME}] Works uploaded"
      expect(email).to have_subject(subject)
    end

    # Test both body contents
    it_behaves_like "a multipart email"

    it_behaves_like "a translated email"

    describe "HTML version" do
      it_behaves_like "a claim notification" do
        let(:part) { email.html_part.decoded }
      end

      it "lists the first imported work" do
        expect(email).to have_html_part_content(title)
      end

      it "lists the second imported work" do
        expect(email).to have_html_part_content(title2)
      end

      it "only has style_to links in the HTML body" do
        expect(email.html_part.decoded).not_to have_xpath("//a[not(@style)]")
      end
    end

    describe "text version" do
      it_behaves_like "a claim notification" do
        let(:part) { email.text_part.decoded }
      end

      it "lists the second imported work with a leading hyphen" do
        expect(email).to have_text_part_content("- \"#{title2}\"")
      end

      it "displays titles with non-ASCII characters" do
        expect(email).to have_text_part_content(title)
      end
    end
  end

  describe "invitation_to_claim" do
    title = Faker::Book.title
    title2 = Faker::Book.title

    subject(:email) { UserMailer.invitation_to_claim(invitation.id, archivist.login) }

    let(:archivist) { create(:user) }
    let(:external_author) { create(:external_author) }

    let(:external_author_name) do
      create(:external_author_name,
             external_author_id: external_author.id,
             name: "External Author")
    end

    let(:invitation) do
      create(:invitation, external_author_id: external_author.id)
    end

    let(:work) { create(:work, title: title) }
    let(:work2) { create(:work, title: title2) }

    let!(:work_external_creatorship) do
      create(:external_creatorship,
             creation_id: work.id,
             external_author_name_id: external_author_name.id)
    end

    let!(:work2_external_creatorship) do
      create(:external_creatorship,
             creation_id: work2.id,
             external_author_name_id: external_author_name.id)
    end

    # Shared content tests for both email types
    shared_examples_for "an invitation to claim content" do
      it "contains the text for an invitation claim email" do
        expect(part).to include("You're receiving this e-mail because an archive has recently been imported by")
        expect(part).to include("The Open Doors team")
      end
    end

    # Test the headers
    it_behaves_like "an email with a valid sender"

    it "has the correct subject line" do
      subject = "[#{ArchiveConfig.APP_SHORT_NAME}] Invitation to claim works"
      expect(email).to have_subject(subject)
    end

    # Test both body contents
    it_behaves_like "a multipart email"

    it_behaves_like "a translated email"

    describe "HTML version" do
      it_behaves_like "an invitation to claim content" do
        let(:part) { email.html_part.decoded }
      end

      it "lists the first imported work in an unordered list in the HTML body" do
        expect(email.html_part.decoded).to have_xpath("//ul/li", text: title)
      end

      it "lists the second imported work in an unordered list in the HTML body" do
        expect(email.html_part.decoded).to have_xpath("//ul/li", text: title2)
      end

      it "only has style_to links in the HTML body" do
        expect(email.html_part.decoded).not_to have_xpath("//a[not(@style)]")
      end
    end

    describe "text version" do
      it_behaves_like "an invitation to claim content" do
        let(:part) { email.text_part.decoded }
      end

      it "lists the first imported work as plain text" do
        expect(email.text_part.decoded).not_to have_xpath("//ul/li", text: title)
      end

      it "lists the second imported work with a leading hyphen" do
        expect(email).to have_text_part_content("- \"#{title2}\"")
      end
    end
  end

  describe "invitation" do
    context "when sent by a user" do
      subject(:email) { UserMailer.invitation(invitation.id) }

      let(:user) { create(:user) }
      let(:invitation) { create(:invitation, creator: user) }

      # Test the headers
      it_behaves_like "an email with a valid sender"

      it "has the correct subject line" do
        subject = "[#{ArchiveConfig.APP_SHORT_NAME}] Invitation"
        expect(email).to have_subject(subject)
      end

      # Test both body contents
      it_behaves_like "a multipart email"

      it_behaves_like "a translated email"

      describe "HTML version" do
        it "has the correct content" do
          expect(email).to have_html_part_content("follow this link to sign up</a>")
          expect(email).to have_html_part_content("has invited you")
          expect(email).to have_html_part_content("Organization for Transformative Works</a>, which works to protect fan rights and preserve fanworks.")
        end
      end

      describe "text version" do
        it "has the correct content" do
          expect(email).to have_text_part_content("like to join us, please follow this link to sign up")
          expect(email).to have_text_part_content("has invited you")
          expect(email).to have_text_part_content("the Organization for Transformative Works (https://www.transformativeworks.org), which works to protect fan rights and preserve fanworks")
        end
      end
    end

    context "when sent from the queue or by an admin" do
      subject(:email) { UserMailer.invitation(invitation.id) }

      let(:invitation) { create(:invitation) }

      # Test the headers
      it_behaves_like "an email with a valid sender"

      it "has the correct subject line" do
        subject = "[#{ArchiveConfig.APP_SHORT_NAME}] Invitation"
        expect(email).to have_subject(subject)
      end

      # Test both body contents
      it_behaves_like "a multipart email"

      it_behaves_like "a translated email"

      describe "HTML version" do
        it "has the correct content" do
          expect(email).to have_html_part_content("follow this link to sign up")
          expect(email).to have_html_part_content("been invited")
          expect(email).to have_html_part_content("Organization for Transformative Works</a>, which works to protect fan rights and preserve fanworks.")
        end
      end

      describe "text version" do
        it "has the correct content" do
          expect(email).to have_text_part_content("like to join us, please follow this link to sign up")
          expect(email).to have_text_part_content("been invited")
          expect(email).to have_text_part_content("the Organization for Transformative Works (https://www.transformativeworks.org), which works to protect fan rights and preserve fanworks")
        end
      end
    end
  end

  describe "challenge_assignment_notification" do
    subject(:email) { UserMailer.challenge_assignment_notification(collection.id, otheruser.id, open_assignment.id) }

    let!(:gift_exchange) { create(:gift_exchange) }
    let!(:collection) { create(:collection, challenge: gift_exchange, challenge_type: "GiftExchange") }
    let!(:otheruser) { create(:user) }
    let!(:offer) { create(:challenge_signup, collection: collection, pseud: otheruser.default_pseud) }
    let!(:open_assignment) { create(:challenge_assignment, collection: collection, offer_signup: offer, sent_at: Time.current) }

    # Test the headers
    it_behaves_like "an email with a valid sender"

    it "has the correct subject line" do
      subject = "[#{ArchiveConfig.APP_SHORT_NAME}][#{collection.title}] Your assignment!"
      expect(email).to have_subject(subject)
    end

    # Test both body contents
    it_behaves_like "a multipart email"

    it_behaves_like "a translated email"

    describe "HTML version" do
      it "has the correct content" do
        expect(email).to have_html_part_content("You have been assigned the following request")
      end
    end

    describe "text version" do
      it "has the correct content" do
        expect(email).to have_text_part_content("You have been assigned the following request")
      end
    end
  end

  describe "invite_request_declined" do
    subject(:email) { UserMailer.invite_request_declined(user.id, total, reason) }

    let(:user) { create(:user) }
    let(:total) { 2 }
    let(:reason) { "You smell" }

    # Test the headers
    it_behaves_like "an email with a valid sender"

    it "has the correct subject line" do
      subject = "[#{ArchiveConfig.APP_SHORT_NAME}] Additional invitation request declined"
      expect(email).to have_subject(subject)
    end

    # Test both body contents
    it_behaves_like "a multipart email"

    it_behaves_like "a translated email"

    describe "HTML version" do
      it "has the correct content" do
        expect(email).to have_html_part_content("We regret to inform you that your request for 2 new invitations cannot be fulfilled at this time")
      end
    end

    describe "text version" do
      it "has the correct content" do
        expect(email).to have_text_part_content("We regret to inform you that your request for 2 new invitations cannot be fulfilled at this time")
      end
    end
  end

  describe "signup_notification" do
    subject(:email) { UserMailer.signup_notification(user.id) }

    let(:user) { create(:user, :unconfirmed) }

    # Test the headers
    it_behaves_like "an email with a valid sender"

    it "has the correct subject line" do
      subject = "[#{ArchiveConfig.APP_SHORT_NAME}] Activate your account"
      expect(email).to have_subject(subject)
    end

    # Test both body contents
    it_behaves_like "a multipart email"

    it_behaves_like "a translated email"

    describe "HTML version" do
      it "has the correct content" do
        expect(email).to have_html_part_content("Once your account is up and running, you can post your fanworks, set up email")
        expect(email).to have_html_part_content("follow this link to activate your account</a>.")
      end
    end

    describe "text version" do
      it "has the correct content" do
        expect(email).to have_text_part_content("Once your account is up and running, you can post your fanworks, set up email")
        expect(email).to have_text_part_content("follow this link to activate your account:")
      end
    end
  end

  describe "invite_increase_notification" do
    let!(:user) { create(:user) }

    context "when 1 invitation is issued" do
      subject(:email) { UserMailer.invite_increase_notification(user.id, count) }

      let(:count) { 1 }

      # Test the headers
      it_behaves_like "an email with a valid sender"

      it "has the correct subject line" do
        expect(email.subject).to eq("[#{ArchiveConfig.APP_SHORT_NAME}] New invitations")
      end

      # Test both body contents
      it_behaves_like "a multipart email"

      it_behaves_like "a translated email"

      describe "HTML version" do
        it "has the correct content" do
          expect(email).to have_html_part_content("you have #{count} new invitation, which")
          expect(email).to have_html_part_content("your invitations page</a>.")
        end
      end

      describe "text version" do
        it "has the correct content" do
          expect(email).to have_text_part_content("you have #{count} new invitation, which")
          expect(email).to have_text_part_content("your invitations page (")
        end
      end
    end

    context "when multiple invitations are issued" do
      subject(:email) { UserMailer.invite_increase_notification(user.id, count) }

      let(:count) { 5 }

      # Test the headers
      it_behaves_like "an email with a valid sender"

      it "has the correct subject line" do
        expect(email.subject).to eq("[#{ArchiveConfig.APP_SHORT_NAME}] New invitations")
      end

      # Test both body contents
      it_behaves_like "a multipart email"

      it_behaves_like "a translated email"

      describe "HTML version" do
        it "has the correct content" do
          expect(email).to have_html_part_content("you have #{count} new invitations, which")
          expect(email).to have_html_part_content("your invitations page</a>.")
        end
      end

      describe "text version" do
        it "has the correct content" do
          expect(email).to have_text_part_content("you have #{count} new invitations, which")
          expect(email).to have_text_part_content("your invitations page (")
        end
      end
    end
  end

  describe "batch_subscription_notification" do
    include ActiveJob::TestHelper
    def queue_adapter_for_test
      ActiveJob::QueueAdapters::TestAdapter.new
    end

    subject(:email) { UserMailer.batch_subscription_notification(subscription.id, ["Work_#{work.id}", "Chapter_#{chapter.id}"].to_json) }

    let(:work) { create(:work, summary: "<p>Paragraph <u>one</u>.</p><p>Paragraph 2.</p>") }
    let(:chapter) { create(:chapter, work: work, summary: "<p><b>Another</b> HTML summary.</p>") }
    let(:subscription) { create(:subscription, subscribable: work) }

    context "when the user is unavailable" do
      before { subscription.user.delete }

      include_examples "it retries and fails on", NoMethodError
    end

    context "when the user's preferences are unavailable" do
      before { subscription.user.preference.delete }

      include_examples "it retries and fails on", NoMethodError
    end

    # Test the headers
    it_behaves_like "an email with a valid sender"

    it "has the correct subject line" do
      subject = "[#{ArchiveConfig.APP_SHORT_NAME}] #{subscription.subject_text(work)} and 1 more"
      expect(email).to have_subject(subject)
    end

    # Test both body contents
    it_behaves_like "a multipart email"

    describe "HTML version" do
      it "has the correct content" do
        expect(email).to have_html_part_content("new work")
        expect(email).to have_html_part_content("new chapter of")
      end

      it "includes HTML from the work summary" do
        expect(email).to have_html_part_content("<p>Paragraph <u>one</u>.</p>")
        expect(email).to have_html_part_content("<p>Paragraph 2.</p>")
      end

      it "includes HTML from the chapter summary" do
        expect(email).to have_html_part_content("<p><b>Another</b> HTML summary.</p>")
      end
    end

    describe "text version" do
      it "has the correct content" do
        expect(email).to have_text_part_content("new work")
        expect(email).to have_text_part_content("new chapter of #{work.title}")
      end

      it "reformats HTML from the work summary" do
        expect(email).to have_text_part_content("Paragraph _one_.")
        expect(email).to have_text_part_content("Paragraph 2.")
      end

      it "reformats HTML from the chapter summary" do
        expect(email).to have_text_part_content("*Another* HTML summary.")
      end
    end
  end

  describe "admin_hidden_work_notification" do
    subject(:email) { UserMailer.admin_hidden_work_notification([work.id], user.id) }

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

    # Test the headers
    it_behaves_like "an email with a valid sender"

    it "has the correct subject line" do
      subject = "[#{ArchiveConfig.APP_SHORT_NAME}] Your work has been hidden by the Policy & Abuse committee"
      expect(email.subject).to eq(subject)
    end

    # Test both body contents
    it_behaves_like "a multipart email"

    it_behaves_like "a translated email"

    describe "HTML version" do
      it "has the correct content" do
        expect(email).to have_html_part_content("Hi <b")
        expect(email).to have_html_part_content("#{user.login}</b>,")
        expect(email).to have_html_part_content("> has been hidden")
      end
    end

    describe "text version" do
      it "has the correct content" do
        expect(email).to have_text_part_content("Hi #{user.login},")
        expect(email).to have_text_part_content(") has been hidden")
      end
    end
  end

  describe "abuse_report" do
    let(:report) { create(:abuse_report) }
    let(:email) { UserMailer.abuse_report(report.id) }

    it "has the correct subject" do
      expect(email).to have_subject "[#{ArchiveConfig.APP_SHORT_NAME}] Abuse - #{report.summary}"
    end

    it "delivers to the user who filed the report" do
      expect(email).to deliver_to(report.email)
    end

    it_behaves_like "an email with a valid sender"

    it_behaves_like "a multipart email"

    it_behaves_like "a translated email"

    describe "HTML version" do
      it "contains the comment and the URL reported" do
        expect(email).to have_html_part_content(report.comment)
        expect(email).to have_html_part_content(report.url)
      end
    end

    describe "text version" do
      it "contains the comment and the URL reported" do
        expect(email).to have_text_part_content(report.comment)
        expect(email).to have_text_part_content(report.url)
      end
    end

    describe "translation" do
      it "formats the date rightfully in English" do
        travel_to "2022-03-14 13:27:09 +0000" do
          expect(email).to have_html_part_content("Sent at Mon, 14 Mar 2022 13:27:09 +0000.")
          expect(email).to have_text_part_content("Sent at Mon, 14 Mar 2022 13:27:09 +0000.")
        end
      end

      it "formats the date rightfully in French" do
        I18n.with_locale("fr") do
          travel_to "2022-03-14 13:27:09 +0000" do
            expect(email).to have_html_part_content("Envoyé le 14 mars 2022 13h 27min 09s.")
            expect(email).to have_text_part_content("Envoyé le 14 mars 2022 13h 27min 09s.")
          end
        end 
      end
    end
  end

  describe "feedback" do
    context "when username is present" do
      subject(:email) { UserMailer.feedback(feedback.id) }

      let(:feedback) { create(:feedback) }

      # Test the headers
      it_behaves_like "an email with a valid sender"

      it "has the correct subject line" do
        subject = "[#{ArchiveConfig.APP_SHORT_NAME}] Support - #{feedback.summary}"
        expect(email).to have_subject(subject)
      end

      # Test both body contents
      it_behaves_like "a multipart email"
      it_behaves_like "a translated email"

      describe "HTML version" do
        it "has the correct content" do
          expect(email).to have_html_part_content("Hi!")
          expect(email).to have_html_part_content(">The AO3 Support team</b>")
        end
      end

      describe "text version" do
        it "has the correct content" do
          expect(email).to have_text_part_content("Hi!")
          expect(email).to have_text_part_content("The AO3 Support team")
        end
      end
    end

    context "when username is not present" do
      subject(:email) { UserMailer.feedback(feedback.id) }

      let(:feedback) { create(:feedback, username: "A. Name") }

      # Test the headers
      it_behaves_like "an email with a valid sender"

      it "has the correct subject line" do
        subject = "[#{ArchiveConfig.APP_SHORT_NAME}] Support - #{feedback.summary}"
        expect(email).to have_subject(subject)
      end

      # Test both body contents
      it_behaves_like "a multipart email"
      it_behaves_like "a translated email"

      describe "HTML version" do
        it "has the correct content" do
          expect(email).to have_html_part_content("Hi, <b")
          expect(email).to have_html_part_content("#{feedback.username}</b>")
          expect(email).to have_html_part_content(">The AO3 Support team</b>")
        end
      end

      describe "text version" do
        it "has the correct content" do
          expect(email).to have_text_part_content("Hi, #{feedback.username}!")
          expect(email).to have_text_part_content("The AO3 Support team")
        end
      end
    end
  end

  describe "admin_spam_work_notification" do
    subject(:email) { UserMailer.admin_spam_work_notification(work.id, user.id) }

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

    # Test the headers
    it_behaves_like "an email with a valid sender"

    it "has the correct subject line" do
      subject = "[#{ArchiveConfig.APP_SHORT_NAME}] Your work was hidden as spam"
      expect(email.subject).to eq(subject)
    end

    # Test both body contents
    it_behaves_like "a multipart email"

    it_behaves_like "a translated email"

    describe "HTML version" do
      it "has the correct content" do
        expect(email).to have_html_part_content("Hi <b")
        expect(email).to have_html_part_content("#{user.login}</b>,")
        expect(email).to have_html_part_content("> has been flagged by our automated system")
      end
    end

    describe "text version" do
      it "has the correct content" do
        expect(email).to have_text_part_content("Hi #{user.login},")
        expect(email).to have_text_part_content(") has been flagged by our automated system")
      end
    end
  end

  describe "invited_to_collection_notification" do
    subject(:email) { UserMailer.invited_to_collection_notification(user.id, work.id, collection.id) }

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

    # Test the headers
    it_behaves_like "an email with a valid sender"

    it "has the correct subject line" do
      subject = "[#{ArchiveConfig.APP_SHORT_NAME}][#{collection.title}] Request to include work in a collection"
      expect(email.subject).to eq(subject)
    end

    # Test both body contents
    it_behaves_like "a multipart email"

    it_behaves_like "a translated email"

    describe "HTML version" do
      it "has the correct content" do
        expect(email).to have_html_part_content("Hi <b")
        expect(email).to have_html_part_content("#{user.login}</b>,")
      end
    end

    describe "text version" do
      it "has the correct content" do
        expect(email).to have_text_part_content("Hi #{user.login},")
      end
    end
  end

  describe "#archivist_added_to_collection_notification" do
    subject(:email) do
      UserMailer.archivist_added_to_collection_notification(user.id, work.id, collection.id)
    end

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

    # Test the headers
    it_behaves_like "an email with a valid sender"

    it "has the correct subject line" do
      subject = "[#{ArchiveConfig.APP_SHORT_NAME}][#{collection.title}] An Open Doors archivist has added your work to a collection"
      expect(email.subject).to eq(subject)
    end

    # Test both body contents
    it_behaves_like "a multipart email"

    it_behaves_like "a translated email"

    describe "HTML version" do
      it "has the correct content" do
        expect(email).to have_html_part_content("Hi <b")
        expect(email).to have_html_part_content("#{user.login}</b>,")
        expect(email).to have_html_part_content(collection.title)
        expect(email).to have_html_part_content(work.title)
      end
    end

    describe "text version" do
      it "has the correct content" do
        expect(email).to have_text_part_content("Hi #{user.login},")
        expect(email).to have_text_part_content(collection.title)
        expect(email).to have_text_part_content(work.title)
      end
    end
  end

  describe "recipient_notification" do
    context "when collection is present" do
      subject(:email) { UserMailer.recipient_notification(user.id, work.id, collection.id) }

      let(:user) { create(:user) }
      let(:work) { create(:work, fandom_string: "Fandom 1, Fandom 2", character_string: "A, B") }
      let(:collection) { create(:collection) }

      # Test the headers
      it_behaves_like "an email with a valid sender"

      it "has the correct subject line" do
        subject = "[#{ArchiveConfig.APP_SHORT_NAME}][#{collection.title}] A gift work for you from #{collection.title}"
        expect(email).to have_subject(subject)
      end

      # Test both body contents
      it_behaves_like "a multipart email"

      it_behaves_like "a translated email"

      describe "HTML version" do
        it "has the correct content" do
          expect(email).to have_html_part_content("Hi, <b")
          expect(email).to have_html_part_content("#{user.login}</b>")
        end
      end

      describe "text version" do
        it "has the correct content" do
          expect(email).to have_text_part_content("Hi, #{user.login}!")
        end
      end
    end

    context "when no collection is present" do
      subject(:email) { UserMailer.recipient_notification(user.id, work.id) }

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

      # Test the headers
      it_behaves_like "an email with a valid sender"

      it "has the correct subject line" do
        subject = "[#{ArchiveConfig.APP_SHORT_NAME}] A gift work for you"
        expect(email).to have_subject(subject)
      end

      # Test both body contents
      it_behaves_like "a multipart email"

      it_behaves_like "a translated email"

      describe "HTML version" do
        it "has the correct content" do
          expect(email).to have_html_part_content("Hi, <b")
          expect(email).to have_html_part_content("#{user.login}</b>")
        end
      end

      describe "text version" do
        it "has the correct content" do
          expect(email).to have_text_part_content("Hi, #{user.login}")
        end
      end
    end
  end

  describe "potential_match_generation_notification" do
    subject(:email) { UserMailer.potential_match_generation_notification(collection.id, "test@example.com") }

    let(:collection) { create(:collection) }

    # Test the headers
    it_behaves_like "an email with a valid sender"

    it "has the correct subject line" do
      subject = "[#{ArchiveConfig.APP_SHORT_NAME}][#{collection.title}] Potential assignment generation complete"
      expect(email.subject).to eq(subject)
    end

    # Test both body contents
    it_behaves_like "a multipart email"

    it_behaves_like "a translated email"

    describe "HTML version" do
      it "has the correct content" do
        expect(email).to have_html_part_content("potential assignments for your gift exchange <")
        expect(email).to have_html_part_content("on its <")
      end
    end

    describe "text version" do
      it "has the correct content" do
        expect(email).to have_text_part_content("potential assignments for your gift exchange \"#{collection.title}\"")
        expect(email).to have_text_part_content("on its Matching page:")
      end
    end
  end

  describe "potential_match_generation_notification_collection_email" do
    subject(:email) { UserMailer.potential_match_generation_notification(collection.id, collection.collection_email) }

    let(:collection) { create(:collection) }

    it_behaves_like "an email with a valid sender"

    describe "HTML version" do
      it "has the correct footer content" do
        expect(email).to have_html_part_content("your email address has been listed as the collection email")
      end
    end

    describe "text version" do
      it "has the correct footer content" do
        expect(email).to have_text_part_content("your email address has been listed as the collection email")
      end
    end
  end

  describe "invalid_signup_notification" do
    subject(:email) { UserMailer.invalid_signup_notification(collection.id, [signup.id], "test@example.com") }

    let(:collection) { create(:collection) }
    let(:signup) { create(:challenge_signup) }

    # Test the headers
    it_behaves_like "an email with a valid sender"

    it "has the correct subject line" do
      subject = "[#{ArchiveConfig.APP_SHORT_NAME}][#{collection.title}] Invalid sign-ups found"
      expect(email.subject).to eq(subject)
    end

    # Test both body contents
    it_behaves_like "a multipart email"

    it_behaves_like "a translated email"

    describe "HTML version" do
      it "has the correct content" do
        expect(email).to have_html_part_content("invalid sign-ups in your gift exchange <")
      end
    end

    describe "text version" do
      it "has the correct content" do
        expect(email).to have_text_part_content("invalid sign-ups in your gift exchange \"#{collection.title}\"")
      end
    end
  end

  describe "invalid_signup_notification_collection_email" do
    subject(:email) { UserMailer.invalid_signup_notification(collection.id, [signup.id], collection.collection_email) }

    let(:collection) { create(:collection) }
    let(:signup) { create(:challenge_signup) }

    it_behaves_like "an email with a valid sender"

    describe "HTML version" do
      it "has the correct footer content" do
        expect(email).to have_html_part_content("your email address has been listed as the collection email")
      end
    end

    describe "text version" do
      it "has the correct footer content" do
        expect(email).to have_text_part_content("your email address has been listed as the collection email")
      end
    end
  end

  describe "collection_notification" do
    subject(:email) { UserMailer.collection_notification(collection.id, subject_text, message_text, "test@example.com") }

    let(:collection) { create(:collection) }
    let(:subject_text) { Faker::Hipster.sentence }
    let(:message_text) { Faker::Hipster.paragraph }

    # Test the headers
    it_behaves_like "an email with a valid sender"

    it "has the correct subject line" do
      subject = "[#{ArchiveConfig.APP_SHORT_NAME}][#{collection.title}] #{subject_text}"
      expect(email.subject).to eq(subject)
    end

    # Test both body contents
    it_behaves_like "a multipart email"

    it_behaves_like "a translated email"

    describe "HTML version" do
      it "has the correct content" do
        expect(email).to have_html_part_content("your collection <")
        expect(email).to have_html_part_content("#{message_text}</blockquote>")
      end
    end

    describe "text version" do
      it "has the correct content" do
        expect(email).to have_text_part_content("your collection \"#{collection.title}\"")
        expect(email).to have_text_part_content(message_text)
      end
    end
  end

  describe "prompter_notification" do
    context "when collection is present" do
      subject(:email) { UserMailer.prompter_notification(work.id, collection.id) }

      let(:collection) { create(:collection) }
      let(:claim) { create(:challenge_claim, request_signup: create(:prompt_meme_signup)) }
      let(:work) { create(:work, challenge_claims: [claim]) }

      # Test the headers
      it_behaves_like "an email with a valid sender"

      it "has the correct subject line" do
        subject = "[#{ArchiveConfig.APP_SHORT_NAME}] A response to your prompt"
        expect(email).to have_subject(subject)
      end

      # Test both body contents
      it_behaves_like "a multipart email"

      describe "HTML version" do
        it "has the correct content" do
          expect(email).to have_html_part_content("A response to your prompt")
          expect(email).to have_html_part_content("#{collection.title}</a>")
        end
      end

      describe "text version" do
        it "has the correct content" do
          expect(email).to have_text_part_content("A response to your prompt")
          expect(email).to have_text_part_content("\"#{collection.title}\" collection (#{collection_url(collection)})")
        end
      end
    end

    context "when no collection is present" do
      subject(:email) { UserMailer.prompter_notification(work.id) }

      let(:claim) { create(:challenge_claim, request_signup: create(:prompt_meme_signup)) }
      let(:work) { create(:work, challenge_claims: [claim]) }

      # Test the headers
      it_behaves_like "an email with a valid sender"

      it "has the correct subject line" do
        subject = "[#{ArchiveConfig.APP_SHORT_NAME}] A response to your prompt"
        expect(email).to have_subject(subject)
      end

      # Test both body contents
      it_behaves_like "a multipart email"

      describe "HTML version" do
        it "has the correct content" do
          expect(email).to have_html_part_content("posted at the Archive")
          expect(email).not_to have_html_part_content("collection")
        end
      end

      describe "text version" do
        it "has the correct content" do
          expect(email).to have_text_part_content("posted at the Archive")
          expect(email).not_to have_text_part_content("collection")
        end
      end
    end
  end

  describe "delete_work_notification" do
    subject(:email) { UserMailer.delete_work_notification(user, work, user) }

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

    it_behaves_like "an email with a valid sender"
    it_behaves_like "a translated email"

    it "has the correct subject line" do
      subject = "[#{ArchiveConfig.APP_SHORT_NAME}] Your work has been deleted"
      expect(email).to have_subject(subject)
    end

    it "has the correct attachments" do
      filename = work.title.gsub(/\s/, "_")
      expect(email.attachments.length).to eq(2)
      expect(email.attachments).to contain_exactly(
        an_object_having_attributes(filename: "#{filename}.html"),
        an_object_having_attributes(filename: "#{filename}.txt")
      )
    end

    context "HTML version" do
      it "has the correct content" do
        expect(email).to have_html_part_content("Hi <b")
        expect(email).to have_html_part_content("#{user.login}</b>,")
        expect(email).to have_html_part_content("was deleted at your request")
      end
    end

    context "text version" do
      it "has the correct content" do
        expect(email).to have_text_part_content("Hi #{user.login},")
        expect(email).to have_text_part_content("Your work \"#{work.title}\" was deleted at your request")
      end
    end

    context "when work has posted and draft chapters" do
      let!(:draft_chapter) { create(:chapter, :draft, work: work, position: 2) }

      it_behaves_like "an email with a deleted work with draft chapters attached"
    end

    context "when work has only draft chapters" do
      before do
        work.chapters.first.update_column(:posted, false)
      end

      it_behaves_like "an email with a deleted work with draft chapters attached"
    end
  end

  describe "admin_deleted_work_notification" do
    subject(:email) { UserMailer.admin_deleted_work_notification(user, work) }

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

    it_behaves_like "an email with a valid sender"
    it_behaves_like "a translated email"

    it "has the correct subject line" do
      subject = "[#{ArchiveConfig.APP_SHORT_NAME}] Your work has been deleted by an admin"
      expect(email).to have_subject(subject)
    end

    it "has the correct attachments" do
      filename = work.title.gsub(/\s/, "_")
      expect(email.attachments.length).to eq(2)
      expect(email.attachments).to contain_exactly(
        an_object_having_attributes(filename: "#{filename}.html"),
        an_object_having_attributes(filename: "#{filename}.txt")
      )
    end

    context "HTML version" do
      it "has the correct content" do
        expect(email).to have_html_part_content("Hi <b")
        expect(email).to have_html_part_content("#{user.login}</b>,")
        expect(email).to have_html_part_content("was deleted from the Archive by a site admin")
      end
    end

    context "text version" do
      it "has the correct content" do
        expect(email).to have_text_part_content("Hi #{user.login},")
        expect(email).to have_text_part_content("Your work \"#{work.title}\" was deleted from the Archive by a site admin")
      end
    end

    context "when work has posted and draft chapters" do
      let!(:draft_chapter) { create(:chapter, :draft, work: work, position: 2) }

      it_behaves_like "an email with a deleted work with draft chapters attached"
    end

    context "when work has only draft chapters" do
      before do
        work.chapters.first.update_column(:posted, false)
      end

      it_behaves_like "an email with a deleted work with draft chapters attached"
    end
  end
end
