# frozen_string_literal: true

require 'spec_helper'

describe ReleaseTools::Slack::Message do
  describe '.post' do
    let(:slack_channel) { 'foo' }
    let(:message) { 'baz' }

    context 'without credentials' do
      it 'raises NoCredentialsError' do
        ClimateControl.modify(SLACK_APP_BOT_TOKEN: nil) do
          expect do
            described_class.post(channel: slack_channel, message: message)
          end.to raise_error(described_class::NoCredentialsError)
        end
      end
    end

    context 'with credentials' do
      let(:fake_client) { double(:client) }

      let(:request_params) do
        {
          channel: slack_channel,
          text: message,
          unfurl_links: false,
          unfurl_media: false
        }
      end

      let(:json_response) do
        {
          ok: true,
          channel: 'foo',
          ts: '123456',
          message: {
            type: 'message',
            subtype: 'bot_message',
            text: message,
            ts: '123456',
            username: 'bot',
            bot_id: 'bar'
          }
        }.to_json
      end

      let(:response) do
        instance_spy(
          HTTP::Response,
          status: double(:status, success?: true),
          code: 200,
          reason: 'OK',
          body: json_response
        )
      end

      before do
        allow(described_class)
          .to receive(:client).and_return(fake_client)

        allow(fake_client)
          .to receive(:post).and_return(response)
      end

      it 'posts a message on a specific Slack channel' do
        expect(fake_client)
          .to receive(:post)
          .with(
            described_class::API_URL,
            json: request_params
          )

        ClimateControl.modify(SLACK_APP_BOT_TOKEN: '123456') do
          without_dry_run do
            described_class.post(channel: slack_channel, message: message)
          end
        end
      end

      context 'when slack_down FF is on' do
        before do
          enable_feature(:slack_down)
        end

        it 'does not call API' do
          expect(fake_client).not_to receive(:post)

          ClimateControl.modify(SLACK_APP_BOT_TOKEN: '123456') do
            without_dry_run do
              expect(described_class.post(channel: slack_channel, message: message)).to eq({})
            end
          end
        end
      end

      context 'with blocks' do
        it 'sends blocks' do
          blocks = [
            {
              type: 'section',
              text: { type: 'mrkdwn', text: 'foo' }
            }
          ]

          expect(fake_client)
            .to receive(:post)
            .with(
              described_class::API_URL,
              json: request_params.merge(blocks: blocks)
            )

          ClimateControl.modify(SLACK_APP_BOT_TOKEN: '123456') do
            without_dry_run do
              described_class.post(channel: slack_channel, message: message, blocks: blocks)
            end
          end
        end
      end

      context 'with thread_ts' do
        it 'sends a threaded message' do
          thread_ts = '123456'

          expect(fake_client)
            .to receive(:post)
            .with(
              described_class::API_URL,
              json: request_params.merge(thread_ts: thread_ts)
            )

          ClimateControl.modify(SLACK_APP_BOT_TOKEN: '123456') do
            without_dry_run do
              described_class.post(channel: slack_channel, message: message, additional_options: { thread_ts: thread_ts })
            end
          end
        end
      end

      context 'with unfurl options' do
        it 'overrides default unfurl options' do
          expect(fake_client)
            .to receive(:post)
            .with(
              described_class::API_URL,
              json: request_params.merge(unfurl_links: true, unfurl_media: true)
            )

          ClimateControl.modify(SLACK_APP_BOT_TOKEN: '123456') do
            without_dry_run do
              described_class.post(channel: slack_channel, message: message, additional_options: { unfurl_links: true, unfurl_media: true })
            end
          end
        end
      end

      context 'when response is not ok' do
        let(:json_response) { { ok: false, error: 'invalid_auth' }.to_json }

        around do |ex|
          ClimateControl.modify(SLACK_APP_BOT_TOKEN: '123456') do
            without_dry_run do
              ex.run
            end
          end
        end

        it 'raises CouldNotPostMessageError' do
          expect { described_class.post(channel: slack_channel, message: message) }
            .to raise_error(described_class::CouldNotPostMessageError)
        end
      end

      context 'when response is not successful' do
        let(:response) do
          instance_spy(
            HTTP::Response,
            status: double(:status, success?: false),
            code: 400,
            reason: 'Bad Request',
            body: nil
          )
        end

        around do |ex|
          ClimateControl.modify(SLACK_APP_BOT_TOKEN: '123456') do
            without_dry_run do
              ex.run
            end
          end
        end

        it 'raises CouldNotPostMessageError' do
          expect { described_class.post(channel: slack_channel, message: message) }
            .to raise_error(described_class::CouldNotPostMessageError)
        end
      end

      context 'with dry run' do
        it 'does nothing' do
          expect(described_class).not_to receive(:client)

          ClimateControl.modify(SLACK_APP_BOT_TOKEN: '123456') do
            expect(described_class.post(channel: slack_channel, message: message)).to eq({})
          end
        end
      end
    end
  end
end
