# Copyright 2013 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import base64
import copy
import json
import urllib
import urlparse
import uuid

from keystone import config
from keystone.common import dependency
from keystone.contrib.oauth2 import core
from keystone.tests import test_v3

CONF = config.CONF

class OAuth2BaseTests(test_v3.RestfulTestCase):


    EXTENSION_NAME = 'oauth2'
    EXTENSION_TO_ADD = 'oauth2_extension'

    PATH_PREFIX = '/OS-OAUTH2'
    CONSUMER_URL = PATH_PREFIX + '/consumers'
    USERS_URL = '/users/{user_id}'
    ACCESS_TOKENS_URL = PATH_PREFIX + '/access_tokens'

    DEFAULT_REDIRECT_URIS = [
        'https://%s.com' %uuid.uuid4().hex,
        'https://%s.com' %uuid.uuid4().hex
    ]
    DEFAULT_SCOPES = [
        uuid.uuid4().hex,
        uuid.uuid4().hex,
        'all_info'
    ]

    def setUp(self):
        super(OAuth2BaseTests, self).setUp()

        # Now that the app has been served, we can query CONF values
        self.base_url = 'http://localhost/v3'
        # NOTE(garcianavalon) I've put this line for dependency injection to work, 
        # but I don't know if its the right way to do it...
        self.manager = core.Manager()

    def _create_consumer(self, name=None, description=None,
                         client_type='confidential',
                         redirect_uris=DEFAULT_REDIRECT_URIS,
                         grant_type='authorization_code',
                         scopes=DEFAULT_SCOPES,
                         **kwargs):
        if not name:
            name = uuid.uuid4().hex
        data = {
            'name': name,
            'description': description,
            'client_type': client_type,
            'redirect_uris': redirect_uris,
            'grant_type': grant_type,
            'scopes': scopes
        }
        # extra
        data.update(kwargs)
        response = self.post(self.CONSUMER_URL, body={'consumer': data})

        return response.result['consumer'], data

    def _create_user_and_tenant(self):
        pass

class ConsumerCRUDTests(OAuth2BaseTests):


    def test_create_consumer(self):
        consumer, data = self._create_consumer()
        self.assertEqual(consumer['description'], data['description'])
        self.assertIsNotNone(consumer['id'])
        self.assertIsNotNone(consumer['name'])
        self.assertIsNotNone(consumer['secret'])
        # NOTE(garcianavalon) removed because owner field is removed
        # self.assertEqual(self.user['id'], consumer['owner'])

    def test_create_consumer_with_extra(self):
        extra_data = {
            'url': uuid.uuid4().hex,
            'image': uuid.uuid4().hex
        }
        consumer, data = self._create_consumer(**extra_data)
        self.assertEqual(consumer['description'], data['description'])
        self.assertIsNotNone(consumer['id'])
        self.assertIsNotNone(consumer['name'])
        self.assertIsNotNone(consumer['secret'])
        # NOTE(garcianavalon) removed because owner field is removed
        # self.assertEqual(self.user['id'], consumer['owner'])
        for k in extra_data:
            self.assertEqual(extra_data[k], consumer[k])

    def test_consumer_delete(self):
        consumer, data = self._create_consumer()
        consumer_id = consumer['id']
        response = self.delete(self.CONSUMER_URL + '/%s' % consumer_id,
                                expected_status=204)

    def test_consumer_delete_bad_id(self):
        consumer, data = self._create_consumer()
        consumer_id = uuid.uuid4().hex
        response = self.delete(self.CONSUMER_URL + '/%s' % consumer_id,
                                expected_status=404)

    def test_consumer_get(self):
        consumer, data = self._create_consumer()
        consumer_id = consumer['id']
        response = self.get(self.CONSUMER_URL + '/%s' % consumer_id)
        self_url = ['http://localhost/v3', self.CONSUMER_URL,
                    '/', consumer_id]
        self_url = ''.join(self_url)
        self.assertEqual(response.result['consumer']['links']['self'], self_url)
        self.assertEqual(response.result['consumer']['id'], consumer_id)

    def test_consumer_get_bad_id(self):
        self.get(self.CONSUMER_URL + '/%(consumer_id)s'
                 % {'consumer_id': uuid.uuid4().hex},
                 expected_status=404)

    def test_consumer_list(self):
        self._create_consumer()
        response = self.get(self.CONSUMER_URL)
        entities = response.result['consumers']
        self.assertIsNotNone(entities)

        self_url = ['http://localhost/v3', self.CONSUMER_URL]
        self_url = ''.join(self_url)
        self.assertEqual(
response.result['links']['self'], self_url)
        self.assertValidListLinks(response.result['links'])
       
    def test_consumer_update(self):
        consumer, data = self._create_consumer()
        original_id = consumer['id']
        original_description = consumer['description'] or ''
        update_description = original_description + '_new'
        update_scopes = ['new_scopes'
]
        update_redirect_uris = ['new_uris']

        body = {
            'consumer': {
                'description': update_description,
                'scopes': update_scopes,
                'redirect_uris': update_redirect_uris
            }
        }
        update_response = self.patch(self.CONSUMER_URL + '/%s' % original_id,
                                 body=body)
        consumer = update_response.result['consumer']
        self.assertEqual(consumer['description'], update_description)
        self.assertEqual(consumer['scopes'], update_scopes)
        self.assertEqual(consumer['redirect_uris'], update_redirect_uris)
        self.assertEqual(consumer['id'], original_id)

    def test_consumer_update_bad_secret(self):
        consumer, data = self._create_consumer()
        original_id = consumer['id']
        update_ref = copy.deepcopy(consumer)
        update_ref['description'] = uuid.uuid4().hex
        update_ref['secret'] = uuid.uuid4().hex
        self.patch(self.CONSUMER_URL + '/%s' % original_id,
                   body={'consumer': update_ref},
                   expected_status=400)

    def test_consumer_update_bad_id(self):
        consumer, data = self._create_consumer()
        original_id = consumer['id']
        original_description = consumer['description'] or ''
        update_description = original_description + "_new"

        update_ref = copy.deepcopy(consumer)
        update_ref['description'] = update_description
        update_ref['id'] = uuid.uuid4().hex
        self.patch(self.CONSUMER_URL + '/%s' % original_id,
                   body={'consumer': update_ref},
                   expected_status=400) 

@dependency.requires('oauth2_api')
class AccessTokenEndpointTests(OAuth2BaseTests):

    def new_access_token_ref(self, user_id, consumer_id):
        token_ref = {
            'id':uuid.uuid4().hex,
            'consumer_id':consumer_id,
            'authorizing_user_id':user_id,
            'scopes': [uuid.uuid4().hex],
            'expires_at':uuid.uuid4().hex,
        }
        return token_ref

    def _create_access_token(self, user_id, consumer_id):
        token_ref = self.new_access_token_ref(user_id, consumer_id)
        access_token = self.oauth2_api.store_access_token(token_ref)
        return access_token

    def _list_access_tokens(self, user_id, expected_status=200):
        url = self.USERS_URL.format(user_id=user_id) + self.ACCESS_TOKENS_URL
        response = self.get(url, expected_status=expected_status)
        return response.result['access_tokens']

    def _get_access_token(self, user_id, token_id, expected_sta