import unittest
import manage_sys_paths
import json

from messaging.MessageHandler import MessageHandler

class DummyMongoRepo:
    '''Dummy class to be used for testing the MessageHandler'''
    last_trace = None

    def __init__(self):
        self.added_transactions = []
        self.duplicated_transactions = []

    def insert_trace(self, trace):
        self.last_trace = trace
    
    def add_transaction(self, transaction):
        self.added_transactions.append(transaction)

    def get_transaction_with_id(self, unique_id: str, use_case,table):
        result = []
        for trans in self.added_transactions:
            transID = trans.id()
            if transID == unique_id and trans.use_case == use_case and trans.table == table:
                result.append(trans)
        if len(result) > 0:
            return result[0]
        
        return None

    

    def add_duplicated_transaction(self, transaction):
        self.duplicated_transactions.append(transaction)

from messaging.DummyMessageManager import DummyMessageManager as DummyMessageSender
from messaging.dummy_rest_fetcher import DummyRestFetcher

class Test_MessageHandler(unittest.TestCase):
    handler = None
    repo = None
    msg_sender = None
    rest_fetcher = None

    def setUp(self):
        self.repo = DummyMongoRepo()
        self.msg_sender = DummyMessageSender.get_instance()
        self.rest_fetcher = DummyRestFetcher()
        self.handler = MessageHandler(self.repo, self.msg_sender, self.rest_fetcher)
  
    def _get_valid_message(self) -> str:
        message_values = \
        { 'type': 'blockchain-transaction',
          'content': 
            {
            "ApplicationType": "string",
            "docType": "string",
            "Metadata": {},
            "ResourceIds": "string",
            "ResourceMd5": "string",
            "ResourceState": "string",
            "Timestamp": "2019-08-27T14:00:48.587Z",
            "TransactionFrom": "string",
            "TransactionFromLatLng": "string",
            "TransactionId": "string",
            "TransactionTo": "string",
            "TransactionToLatLng": "string",
            "TransferredAsset": "string"
            }
        }
        return json.dumps(message_values)
    def _get_valid_message2(self) -> str:
        message_values = \
        { 'type': 'blockchain-transaction',
          'content': 
            {
            "ApplicationType": "string2",
            "docType": "string",
            "Metadata": {},
            "ResourceIds": "string",
            "ResourceMd5": "string",
            "ResourceState": "string",
            "Timestamp": "2019-08-27T14:00:48.587Z",
            "TransactionFrom": "string",
            "TransactionFromLatLng": "string",
            "TransactionId": "string",
            "TransactionTo": "string",
            "TransactionToLatLng": "string",
            "TransferredAsset": "string"
            }
        }
        return json.dumps(message_values)
    def _get_valid_message3(self) -> str:
        message_values = \
        { 'type': 'blockchain-transaction',
          'content': 
            {
            "ApplicationType": "string",
            "docType": "string2",
            "Metadata": {},
            "ResourceIds": "string",
            "ResourceMd5": "string",
            "ResourceState": "string",
            "Timestamp": "2019-08-27T14:00:48.587Z",
            "TransactionFrom": "string",
            "TransactionFromLatLng": "string",
            "TransactionId": "string",
            "TransactionTo": "string",
            "TransactionToLatLng": "string",
            "TransferredAsset": "string"
            }
        }
        return json.dumps(message_values)

    def test_handleGeneric_emptyMessage_NotJsonError(self):
        res = self.handler.handle_generic('')
        self.assertEqual(self.handler.MSG_NOT_JSON, res)

    def test_handleGeneric_noneMessage_NotJsonError(self):
        res = self.handler.handle_generic(None)
        self.assertEqual(self.handler.MSG_NOT_JSON, res)

    def test_handleGeneric_emptyJson1_NoTypeFieldError(self):
        message = json.dumps({})
        res = self.handler.handle_generic(message)
        self.assertEqual(self.handler.MSG_NO_TYPE, res)

    def test_handleGeneric_emptyJson2_NoTypeFieldError(self):
        message = json.dumps('')
        res = self.handler.handle_generic(message)
        self.assertEqual(self.handler.MSG_NO_TYPE, res)

    def test_handleGeneric_missingTypeJson_NoTypeFieldError(self):
        message = json.dumps({'test': 'content'})
        res = self.handler.handle_generic(message)
        self.assertEqual(self.handler.MSG_NO_TYPE, res)

    def test_handleGeneric_randomType1_NotProcessed(self):
        message = json.dumps({'type': ''})
        res = self.handler.handle_generic(message)
        self.assertEqual(self.handler.MSG_NOT_PROCESSED, res)

    def test_handleGeneric_randomType2_NotProcessed(self):
        message = json.dumps({'type': 'test'})
        res = self.handler.handle_generic(message)
        self.assertEqual(self.handler.MSG_NOT_PROCESSED, res)

    def test_handleGeneric_randomTypeWithOtherField_NotProcessed(self):
        message = json.dumps({'type': 'test', 'other': 'content'})
        res = self.handler.handle_generic(message)
        self.assertEqual(self.handler.MSG_NOT_PROCESSED, res)

    def test_handleGeneric_correctTraceContent_ProcessedResult(self):
        res = self.handler.handle_generic(self._get_valid_message())
        self.assertEqual(self.handler.MSG_TRACE_PROCESSED, res)

    def test_handleGeneric_correctTraceContent_AddedToRepo(self):
        msg = self._get_valid_message()
        _ = self.handler.handle_generic(msg)

        self.assertEqual(self.repo.added_transactions[-1].to_serializable_dict()["properties"]["RIds"], "string")

    def test_handleGeneric_correctTraceContent_NotificationSentCorrectly(self):
        # TODO message queue should contain new message with datapoint as content
        msg = self._get_valid_message()
        _ = self.handler.handle_generic(msg)

        self.assertEqual('datahub', self.msg_sender.last_message['ex'])
        self.assertEqual('semantic-linking', self.msg_sender.last_message['key'])
        self.assertEqual('new-trace', json.loads(self.msg_sender.last_message['msg'])["type"])

    def test_handleBlockchainTransaction_duplicateTrace_oneTransAddedToDuplicateRepo(self):
        msg = self._get_valid_message()
        msg2 = self._get_valid_message()
        msg = eval(msg)
        msg2 = eval(msg2)
        self.handler.handle_blockchain_transaction(msg['content'])
        self.handler.handle_blockchain_transaction(msg2['content'])
        self.assertEqual(len(self.repo.added_transactions),len(self.repo.duplicated_transactions))
        self.assertEqual(len(self.repo.added_transactions),1)

    def test_handleBlockchainTransaction_duplicateTraceDifferentTable_bothTransactionsAddedAsUnique(self):
        msg = self._get_valid_message()
        msg2 = self._get_valid_message2()
        msg = eval(msg)
        msg2 = eval(msg2)
        self.handler.handle_blockchain_transaction(msg['content'])
        self.handler.handle_blockchain_transaction(msg2['content'])
        self.assertEqual(len(self.repo.added_transactions),2)

    def test_handleBlockchainTransaction_duplicateTraceDifferentUseCase_bothTransactionsAddedAsUnique(self):
        msg = self._get_valid_message()
        msg2 = self._get_valid_message3()
        msg = eval(msg)
        msg2 = eval(msg2)
        self.handler.handle_blockchain_transaction(msg['content'])
        self.handler.handle_blockchain_transaction(msg2['content'])
        self.assertEqual(len(self.repo.added_transactions),2)

    def test_handleBlockchainTransaction_multipleTransactions3SameIdDiffUseCaseTable_3AddedUnique2Duplicate(self):
        #print("Entered Test: 3Unique 2Dupli")
        msg = self._get_valid_message()
        msg2 = self._get_valid_message2()
        msg3 = self._get_valid_message3()
        msg4 = self._get_valid_message3()
        msg5 = self._get_valid_message3()
        msg = eval(msg)
        msg2 = eval(msg2)
        msg3 = eval(msg3)
        msg4 = eval(msg4)
        msg5 = eval(msg5)
        self.handler.handle_blockchain_transaction(msg['content'])
        self.handler.handle_blockchain_transaction(msg2['content'])
        self.handler.handle_blockchain_transaction(msg3['content'])
        self.handler.handle_blockchain_transaction(msg4['content'])
        self.handler.handle_blockchain_transaction(msg5['content'])
        self.assertEqual(len(self.repo.added_transactions),3)
        self.assertEqual(len(self.repo.duplicated_transactions),2)
if __name__ == '__main__':
    unittest.main()