require 'rails_helper'

RSpec.describe 'Deerjikists API', type: :request do
  let(:platform) { 'nico' }
  let(:code) { 'deerjika-bot' }

  let!(:tag1) { create(:tag, category: :deerjikist) }
  let!(:tag2) { create(:tag, category: :deerjikist) }

  let(:member) { create(:user, :member) }
  let(:guest) { create(:user, role: :guest) }

  describe 'GET /deerjikists/:platform/:code' do
    subject(:do_request) do
      get "/deerjikists/#{ platform }/#{ code }"
    end

    context 'when deerjikist exists' do
      before do
        Deerjikist.create!(platform:, code:, tag: tag1)
      end

      it 'returns 200 and deerjikist json' do
        do_request
        expect(response).to have_http_status(:ok)

        expect(json).to be_a(Hash)
        expect(json['platform']).to eq(platform)
        expect(json['code']).to eq(code)

        expect(json['tag']).to be_a(Hash)
        expect(json['tag']['id']).to eq(tag1.id)
        expect(json['tag']['name']).to eq(tag1.name)
      end
    end

    context 'when deerjikist does not exist' do
      it 'returns 404' do
        do_request
        expect(response).to have_http_status(:not_found)
      end
    end

    context 'when platform or code become blank after strip' do
      it 'returns 400' do
        get '/deerjikists/%20/%20'
        expect(response).to have_http_status(:bad_request)
      end
    end
  end

  describe 'PUT /deerjikists/:platform/:code' do
    subject(:do_request) do
      put "/deerjikists/#{ platform }/#{ code }", params: payload
    end

    let(:payload) { { tag_id: tag1.id } }

    context 'when not legged in' do
      it 'returns 401' do
        sign_out
        do_request
        expect(response).to have_http_status(:unauthorized)
      end
    end

    context 'when logged in but not member' do
      it 'returns 403' do
        sign_in_as guest
        do_request
        expect(response).to have_http_status(:forbidden)
      end
    end

    context 'when member' do
      before do
        sign_in_as member
      end

      context 'when params invalid' do
        it 'returns 400 when tag_id is missing or invalid' do
          put "/deerjikists/#{ platform }/#{ code }", params: { tag_id: 0 }
          expect(response).to have_http_status(:bad_request)
        end

        it 'returns 400 when platform or code blank after strip' do
          put '/deerjikists/%20/%20', params: { tag_id: tag1.id }
          expect(response).to have_http_status(:bad_request)
        end
      end

      context 'when creating new deerjikist' do
        it 'creates and returns 200 with json' do
          expect { do_request }.to change { Deerjikist.count }.by(1)
          expect(response).to have_http_status(:ok)

          d = Deerjikist.find_by(platform:, code:)
          expect(d).to be_present
          expect(d.tag_id).to eq(tag1.id)

          expect(json['platform']).to eq(platform)
          expect(json['code']).to eq(code)
        end
      end

      context 'when updating existing deerjikist' do
        before do
          Deerjikist.create!(platform:, code:, tag: tag1)
        end

        let(:payload) { { tag_id: tag2.id } }

        it 'updates tag_id and returns 200' do
          expect { do_request }.not_to change { Deerjikist.count }
          expect(response).to have_http_status(:ok)

          d = Deerjikist.find_by(platform:, code:)
          expect(d.tag_id).to eq(tag2.id)

          expect(json['platform']).to eq(platform)
          expect(json['code']).to eq(code)
        end
      end
    end
  end

  describe 'DELETE /deerjikists/:platform/:code' do
    subject(:do_request) do
      delete "/deerjikists/#{ platform }/#{ code }"
    end

    context 'when not logged in' do
      it 'returns 401' do
        sign_out
        do_request
        expect(response).to have_http_status(:unauthorized)
      end
    end

    context 'when logged in but not member' do
      it 'returns 403' do
        sign_in_as guest
        do_request
        expect(response).to have_http_status(:forbidden)
      end
    end

    context 'when member' do
      before do
        sign_in_as member
      end

      it 'returns 400 when platform/code blank after strip' do
        delete '/deerjikists/%20/%20'
        expect(response).to have_http_status(:bad_request)
      end

      context 'when deerjikist exists' do
        before do
          Deerjikist.create!(platform: platform, code: code, tag: tag1)
        end

        it 'destroys and returns 204' do
          expect {
            do_request
          }.to change { Deerjikist.exists?(platform: platform, code: code) }
            .from(true).to(false)

          expect(response).to have_http_status(:no_content)
        end
      end

      context 'when deerjikist does not exist' do
        it 'returns 404' do
          do_request
          expect(response).to have_http_status(:not_found)
        end
      end
    end
  end
end
