# frozen_string_literal: true
require 'spec_helper'

RSpec.describe Notion::Api::Pagination::Cursor do
  let(:client) { Notion::Client.new }

  context 'default cursor' do
    let(:cursor) { described_class.new(client, 'users_list', {}) }

    it 'provides a default page_size' do
      expect(client).to receive(:users_list).with(page_size: 100)
      cursor.first
    end

    it 'handles blank response metadata' do
      expect(client).to receive(:users_list).once.and_return(Notion::Messages::Message.new)
      cursor.to_a
    end

    it 'handles nil response metadata' do
      expect(client).to(
        receive(:users_list).once.and_return(Notion::Messages::Message.new(response: nil))
      )
      cursor.to_a
    end

    it 'paginates with a cursor inside response metadata' do
      expect(client).to receive(:users_list).twice.and_return(
        Notion::Messages::Message.new(has_more: true, next_cursor: 'next'),
        Notion::Messages::Message.new
      )
      expect(cursor).not_to receive(:sleep)
      cursor.to_a
    end

    context 'with rate limiting' do
      let(:error) { Notion::Api::Errors::TooManyRequests.new({}) }

      context 'with default max retries' do
        it 'sleeps after a TooManyRequestsError' do
          expect(client).to(
            receive(:users_list)
              .with(page_size: 100)
              .ordered
              .and_return(Notion::Messages::Message.new({ has_more: true, next_cursor: 'next' }))
          )
          expect(client).to(
            receive(:users_list).with(page_size: 100, start_cursor: 'next').and_raise(error)
          )
          expect(cursor).to receive(:sleep).once.ordered.with(10)
          expect(client).to(
            receive(:users_list)
              .with(page_size: 100, start_cursor: 'next')
              .ordered
              .and_return(Notion::Messages::Message.new)
          )
          cursor.to_a
        end
      end

      context 'with a custom max_retries' do
        let(:cursor) { described_class.new(client, 'users_list', max_retries: 4) }

        it 'raises the error after hitting the max retries' do
          expect(client).to(
            receive(:users_list)
              .with(page_size: 100)
              .and_return(Notion::Messages::Message.new({ has_more: true, next_cursor: 'next' }))
          )
          expect(client).to(
            receive(:users_list).with(page_size: 100, start_cursor: 'next').exactly(5).times.and_raise(error)
          )
          expect(cursor).to receive(:sleep).exactly(4).times.with(10)
          expect { cursor.to_a }.to raise_error(error)
        end
      end

      context 'with a custom retry_after' do
        let(:cursor) { described_class.new(client, 'users_list', retry_after: 5) }

        it 'sleeps for retry_after seconds after a TooManyRequestsError' do
          expect(client).to(
            receive(:users_list)
              .with(page_size: 100)
              .ordered
              .and_return(Notion::Messages::Message.new({ has_more: true, next_cursor: 'next' }))
          )
          expect(client).to(
            receive(:users_list).with(page_size: 100, start_cursor: 'next').ordered.and_raise(error)
          )
          expect(cursor).to receive(:sleep).once.ordered.with(5)
          expect(client).to(
            receive(:users_list)
              .with(page_size: 100, start_cursor: 'next')
              .ordered
              .and_return(Notion::Messages::Message.new)
          )
          cursor.to_a
        end
      end
    end
  end

  context 'with a custom page_size' do
    let(:cursor) { described_class.new(client, 'users_list', page_size: 42) }

    it 'overrides default page_size' do
      expect(client).to receive(:users_list).with(page_size: 42)
      cursor.first
    end
  end

  context 'with a custom sleep_interval' do
    let(:cursor) { described_class.new(client, 'users_list', sleep_interval: 3) }

    it 'sleeps between requests' do
      expect(client).to receive(:users_list).exactly(3).times.and_return(
        Notion::Messages::Message.new(has_more: true, next_cursor: 'next_a'),
        Notion::Messages::Message.new(has_more: true, next_cursor: 'next_b'),
        Notion::Messages::Message.new
      )
      expect(cursor).to receive(:sleep).with(3).twice
      cursor.to_a
    end
  end
end
