from authentication.token_manager import TokenManager
import network_constants
from database.entities.transaction import Transaction
from database.repository import Repository

import json
import hashlib
import logging
import requests

from typing import Dict

LOGGER = logging.getLogger(__name__)

class MessageHandler:
    MSG_NOT_JSON = "Message is not in JSON format and is ignored"
    MSG_NO_TYPE = "Message has no type field and is ignored"
    MSG_NOT_PROCESSED = "Message Type could not be processed"
    MSG_TRACE_PROCESSED = "Message handled as blockchain-transaction"

    _mongo_repo = None
    _message_sender = None

    _repository = Repository()

    def __init__(self, mongo_repo, message_sender):
        self._mongo_repo = mongo_repo
        self._message_sender = message_sender
        self._message_sender.create_message_destination('datahub', 'direct')

    def handle_generic(self, body):
        LOGGER.info(f"Received message: {body}")
        result = None

        message = None
        try:
            message = json.loads(body)
        except (ValueError, TypeError):
            result = self.MSG_NOT_JSON
            LOGGER.warning(result)
            return result

        if not 'type' in message:
            result = self.MSG_NO_TYPE
            LOGGER.warning(result)
            return result
        
        if message['type'] == 'blockchain-transaction':
            self.handle_blockchain_transaction(message['content'])
            result = self.MSG_TRACE_PROCESSED
        else:
            result = self.MSG_NOT_PROCESSED

        LOGGER.info(result)
        return result
    
    def _resolve_path(self, data: Dict, path:str) -> Dict:
        '''
        resolves a path without concatenation in a json dictionary

        @params
        data - Required: Dictionary that is the decoded json string
        path - Required: path of multiple keys seperated by "//" and list indizes "[5]"
        '''
        path_pieces = path.split("//")
        value = data

        # resolve all pieces of the path in order
        for i in range(0,len(path_pieces)):
            piece = path_pieces[i]

            # is the current path piece in the form attribute[index]?
            if piece[-1] == "]":
                start = piece.index("[")

                # stem ... attribute name
                # index ... list index
                stem = piece[:start]
                index = int(piece[start+1:-1])

                value = value[stem][index]
            else:
                value = value[piece]
        return value

    def handle_blockchain_transaction(self, transaction):
        jwt_token = TokenManager.getInstance().getToken()

        transaction_data = json.loads(transaction)

        use_case = transaction_data["content"]["ApplicationType"]

        # query schema information
        url = f'https://{network_constants.BUSINESS_LOGIC_HOSTNAME}:{network_constants.BUSINESS_LOGIC_REST_PORT}/api/use-cases/{use_case}/schema'

        print(f"CALLING: {url}")

        response = requests.get(
                url,
                verify = False, 
                proxies = { "http":None, "https":None },
                headers = { "Authorization": f"Bearer {jwt_token}"}
        )

        print(f"RESPONSE: {response.text}")

        data = json.loads(response.text)

        mappings = data["mappings"]
        flattened = {}

        # iterate over schema mappings and resolve paths
        for mapping in mappings.keys():
            full_path = mappings[mapping]

            concat_paths = full_path.split("+")
            values = []

            for path in concat_paths:
                values.append(
                    self._resolve_path(transaction_data["content"], path)
                )

            if len(values) > 1:
                final_value = "".join(values)
            else:
                final_value = values[0]

            flattened[mapping] = final_value
        
        flattened["UniqueID"] = hashlib.sha256(flattened["UniqueID"].encode("utf-8")).hexdigest()

        transaction = Transaction(use_case, flattened)
        MessageHandler._repository.add_transaction(transaction)
        
        # inform semantic linking microservice
        self._message_sender.send_message('datahub', 'semantic-linking', json.dumps(transaction.to_serializable_dict()))