# frozen_string_literal: true

describe Octokit::Client::Users do
  before(:each) do
    Octokit.reset!
    @client = oauth_client
  end

  describe '.all_users', :vcr do
    it 'returns all GitHub users' do
      users = Octokit.all_users
      expect(users).to be_kind_of Array
    end
  end # .all_users

  describe '.user', :vcr do
    it 'returns a user' do
      user = Octokit.client.user('sferik')
      expect(user.login).to eq('sferik')
    end

    it 'handle [bot] users', :vcr do
      user = Octokit.client.user('shipit[bot]')
      expect(user.login).to eq('shipit[bot]')
    end

    it 'returns the authenticated user' do
      user = @client.user
      expect(user.login).to eq(test_github_login)
    end
  end # .user

  describe '.validate_credentials', :vcr do
    it 'validates username and password' do
      result = Octokit.validate_credentials(
        login: test_github_login,
        password: test_github_password
      )

      expect(result).to be_truthy
    end
  end # .validate_credentials

  describe '.update_user', :vcr do
    it 'updates a user profile' do
      user = @client.update_user(location: 'San Francisco, CA', hireable: false)
      expect(user.login).to eq(test_github_login)
      assert_requested :patch, github_url('/user')
    end
  end # .update_user

  describe '.followers', :vcr do
    it 'returns followers for a user' do
      users = Octokit.followers('sferik')
      expect(users).to be_kind_of Array
      assert_requested :get, github_url('/users/sferik/followers')
    end
    it "returns the authenticated user's followers" do
      users = @client.followers
      expect(users).to be_kind_of Array
      assert_requested :get, github_url("/users/#{test_github_login}/followers")
    end
  end # .followers

  describe '.following', :vcr do
    it 'returns following for a user' do
      users = Octokit.following('sferik')
      expect(users).to be_kind_of Array
      assert_requested :get, github_url('/users/sferik/following')
    end
    it "returns the authenticated user's following" do
      users = @client.following
      expect(users).to be_kind_of Array
      assert_requested :get, github_url("/users/#{test_github_login}/following")
    end
  end # .following

  describe '.follows?', :vcr do
    it 'checks if the authenticated user follows another' do
      @client.follows?('sferik')
      assert_requested :get, github_url('/user/following/sferik')
    end

    it 'checks if given user is following target user' do
      @client.follows?('sferik', 'pengwynn')
      assert_requested :get, github_url('/users/sferik/following/pengwynn')
    end
  end # .follows?

  describe '.follow', :vcr do
    it 'follows a user' do
      @client.follow('pengwynn')
      assert_requested :put, github_url('/user/following/pengwynn')
    end
  end # .follow

  describe '.unfollow', :vcr do
    it 'unfollows a user' do
      @client.unfollow('pengwynn')
      assert_requested :delete, github_url('/user/following/pengwynn')
    end
  end # .unfollow

  describe '.starred?', :vcr do
    it 'checks if the authenticated user has starred a repository' do
      @client.starred?('sferik/rails_admin')
      assert_requested :get, github_url('/user/starred/sferik/rails_admin')
    end
  end # .starred?

  describe '.starred', :vcr do
    context 'not authenticated' do
      it "gets a user's starred repositories" do
        Octokit.starred('sferik')
        assert_requested :get, github_url('/users/sferik/starred')
      end
    end
    context 'authenticated' do
      it "gets the authenticated user's starred repositories" do
        @client.starred
        assert_requested :get, github_url('/user/starred')
      end

      it "gets a user's starred repositories" do
        @client.starred('sferik')
        assert_requested :get, github_url('/users/sferik/starred')
      end
    end
  end # .starred

  describe '.keys', :vcr do
    it 'returns public keys for the authenticated user' do
      public_keys = @client.keys
      expect(public_keys).to be_kind_of Array
      assert_requested :get, github_url('/user/keys')
    end
  end # .keys

  describe '.user_keys', :vcr do
    it 'returns public keys for another user' do
      public_keys = Octokit.user_keys('pengwynn')
      expect(public_keys).to be_kind_of Array
      assert_requested :get, github_url('/users/pengwynn/keys')
    end
  end # .user_keys

  context 'methods requiring an existing @public_key', :vcr do
    before(:each) do
      title = 'wynning'
      key = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDN/h7Hf5TA6G4p19deF8YS9COfuBd133GPs49tO6AU/DKIt7tlitbnUnttT0VbNZM4fplyinPu5vJl60eusn/Ngq2vDfSHP5SfgHfA9H8cnHGPYG7w6F0CujFB3tjBhHa3L6Je50E3NC4+BGGhZMpUoTClEI5yEzx3qosRfpfJu/2MUp/V2aARCAiHUlUoj5eiB15zC25uDsY7SYxkh1JO0ecKSMISc/OCdg0kwi7it4t7S/qm8Wh9pVGuA5FmVk8w0hvL+hHWB9GT02WPqiesMaS9Sj3t0yuRwgwzLDaudQPKKTKYXi+SjwXxTJ/lei2bZTMC4QxYbqfqYQt66pQB wynn.netherland+api-padawan@gmail.com'
      @public_key = @client.add_key(title, key)
    end

    after(:each) do
      @client.remove_key(@public_key.id)
    end

    describe '.add_key' do
      it 'adds a public key' do
        assert_requested :post, github_url('/user/keys')
      end
    end # .add_key

    describe '.key' do
      it 'returns a public key' do
        @client.key @public_key.id
        assert_requested :get, github_url("/user/keys/#{@public_key.id}")
      end
    end

    describe '.update_key' do
      it 'updates a public key' do
        expect { @client.update_key(@public_key.id, title: 'Updated key') }
          .to raise_error(Octokit::MethodNotAllowed)
        assert_requested :patch, github_url("/user/keys/#{@public_key.id}")
      end
    end # .update_key

    describe '.remove_key' do
      it 'removes a public key' do
        @client.remove_key(@public_key.id)
        assert_requested :delete, github_url("/user/keys/#{@public_key.id}")
      end
    end # .remove_key
  end # @public_key methods

  describe '.emails', :vcr do
    it 'returns email addresses' do
      emails = @client.emails
      expect(emails).to be_kind_of Array
      assert_requested :get, github_url('/user/emails')
    end
  end # .emails

  describe '.add_email', :vcr do
    it 'adds an email address' do
      @client.add_email('wynn.netherland+apitest@gmail.com')
      assert_requested :post, github_url('/user/emails')
    end
  end # .add_email

  describe '.remove_email', :vcr do
    it 'removes an email address' do
      @client.remove_email('wynn.netherland+apitest@gmail.com')
      assert_requested :delete, github_url('/user/emails')
    end
  end # .remove_email

  describe '.subscriptions', :vcr do
    context 'not authenticated' do
      it 'returns the repositories a user watches for notifications' do
        Octokit.subscriptions('pengwynn')
        assert_requested :get, github_url('/users/pengwynn/subscriptions')
      end
    end

    context 'authenticated' do
      it 'returns the repositories the authenticated user watches for notifications' do
        @client.subscriptions
        assert_requested :get, github_url('/user/subscriptions')
      end

      it 'returns the repositories a user watches for notifications' do
        @client.subscriptions('pengwynn')
        assert_requested :get, github_url('/users/pengwynn/subscriptions')
      end
    end
  end # .subscriptions

  describe '.exchange_code_for_token' do
    context 'with application authenticated client' do
      it 'returns the access_token' do
        request_body = {
          code: 'code',
          client_id: '123',
          client_secret: '345'
        }

        client = Octokit::Client.new(
          client_id: request_body[:client_id],
          client_secret: request_body[:client_secret]
        )

        request = stub_post('https://github.com/login/oauth/access_token')
                  .with(
                    basic_auth: [request_body[:client_id], request_body[:client_secret]],
                    body: request_body.to_json
                  ).to_return(json_response('web_flow_token.json'))

        response = client.exchange_code_for_token('code')

        expect(response.access_token).to eq 'this_be_ye_token/use_it_wisely'
        assert_requested request
      end
    end # with application authenticated client

    context 'with credentials passed as parameters by unauthed client' do
      it 'returns the access_token' do
        client = Octokit::Client.new
        post = stub_request(:post, 'https://github.com/login/oauth/access_token')
               .with(body: { code: 'code', client_id: 'id', client_secret: 'secret' }.to_json)
               .to_return(json_response('web_flow_token.json'))
        response = client.exchange_code_for_token('code', 'id', 'secret')
        expect(response.access_token).to eq 'this_be_ye_token/use_it_wisely'
        assert_requested post
      end
    end # with credentials passed as parameters
  end # .exchange_code_for_token

  describe '.refresh_access_token' do
    context 'with application authenticated client' do
      it 'returns the access_token' do
        request_body = {
          refresh_token: 'code',
          client_id: '123',
          client_secret: '345',
          grant_type: 'refresh_token'
        }

        client = Octokit::Client.new(
          client_id: request_body[:client_id],
          client_secret: request_body[:client_secret]
        )

        request = stub_post('https://github.com/login/oauth/access_token')
                  .with(
                    basic_auth: [request_body[:client_id], request_body[:client_secret]],
                    body: request_body.to_json
                  ).to_return(json_response('renew_access_token.json'))

        response = client.refresh_access_token('code')

        expect(response.access_token).to eq 'new_access_token'
        expect(response.refresh_token).to eq 'new_refresh_token'
        assert_requested request
      end
    end # with application authenticated client

    context 'with credentials passed as parameters by unauthed client' do
      it 'returns the access_token' do
        client = Octokit::Client.new
        post = stub_request(:post, 'https://github.com/login/oauth/access_token')
               .with(body: { refresh_token: 'code', client_id: 'id', client_secret: 'secret', grant_type: 'refresh_token' }.to_json)
               .to_return(json_response('renew_access_token.json'))
        response = client.refresh_access_token('code', 'id', 'secret')
        expect(response.access_token).to eq 'new_access_token'
        expect(response.refresh_token).to eq 'new_refresh_token'
        assert_requested post
      end # with credentials passed as parameters
    end # .refresh_access_token
  end

  describe '.migrations', :vcr do
    it 'starts a migration for a user' do
      result = @client.start_user_migration(['snakeoil-ceo/the-insecure'], lock_repositories: true)
      expect(result).to be_kind_of Sawyer::Resource
      assert_requested :post, github_url('/user/migrations')
    end

    it 'lists migrations for a user' do
      result = @client.user_migrations
      expect(result).to be_kind_of Array
      assert_requested :get, github_url('/user/migrations')
    end

    it 'gets the status of a user migration' do
      result = @client.user_migration_status(400_195)
      expect(result).to be_kind_of Sawyer::Resource
      assert_requested :get, github_url('/user/migrations/400195')
    end

    it 'lists repositories for a user migration' do
      result = @client.user_migration_repositories(400_195)
      expect(result).to be_kind_of Array
      assert_requested :get, github_url('/user/migrations/400195/repositories')
    end

    it 'gets a user migration archive url' do
      result = @client.user_migration_archive_url(400_195)
      expect(result).to be_kind_of String
      assert_requested :get, github_url('/user/migrations/400195/archive')
    end

    it 'deletes a user migration archive' do
      result = @client.delete_user_migration_archive(400_195)
      expect(result).to be_kind_of String
      assert_requested :delete, github_url('/user/migrations/400195/archive')
    end

    it 'unlocks a migrated user repository' do
      @client.unlock_user_repository(400_195, 'the-insecure')
      expect(@client.last_response.status).to eq(204)
      assert_requested :delete, github_url('/user/migrations/400195/repos/the-insecure/lock')
    end
  end # .migrations
end
