# frozen_string_literal: true

require 'rails_helper'

RSpec.describe UnEmojiReactService, type: :service do
  subject do
    described_class.new.call(sender, status, emoji_reaction)
    EmojiReaction.where(status: status, account: sender)
  end

  let!(:emoji_reaction) { nil }
  let(:sender) { Fabricate(:user).account }
  let(:author) { Fabricate(:user).account }
  let(:status) { Fabricate(:status, account: author) }

  context 'when a simple case' do
    let(:emoji_reaction) { Fabricate(:emoji_reaction, account: sender, status: status, name: '😀') }

    it 'unreact with emoji' do
      expect(subject.count).to eq 0
    end
  end

  context 'when no emoji reactions' do
    it 'unreact with emoji' do
      expect(subject.count).to eq 0
    end
  end

  context 'with custom emoji of local' do
    let(:emoji_reaction) { Fabricate(:emoji_reaction, account: sender, status: status, name: 'ohagi', custom_emoji: custom_emoji) }
    let(:custom_emoji) { Fabricate(:custom_emoji, shortcode: 'ohagi') }

    it 'react with emoji' do
      expect(subject.count).to eq 0
    end
  end

  context 'with custom emoji of remote' do
    let(:emoji_reaction) { Fabricate(:emoji_reaction, account: sender, status: status, name: 'ohagi', custom_emoji: custom_emoji) }
    let(:custom_emoji) { Fabricate(:custom_emoji, shortcode: 'ohagi', domain: 'foo.bar', uri: 'https://foo.bar/emoji/ohagi') }

    it 'react with emoji' do
      expect(subject.count).to eq 0
    end
  end

  context 'when other account already set' do
    let(:emoji_reaction) { Fabricate(:emoji_reaction, account: sender, status: status, name: '😀') }

    before { Fabricate(:emoji_reaction, status: status, name: '😀') }

    it 'unreact with emoji' do
      expect(subject.count).to eq 0
      expect(EmojiReaction.where(status: status).count).to eq 1
    end
  end

  context 'when this account already set multiple emojis' do
    let(:emoji_reaction) { Fabricate(:emoji_reaction, account: sender, status: status, name: '😀') }

    before do
      Fabricate(:emoji_reaction, account: sender, status: status, name: '😂')
      Fabricate(:emoji_reaction, account: sender, status: status, name: '🚗')
    end

    it 'unreact with emoji' do
      expect(subject.count).to eq 2
      expect(subject.pluck(:name)).to contain_exactly('😂', '🚗')
    end
  end

  context 'with remove all emojis' do
    before do
      Fabricate(:emoji_reaction, account: sender, status: status, name: '😀')
      Fabricate(:emoji_reaction, account: sender, status: status, name: '😂')
      Fabricate(:emoji_reaction, account: sender, status: status, name: '🚗')
    end

    it 'react with emoji' do
      expect(subject.count).to eq 0
    end
  end

  context 'when remote status' do
    let(:author) { Fabricate(:account, domain: 'author.foo.bar', uri: 'https://author.foo.bar/actor', inbox_url: 'https://author.foo.bar/inbox', protocol: 'activitypub') }
    let(:emoji_reaction) { Fabricate(:emoji_reaction, account: sender, status: status, name: '😀') }

    before do
      stub_request(:post, 'https://author.foo.bar/inbox')
    end

    it 'react with emoji', :inline_jobs do
      expect(subject.count).to eq 0
      expect(a_request(:post, 'https://author.foo.bar/inbox').with(body: hash_including({
        type: 'Undo',
        actor: ActivityPub::TagManager.instance.uri_for(sender),
        content: '😀',
      }))).to have_been_made.once
    end

    context 'when has followers' do
      let!(:bob) { Fabricate(:account, domain: 'foo.bar', uri: 'https://foo.bar/actor', inbox_url: 'https://foo.bar/inbox', protocol: 'activitypub') }

      before do
        bob.follow!(sender)
        stub_request(:post, 'https://foo.bar/inbox')
      end

      it 'react with emoji', :inline_jobs do
        expect(subject.count).to eq 0
        expect(a_request(:post, 'https://foo.bar/inbox').with(body: hash_including({
          type: 'Undo',
          actor: ActivityPub::TagManager.instance.uri_for(sender),
          content: '😀',
        }))).to have_been_made.once
      end
    end
  end

  context 'when sender has remote followers' do
    let!(:bob) { Fabricate(:account, domain: 'foo.bar', uri: 'https://foo.bar/actor', inbox_url: 'https://foo.bar/inbox', protocol: 'activitypub') }
    let(:emoji_reaction) { Fabricate(:emoji_reaction, account: sender, status: status, name: '😀') }

    before do
      bob.follow!(sender)
      stub_request(:post, 'https://foo.bar/inbox')
    end

    it 'react with emoji', :inline_jobs do
      expect(subject.count).to eq 0
      expect(a_request(:post, 'https://foo.bar/inbox').with(body: hash_including({
        type: 'Undo',
        actor: ActivityPub::TagManager.instance.uri_for(sender),
        content: '😀',
      }))).to have_been_made.once
    end
  end

  context 'when has relay server' do
    let(:emoji_reaction) { Fabricate(:emoji_reaction, account: sender, status: status, name: '😀') }

    before do
      Fabricate(:relay, inbox_url: 'https://foo.bar/inbox', state: :accepted)
      stub_request(:post, 'https://foo.bar/inbox')
    end

    it 'react with emoji', :inline_jobs do
      expect(subject.count).to eq 0
      expect(a_request(:post, 'https://foo.bar/inbox').with(body: hash_including({
        type: 'Undo',
        actor: ActivityPub::TagManager.instance.uri_for(sender),
        content: '😀',
      }))).to have_been_made.once
    end
  end

  context 'when has friend server' do
    let(:emoji_reaction) { Fabricate(:emoji_reaction, account: sender, status: status, name: '😀') }

    before do
      Fabricate(:friend_domain, inbox_url: 'https://foo.bar/inbox', active_state: :accepted, pseudo_relay: true)
      stub_request(:post, 'https://foo.bar/inbox')
    end

    it 'react with emoji', :inline_jobs do
      expect(subject.count).to eq 0
      expect(a_request(:post, 'https://foo.bar/inbox').with(body: hash_including({
        type: 'Undo',
        actor: ActivityPub::TagManager.instance.uri_for(sender),
        content: '😀',
      }))).to have_been_made.once
    end
  end
end