require 'rails_helper'

RSpec.describe 'External Credentials', type: :request do
  let(:admin_user) { create(:admin_user) }

  context 'without authentication' do
    describe '#index' do
      it 'returns 401 unauthorized' do
        get '/api/v1/external_credentials', as: :json

        expect(response).to have_http_status(:unauthorized)
        expect(json_response).to include('error' => 'authentication failed')
      end
    end

    describe '#app_verify' do
      it 'returns 401 unauthorized' do
        post '/api/v1/external_credentials/facebook/app_verify', as: :json

        expect(response).to have_http_status(:unauthorized)
        expect(json_response).to include('error' => 'authentication failed')
      end
    end

    describe '#link_account' do
      it 'returns 401 unauthorized' do
        get '/api/v1/external_credentials/facebook/link_account', as: :json

        expect(response).to have_http_status(:unauthorized)
        expect(json_response).to include('error' => 'authentication failed')
      end
    end

    describe '#callback' do
      it 'returns 401 unauthorized' do
        get '/api/v1/external_credentials/facebook/callback', as: :json

        expect(response).to have_http_status(:unauthorized)
        expect(json_response).to include('error' => 'authentication failed')
      end
    end
  end

  context 'authenticated as admin' do
    before { authenticated_as(admin_user) }

    describe '#index' do
      it 'responds with an array of ExternalCredential records' do
        get '/api/v1/external_credentials', as: :json

        expect(response).to have_http_status(:ok)
        expect(json_response).to eq([])
      end

      context 'with expand=true URL parameters' do
        it 'responds with an array of ExternalCredential records and their association data' do
          get '/api/v1/external_credentials?expand=true', as: :json

          expect(response).to have_http_status(:ok)
          expect(json_response).to eq([])
        end
      end
    end

    context 'for Facebook' do
      let(:invalid_credentials) do
        { application_id: 123, application_secret: 123 }
      end

      describe '#app_verify' do
        describe 'failure cases' do
          context 'when permission for Facebook channel is deactivated' do
            before { Permission.find_by(name: 'admin.channel_facebook').update(active: false) }

            it 'returns 401 unauthorized with internal (Zammad) error' do
              post '/api/v1/external_credentials/facebook/app_verify', as: :json
              expect(response).to have_http_status(:unauthorized)
              expect(json_response).to include('error' => 'Not authorized (user)!')
            end
          end

          context 'with no credentials' do
            it 'returns 200 with internal (Zammad) error' do
              post '/api/v1/external_credentials/facebook/app_verify', as: :json

              expect(response).to have_http_status(:ok)
              expect(json_response).to include('error' => 'No application_id param!')
            end
          end

          context 'with invalid credentials, via request params' do
            it 'returns 200 with remote (Facebook auth) error', :use_vcr do
              post '/api/v1/external_credentials/facebook/app_verify', params: invalid_credentials, as: :json

              expect(response).to have_http_status(:ok)
              expect(json_response).to include('error' => 'type: OAuthException, code: 101, message: Error validating application. Cannot get application info due to a system error. [HTTP 400]')
            end
          end

          context 'with invalid credentials, via ExternalCredential record' do
            before { create(:facebook_credential, credentials: invalid_credentials) }

            it 'returns 200 with remote (Facebook auth) error', :use_vcr do
              post '/api/v1/external_credentials/facebook/app_verify', as: :json

              expect(response).to have_http_status(:ok)
              expect(json_response).to include('error' => 'type: OAuthException, code: 101, message: Error validating application. Cannot get application info due to a system error. [HTTP 400]')
            end
          end
        end
      end

      describe '#link_account' do
        describe 'failure cases' do
          context 'with no credentials' do
            it 'returns 422 unprocessable entity with internal (Zammad) error' do
              get '/api/v1/external_credentials/facebook/link_account', as: :json

              expect(response).to have_http_status(:unprocessable_entity)
              expect(json_response).to include('error' => 'No facebook app configured!')
            end
          end

          context 'with invalid credentials, via request params' do
            it 'returns 422 unprocessable entity with internal (Zammad) error' do
              get '/api/v1/external_credentials/facebook/link_account', params: invalid_credentials, as: :json

              expect(response).to have_http_status(:unprocessable_entity)
              expect(json_response).to include('error' => 'No facebook app configured!')
            end
          end

          context 'with invalid credentials, via ExternalCredential record' do
            before { create(:facebook_credential, credentials: invalid_credentials) }

            it 'returns 500 with remote (Facebook auth) error', :use_vcr do
              get '/api/v1/external_credentials/facebook/link_account', as: :json

              expect(response).to have_http_status(:internal_server_error)
              expect(json_response).to include('error' => 'type: OAuthException, code: 101, message: Error validating application. Cannot get application info due to a system error. [HTTP 400]')
            end
          end
        end
      end

      describe '#callback' do
        describe 'failure cases' do
          context 'with no credentials' do
            it 'returns 422 unprocessable entity with internal (Zammad) error' do
              get '/api/v1/external_credentials/facebook/callback', as: :json

              expect(response).to have_http_status(:unprocessable_entity)
              expect(json_response).to include('error' => 'No facebook app configured!')
            end
          end

          context 'with invalid credentials, via request params' do
            it 'returns 422 unprocessable entity with internal (Zammad) error' do
              get '/api/v1/external_credentials/facebook/callback', params: invalid_credentials, as: :json

              expect(response).to have_http_status(:unprocessable_entity)
              expect(json_response).to include('error' => 'No facebook app configured!')
            end
          end

          context 'with invalid credentials, via ExternalCredential record' do
            before { create(:facebook_credential, credentials: invalid_credentials) }

            it 'returns 500 with remote (Facebook auth) error', :use_vcr do
              get '/api/v1/external_credentials/facebook/callback', as: :json

              expect(response).to have_http_status(:internal_server_error)
              expect(json_response).to include('error' => 'type: OAuthException, code: 101, message: Error validating application. Cannot get application info due to a system error. [HTTP 400]')
            end
          end
        end
      end
    end

    context 'for Twitter' do
      let(:invalid_credentials) do
        { consumer_key: 123, consumer_secret: 123, oauth_token: 123, oauth_token_secret: 123 }
      end

      describe '#app_verify' do
        describe 'failure cases' do
          context 'when permission for Twitter channel is deactivated' do
            before { Permission.find_by(name: 'admin.channel_twitter').update(active: false) }

            it 'returns 401 unauthorized with internal (Zammad) error' do
              post '/api/v1/external_credentials/twitter/app_verify', as: :json
              expect(response).to have_http_status(:unauthorized)
              expect(json_response).to include('error' => 'Not authorized (user)!')
            end
          end

          context 'with no credentials' do
            it 'returns 200 with internal (Zammad) error' do
              post '/api/v1/external_credentials/twitter/app_verify', as: :json

              expect(response).to have_http_status(:ok)
              expect(json_response).to include('error' => 'No consumer_key param!')
            end
          end

          context 'with invalid credentials, via request params' do
            it 'returns 200 with remote (Twitter auth) error', :use_vcr do
              post '/api/v1/external_credentials/twitter/app_verify', params: invalid_credentials, as: :json

              expect(response).to have_http_status(:ok)
              expect(json_response).to include('error' => '401 Authorization Required')
            end
          end

          context 'with invalid credentials, via existing ExternalCredential record' do
            before { create(:twitter_credential, credentials: invalid_credentials) }

            it 'returns 200 with remote (Twitter auth) error', :use_vcr do
              post '/api/v1/external_credentials/twitter/app_verify', as: :json

              expect(response).to have_http_status(:ok)
              expect(json_response).to include('error' => '401 Authorization Required')
            end
          end
        end
      end

      describe '#link_account' do
        describe 'failure cases' do
          context 'with no credentials' do
            it 'returns 422 unprocessable entity with internal (Zammad) error' do
              get '/api/v1/external_credentials/twitter/link_account', as: :json

              expect(response).to have_http_status(:unprocessable_entity)
              expect(json_response).to include('error' => 'No twitter app configured!')
            end
          end

          context 'with invalid credentials, via request params' do
            it 'returns 422 unprocessable entity with internal (Zammad) error' do
              get '/api/v1/external_credentials/twitter/link_account', params: invalid_credentials, as: :json

              expect(response).to have_http_status(:unprocessable_entity)
              expect(json_response).to include('error' => 'No twitter app configured!')
            end
          end

          context 'with invalid credentials, via ExternalCredential record' do
            before { create(:twitter_credential, credentials: invalid_credentials) }

            it 'returns 500 with remote (Twitter auth) error', :use_vcr do
              get '/api/v1/external_credentials/twitter/link_account', as: :json

              expect(response).to have_http_status(:internal_server_error)
              expect(json_response).to include('error' => '401 Authorization Required')
            end
          end
        end
      end

      describe '#callback' do
        describe 'failure cases' do
          context 'with no credentials' do
            it 'returns 422 unprocessable entity with internal (Zammad) error' do
              get '/api/v1/external_credentials/twitter/callback', as: :json

              expect(response).to have_http_status(:unprocessable_entity)
              expect(json_response).to include('error' => 'No twitter app configured!')
            end
          end

          context 'with invalid credentials, via request params' do
            it 'returns 422 unprocessable entity with internal (Zammad) error' do
              get '/api/v1/external_credentials/twitter/callback', params: invalid_credentials, as: :json

              expect(response).to have_http_status(:unprocessable_entity)
              expect(json_response).to include('error' => 'No twitter app configured!')
            end
          end

          context 'with invalid credentials, via ExternalCredential record' do
            before { create(:twitter_credential, credentials: invalid_credentials) }

            it 'returns 422 unprocessable entity with internal (Zammad) error' do
              get '/api/v1/external_credentials/twitter/callback', as: :json

              expect(response).to have_http_status(:unprocessable_entity)
              expect(json_response).to include('error' => 'No request_token for session found!')
            end
          end
        end
      end
    end
  end
end
