import typing

import retrying

from conio_sdk.generated_protobuf import v1_pb2
from test.integration import moving_coins
from test.integration.v1.base_integration_test import BaseV1IntegrationTest, Satoshi, AuthenticationHeaders


class TestWalletInfo(BaseV1IntegrationTest):
    _SATOSHI = Satoshi(12345600)

    @moving_coins
    def test_activities(self):
        signup_response = self.signin_with_new_user()
        authentication_headers = signup_response.authentication_headers
        bip32_private_key = signup_response.bip32_private_key
        self.ensure_receive_satoshi(self._SATOSHI, authentication_headers)
        self.withdraw_btc(
            bip32_private_key, authentication_headers,
            signup_response.signup_message.cryptoRequest.email,
            satoshi_amount=self._SATOSHI // 2)
        self._test_activities(authentication_headers, v1_pb2.UNCONFIRMED)
        self.generate_blocks(6)

        retrying.retry(
            wait_exponential_multiplier=200,
            wait_exponential_max=2000, stop_max_delay=60000,
            retry_on_exception=lambda e: isinstance(e, AssertionError)
        )(self._test_activities)(authentication_headers, v1_pb2.CONFIRMED)

    def _test_activities(self, authentication_headers: AuthenticationHeaders, tx_status: int):
        expected_send = {
            'activity_type': v1_pb2.ACTIVITY_SEND,
            'satoshi': self._SATOSHI // 2,
            'status': tx_status
        }
        expected_receive = {
            'activity_type': v1_pb2.ACTIVITY_RECV,
            'satoshi': self._SATOSHI,
            'status': v1_pb2.CONFIRMED
        }

        self._check_activities(
            [expected_send, expected_receive],
            self.get_activities(authentication_headers, 0, False),
            authentication_headers
        )

        self._check_activities(
            [expected_send],
            self.get_activities(authentication_headers, 0, False, v1_pb2.ACTIVITY_SEND),
            authentication_headers
        )

        self._check_activities(
            [expected_receive],
            self.get_activities(authentication_headers, 0, False, v1_pb2.ACTIVITY_RECV),
            authentication_headers
        )

    def _check_activities(
            self, expected: typing.Sequence[typing.Dict[str, typing.Any]],
            activities: typing.Sequence[v1_pb2.SimpleActivity],
            authentication_headers: AuthenticationHeaders):
        self.assertEqual(len(expected), len(activities))
        for cur_expected, cur_activity, cur_detailed_activity \
                in zip(
                    expected,
                    activities,
                    map(lambda a: self.get_activity(a.activity_id, authentication_headers), activities)
                ):
                for k, v in cur_expected.items():
                    self.assertEqual(v, getattr(cur_activity, k), msg=str(cur_activity))
                for ks, kd in {
                                  'activity_type': 'activity_type',
                                  'activity_id': 'activity_id',
                                  'satoshi': 'transaction.amount'
                              }.items():
                    self.assertEqual(
                        self._getattr(cur_activity, ks),
                        self._getattr(cur_detailed_activity, kd)
                    )

    @classmethod
    def _getattr(cls, o, field: str):
        r = o
        for f in field.split('.'):
            r = getattr(r, f)
        return r
