# frozen_string_literal: true

RSpec.describe EmailController do
  fab!(:user)

  describe "#perform_unsubscribe" do
    it "raises not found on invalid key" do
      post "/email/unsubscribe/123.json"
      expect(response.status).to eq(404)
    end

    describe "unsubscribe from all emails" do
      let(:key) { UnsubscribeKey.create_key_for(user, UnsubscribeKey::ALL_TYPE) }

      it "can fully unsubscribe" do
        user.user_option.update_columns(
          email_digests: true,
          email_level: UserOption.email_level_types[:never],
          email_messages_level: UserOption.email_level_types[:never],
          mailing_list_mode: true,
        )

        post "/email/unsubscribe/#{key}.json", params: { unsubscribe_all: "1" }

        expect(response.status).to eq(302)

        get response.redirect_url

        # cause it worked ... yay
        expect(body).to include(user.email)

        user.user_option.reload

        expect(user.user_option.email_digests).to eq(false)
        expect(user.user_option.email_level).to eq(UserOption.email_level_types[:never])
        expect(user.user_option.email_messages_level).to eq(UserOption.email_level_types[:never])
        expect(user.user_option.mailing_list_mode).to eq(false)
      end

      it "can disable mailing list" do
        user.user_option.update_columns(mailing_list_mode: true)

        post "/email/unsubscribe/#{key}.json", params: { disable_mailing_list: "1" }

        expect(response.status).to eq(302)
        expect(user.user_option.reload.mailing_list_mode).to eq(false)
      end
    end

    describe "unsubscribe from digest" do
      let(:key) { UnsubscribeKey.create_key_for(user, UnsubscribeKey::DIGEST_TYPE) }

      it "Can change digest frequency" do
        weekly_interval_minutes = 10_080
        user.user_option.update_columns(email_digests: true, digest_after_minutes: 0)

        post "/email/unsubscribe/#{key}.json",
             params: {
               digest_after_minutes: weekly_interval_minutes.to_s,
             }

        expect(response.status).to eq(302)
        expect(user.user_option.reload.digest_after_minutes).to eq(weekly_interval_minutes)
      end

      it "Can disable email digests setting frequency to zero" do
        user.user_option.update_columns(email_digests: true, digest_after_minutes: 10_080)

        post "/email/unsubscribe/#{key}.json", params: { digest_after_minutes: "0" }

        expect(response.status).to eq(302)
        user.user_option.reload
        expect(user.user_option.digest_after_minutes).to be_zero
        expect(user.user_option.email_digests).to eq(false)
      end
    end

    describe "unsubscribe from a topic" do
      fab!(:a_post) { Fabricate(:post) }
      let(:key) { UnsubscribeKey.create_key_for(user, UnsubscribeKey::TOPIC_TYPE, post: a_post) }

      it "can unwatch topic" do
        TopicUser.change(
          user.id,
          a_post.topic_id,
          notification_level: TopicUser.notification_levels[:watching],
        )

        post "/email/unsubscribe/#{key}.json", params: { unwatch_topic: "1" }

        expect(response.status).to eq(302)
        expect(TopicUser.get(a_post.topic, user).notification_level).to eq(
          TopicUser.notification_levels[:tracking],
        )
      end

      it "can mute topic" do
        TopicUser.change(
          user.id,
          a_post.topic_id,
          notification_level: TopicUser.notification_levels[:watching],
        )

        post "/email/unsubscribe/#{key}.json", params: { mute_topic: "1" }

        expect(response.status).to eq(302)
        expect(TopicUser.get(a_post.topic, user).notification_level).to eq(
          TopicUser.notification_levels[:muted],
        )
      end

      it "can unwatch category" do
        cu =
          CategoryUser.create!(
            user_id: user.id,
            category_id: a_post.topic.category_id,
            notification_level: CategoryUser.notification_levels[:watching],
          )

        post "/email/unsubscribe/#{key}.json", params: { unwatch_category: "1" }

        expect(response.status).to eq(302)
        expect(CategoryUser.find_by(id: cu.id)).to eq(nil)
      end

      it "can unwatch first post from category" do
        cu =
          CategoryUser.create!(
            user_id: user.id,
            category_id: a_post.topic.category_id,
            notification_level: CategoryUser.notification_levels[:watching_first_post],
          )

        post "/email/unsubscribe/#{key}.json", params: { unwatch_category: "1" }

        expect(response.status).to eq(302)
        expect(CategoryUser.find_by(id: cu.id)).to eq(nil)
      end
    end
  end

  describe "#unsubscribed" do
    describe "when email is invalid" do
      it "should return the right response" do
        get "/email/unsubscribed", params: { email: "somerandomstring" }
        expect(response.status).to eq(404)
      end
    end

    describe "when topic is public" do
      fab!(:topic)

      it "should return the right response" do
        key = SecureRandom.hex
        Discourse.cache.write(key, user.email)
        get "/email/unsubscribed", params: { key: key, topic_id: topic.id }
        expect(response.status).to eq(200)
        expect(response.body).to include(topic.title)
      end
    end

    describe "when topic is private" do
      fab!(:private_topic) { Fabricate(:private_message_topic) }

      it "should return the right response" do
        key = SecureRandom.hex
        Discourse.cache.write(key, user.email)
        get "/email/unsubscribed", params: { key: key, topic_id: private_topic.id }
        expect(response.status).to eq(200)
        expect(response.body).to_not include(private_topic.title)
      end
    end
  end

  describe "#unsubscribe" do
    it "displays not found if key is not found" do
      navigate_to_unsubscribe(SecureRandom.hex)

      expect(response.body).to include(CGI.escapeHTML(I18n.t("unsubscribe.not_found_description")))
    end

    fab!(:user)

    it "displays an error when the key has no associated user" do
      key_without_owner = UnsubscribeKey.create_key_for(user, UnsubscribeKey::DIGEST_TYPE)
      user.destroy!

      navigate_to_unsubscribe(key_without_owner)

      expect(response.body).to include(
        CGI.escapeHTML(I18n.t("unsubscribe.user_not_found_description")),
      )
    end

    let(:unsubscribe_key) { UnsubscribeKey.create_key_for(user, key_type, post: post) }

    context "when unsubscribing from digest" do
      let(:key_type) { UnsubscribeKey::DIGEST_TYPE }
      let(:post) { nil }

      it "displays log out button if wrong user logged in" do
        sign_in(Fabricate(:admin))

        navigate_to_unsubscribe

        expect(response.body).to include(I18n.t("unsubscribe.log_out"))
        expect(response.body).to include(I18n.t("unsubscribe.different_user_description"))
      end

      it "displays correct label when email_digests is set to false" do
        user.user_option.update!(email_digests: false, digest_after_minutes: 10_080)

        navigate_to_unsubscribe

        expect(body).to include("You are not receiving summary emails")
        expect(body).to include("Don’t send me any mail from Discourse")
      end

      it "hides unsubscribe from all checkbox when user already unsubscribed" do
        user.user_option.update!(
          email_digests: false,
          mailing_list_mode: false,
          email_level: 2,
          email_messages_level: 2,
        )

        navigate_to_unsubscribe

        expect(body).to include("You are not receiving summary emails")
        expect(body).not_to include("Don't send me any mail from Discourse")
      end

      it "correctly handles mailing list mode" do
        SiteSetting.disable_mailing_list_mode = false
        user.user_option.update_columns(mailing_list_mode: true)

        navigate_to_unsubscribe
        expect(response.body).to include(I18n.t("unsubscribe.mailing_list_mode"))

        SiteSetting.disable_mailing_list_mode = true

        navigate_to_unsubscribe
        expect(response.body).not_to include(I18n.t("unsubscribe.mailing_list_mode"))

        user.user_option.update_columns(mailing_list_mode: false)
        SiteSetting.disable_mailing_list_mode = false

        navigate_to_unsubscribe
        expect(response.body).not_to include(I18n.t("unsubscribe.mailing_list_mode"))
      end

      it "Lets you select the digest frequency ranging from never to half a year" do
        selected_digest_frequency = 0
        slow_digest_frequencies = ["weekly", "every month", "every six months", "never"]

        navigate_to_unsubscribe

        source = Nokogiri::HTML5.fragment(response.body)
        expect(source.css(".combobox option").map(&:inner_text)).to eq(slow_digest_frequencies)
      end

      it "Selects the next slowest frequency by default" do
        every_month_freq = 43_200
        six_months_freq = 259_200
        user.user_option.update_columns(digest_after_minutes: every_month_freq)

        navigate_to_unsubscribe

        source = Nokogiri::HTML5.fragment(response.body)
        expect(source.css(".combobox option[selected='selected']")[0]["value"]).to eq(
          six_months_freq.to_s,
        )
      end

      it "Uses never as the selected frequency if current one is six months" do
        never_frequency = 0
        six_months_freq = 259_200
        user.user_option.update_columns(digest_after_minutes: six_months_freq)

        navigate_to_unsubscribe

        source = Nokogiri::HTML5.fragment(response.body)
        expect(source.css(".combobox option[selected='selected']")[0]["value"]).to eq(
          never_frequency.to_s,
        )
      end
    end

    context "when unsubscribing from a post" do
      fab!(:post)
      let(:user) { post.user }
      let(:key_type) { UnsubscribeKey::TOPIC_TYPE }

      it "correctly handles watched categories" do
        cu = create_category_user(:watching)

        navigate_to_unsubscribe
        expect(response.body).to include("unwatch_category")
        doc = Nokogiri::HTML5.fragment(response.body)
        expect(doc.css('a.badge-category__wrapper[href="/c/uncategorized/1"]').size).to eq(1)

        cu.destroy!

        navigate_to_unsubscribe
        expect(response.body).not_to include("unwatch_category")
      end

      it "correctly handles watched first post categories" do
        cu = create_category_user(:watching_first_post)

        navigate_to_unsubscribe
        expect(response.body).to include("unwatch_category")

        cu.destroy!

        navigate_to_unsubscribe
        expect(response.body).not_to include("unwatch_category")
      end

      it "displays form even if topic is deleted" do
        post.topic.trash!

        navigate_to_unsubscribe

        expect(response.status).to eq(200)
        expect(response.body).to include(I18n.t("unsubscribe.all", sitename: SiteSetting.title))
      end

      def create_category_user(notification_level)
        CategoryUser.create!(
          user_id: user.id,
          category_id: post.topic.category_id,
          notification_level: CategoryUser.notification_levels[notification_level],
        )
      end
    end

    def navigate_to_unsubscribe(key = unsubscribe_key)
      get "/email/unsubscribe/#{key}"
    end
  end
end