import json
import unittest
from unittest import mock

import requests
from aws_lambda_tools.common.user_type import ConioUser
from bithustler_client.client.client import BithustlerClient
from bithustler_client.generated_protobuf import bithustler_pb2
from cards_client.client.client import CardsClient
from cards_client.generated_protobuf import base_pb2
from requests import Response
from vault_client.client.auth_client import VaultAuthClient
from vault_client.client.client_definition import VaultClient
from vault_client.generated_protobuf import vault_pb2

from conio_sdk.common import exceptions
from conio_sdk.services.conio.user.vault_service import VaultService
from etc.settings import VaultAuthCredentials


class TestUserServiceRefreshSession(unittest.TestCase):
    def _change_user_info(self, user_info: dict):
        self.user_info = user_info
        self.pb2_user = vault_pb2.User(**self.user_info)
        self.user = ConioUser(data=self.user_info)

    def setUp(self):
        self.external_reference = 'an_external_reference'
        self._change_user_info({
            'reference_key_id': 'a reference key id',
            'external_references': [self.external_reference],
            'status': vault_pb2.LOCKED
        })
        self.vault_auth_credentials = \
            VaultAuthCredentials(
                'http://test.vault.com',
                'vault_auth_username',
                'vault_auth_password'
            )
        self.cards_client = mock.create_autospec(CardsClient)
        self.payer = base_pb2.MsgCreatePayerResponse()
        self.payer.data.payer_id = 'a payer id'
        self.cards_client.create_payer.return_value = self.payer
        self.bithustler_client = mock.create_autospec(BithustlerClient)
        self.seller = bithustler_pb2.Seller(id='a seller id')
        self.bithustler_client.create_seller.return_value = self.seller
        self.vault_client = mock.create_autospec(VaultClient)
        self.vault_auth_client = mock.create_autospec(VaultAuthClient)
        self.sut = VaultService(
            self.vault_client,
            self.vault_auth_client,
            self.vault_auth_credentials,
            self.cards_client,
            self.bithustler_client
        )

    @mock._patch_object(requests, 'post')
    def test_refresh_token(self, post_mock):
        access_token, refresh_token, scope = 'an access token', 'a refresh token', 'a scope'

        def _post(*_, **__):
            resp = Response()
            resp._content = json.dumps(
                {
                    'access_token': access_token,
                    'refresh_token': refresh_token,
                    'scope': scope
                }
            ).encode()
            resp.status_code = 200
            resp.headers = {'Content-Type': 'application/json'}
            return resp
        post_mock.side_effect = _post
        authentication_data = self.sut.refresh_session('a refresh_token')
        self.assertEqual(authentication_data.refresh_token, refresh_token)
        self.assertEqual(authentication_data.access_token, access_token)
        self.assertEqual(authentication_data.current_scope, scope)

    @mock._patch_object(requests, 'post')
    def test_refresh_invalid_token(self, post_mock):
        for err in (401, 403):
            def _post(url: str, *_, **__):
                self.assertEqual('http://test.vault.com/token', url)
                resp = Response()
                resp.status_code = err
                return resp

            post_mock.side_effect = _post

            with self.assertRaises(exceptions.NotAuthenticatedException):
                self.sut.refresh_session('a refresh_token')
