# frozen_string_literal: true

require_relative "../../../support/sentiment_inference_stubs"

RSpec.describe DiscourseAi::Sentiment::EntryPoint do
  fab!(:user) { Fabricate(:user, refresh_auto_groups: true) }

  describe "registering event callbacks" do
    context "when creating a post" do
      let(:creator) do
        PostCreator.new(
          user,
          raw: "this is the new content for my topic",
          title: "this is my new topic title",
        )
      end

      it "queues a job on create if sentiment analysis is enabled" do
        SiteSetting.ai_sentiment_enabled = true

        expect { creator.create }.to change(Jobs::PostSentimentAnalysis.jobs, :size).by(1)
      end

      it "does nothing if sentiment analysis is disabled" do
        SiteSetting.ai_sentiment_enabled = false

        expect { creator.create }.not_to change(Jobs::PostSentimentAnalysis.jobs, :size)
      end
    end

    context "when editing a post" do
      fab!(:post) { Fabricate(:post, user: user) }
      let(:revisor) { PostRevisor.new(post) }

      it "queues a job on update if sentiment analysis is enabled" do
        SiteSetting.ai_sentiment_enabled = true

        expect { revisor.revise!(user, raw: "This is my new test") }.to change(
          Jobs::PostSentimentAnalysis.jobs,
          :size,
        ).by(1)
      end

      it "does nothing if sentiment analysis is disabled" do
        SiteSetting.ai_sentiment_enabled = false

        expect { revisor.revise!(user, raw: "This is my new test") }.not_to change(
          Jobs::PostSentimentAnalysis.jobs,
          :size,
        )
      end
    end
  end

  describe "custom reports" do
    before { SiteSetting.ai_sentiment_inference_service_api_endpoint = "http://test.com" }

    fab!(:pm) { Fabricate(:private_message_post) }

    fab!(:post_1) { Fabricate(:post) }
    fab!(:post_2) { Fabricate(:post) }

    describe "overall_sentiment report" do
      let(:positive_classification) { { negative: 2, neutral: 30, positive: 70 } }
      let(:negative_classification) { { negative: 65, neutral: 2, positive: 10 } }

      def sentiment_classification(post, classification)
        Fabricate(:sentiment_classification, target: post, classification: classification)
      end

      it "calculate averages using only public posts" do
        sentiment_classification(post_1, positive_classification)
        sentiment_classification(post_2, negative_classification)
        sentiment_classification(pm, positive_classification)

        report = Report.find("overall_sentiment")
        positive_data_point = report.data[0][:data].first[:y].to_i
        negative_data_point = report.data[1][:data].first[:y].to_i

        expect(positive_data_point).to eq(1)
        expect(negative_data_point).to eq(-1)
      end
    end

    describe "post_emotion report" do
      let(:emotion_1) do
        { sadness: 49, surprise: 23, neutral: 6, fear: 34, anger: 87, joy: 22, disgust: 70 }
      end
      let(:emotion_2) do
        { sadness: 19, surprise: 63, neutral: 45, fear: 44, anger: 27, joy: 62, disgust: 30 }
      end
      let(:model_used) { "emotion" }

      def emotion_classification(post, classification)
        Fabricate(
          :sentiment_classification,
          target: post,
          model_used: model_used,
          classification: classification,
        )
      end

      def strip_emoji_and_downcase(str)
        stripped_str = str.gsub(/[^\p{L}\p{N}]+/, "") # remove any non-alphanumeric characters
        stripped_str.downcase
      end

      it "calculate averages using only public posts" do
        threshold = 30

        emotion_classification(post_1, emotion_1)
        emotion_classification(post_2, emotion_2)
        emotion_classification(pm, emotion_2)

        report = Report.find("post_emotion")

        data_point = report.data

        data_point.each do |point|
          emotion = strip_emoji_and_downcase(point[:label])
          expected =
            (emotion_1[emotion.to_sym] > threshold ? 1 : 0) +
              (emotion_2[emotion.to_sym] > threshold ? 1 : 0)
          expect(point[:data][0][:y]).to eq(expected)
        end
      end
    end
  end
end
