import os
import unittest
from unittest import mock

import bitcoin
from aws_lambda_tools.common.user_type import ConioUser
from conio_domain.wallet import WalletCryptoCurrency
from vault_client.client.types import AcceptanceType, AcceptanceChoiceType
from vault_client.generated_protobuf import vault_pb2

from conio_sdk.generated_protobuf import v1_pb2
from conio_sdk.services.conio.sdk.login_vo_service import LoginVOServices
from conio_sdk.services.conio.user.user_service import UserService, AuthenticationData, UserCredentials
from conio_sdk.services.conio.wallet.bitcoin_wallet_service import BitcoinWalletService


class TestLoginVOServices(unittest.TestCase):
    def setUp(self):
        self.wallet_id = 'a wallet id'
        self.external_reference = 'an external reference'
        self.user_info = {
            'reference_key_id': self.wallet_id,
            'external_references': [self.external_reference],
            'status': vault_pb2.ACTIVE,
            'wallets': [
                {
                    'reference_id': 'a wallet id',
                    'encrypted_private_key': os.urandom(16),
                    'encrypted_seed': os.urandom(16),
                    'encrypted_mnemonic': os.urandom(16),
                    'crypto_currency': WalletCryptoCurrency.BITCOIN
                }
            ],
            'acceptances': {
                AcceptanceType.MARKETING: AcceptanceChoiceType.ACCEPTED,
                AcceptanceType.APP_IMPROVEMENT: AcceptanceChoiceType.REJECTED,
                AcceptanceType.CLIENT_SUPPORT: AcceptanceChoiceType.ACCEPTED
            }
        }
        self.user = ConioUser(data=self.user_info)
        self.pb2_user = vault_pb2.User(
            **{k: v for k, v in self.user_info.items() if k not in ('wallets', 'acceptances')})
        self.pb2_user.wallets.add(
            reference_id=self.user.get_wallet(WalletCryptoCurrency.BITCOIN)['reference_id'],
            encrypted_private_key=self.user.get_wallet(WalletCryptoCurrency.BITCOIN)['encrypted_private_key'],
            encrypted_seed=self.user.get_wallet(WalletCryptoCurrency.BITCOIN)['encrypted_seed'],
            encrypted_mnemonic=self.user.get_wallet(WalletCryptoCurrency.BITCOIN)['encrypted_mnemonic'],
        )
        for k, v in self.user.acceptances.items():
            self.pb2_user.acceptances.add(
                acceptance_type=k.value,
                acceptance_choice=v.value
            )
        self.user = ConioUser(data=self.user_info)
        self.external_reference = 'an_external_reference'
        self.user_service = mock.create_autospec(UserService)
        self.bitcoin_wallet_service = mock.create_autospec(BitcoinWalletService)
        self.sut = LoginVOServices(self.user_service, self.bitcoin_wallet_service)

    def test(self):
        self.bitcoin_wallet_service.get_bip32_public_key.return_value = \
            bitcoin.bip32_privtopub(bitcoin.bip32_master_key(os.urandom(16)))
        self.user_service.external_user_login.return_value = AuthenticationData(
            'an access token', 'a refresh token', 'a scope'
        )
        self.user_service.get_user.return_value = self.user
        login = v1_pb2.Login()
        login.conioCredentials.externalUserID = 'a user id'
        login.conioCredentials.hashedConioPassword = 'a password'

        login_response, authentication_data = self.sut.login(login, 'it')

        self.assertEqual(
            login_response.encryptedUserKey.encryptedMnemonic,
            self.user.get_wallet(WalletCryptoCurrency.BITCOIN)['encrypted_mnemonic'])
        self.assertEqual(
            login_response.encryptedUserKey.encryptedSeed,
            self.user.get_wallet(WalletCryptoCurrency.BITCOIN)['encrypted_seed'])
        self.assertEqual(
            login_response.encryptedUserKey.encryptedPrivateKey,
            self.user.get_wallet(WalletCryptoCurrency.BITCOIN)['encrypted_private_key'])
        self.assertEqual(
            login_response.encryptedUserKey.bip32PublicKey,
            self.bitcoin_wallet_service.get_bip32_public_key.return_value)

        self.assertEqual(
            self.user_service.external_user_login.return_value.current_scope,
            authentication_data.current_scope)
        self.assertEqual(
            self.user_service.external_user_login.return_value.access_token,
            authentication_data.access_token)
        self.assertEqual(
            self.user_service.external_user_login.return_value.refresh_token,
            authentication_data.refresh_token)

        self.bitcoin_wallet_service.get_bip32_public_key.assert_called_once_with(
            self.wallet_id
        )
        self.user_service.external_user_login.assert_called_once_with(
            UserCredentials(
                login.conioCredentials.externalUserID,
                login.conioCredentials.hashedConioPassword
            ),
            'it'
        )
