from security.token_manager import TokenManager
import network_constants
from db.entities.layer import Layer
from db.repository import Repository

from typing import List, Dict
import requests
import json


def _fetch_use_cases() -> List[str]:
    jwt = TokenManager.getInstance().getToken()

    url = f'https://{network_constants.BUSINESS_LOGIC_HOSTNAME}:{network_constants.BUSINESS_LOGIC_REST_PORT}/api/use-cases'

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

    if response.status_code != 200:
        raise ConnectionError(f"Could not fetch use-cases from business-logic microservice, statuscode: {response.status_code}!")

    data = json.loads(response.text)

    return [row["name"] for row in data]


def _fetch_tables(use_case: str) -> List[str]:
    jwt = TokenManager.getInstance().getToken()

    url = f'https://{network_constants.BUSINESS_LOGIC_HOSTNAME}:{network_constants.BUSINESS_LOGIC_REST_PORT}/api/use-cases/{use_case}/tables'

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

    if response.status_code != 200:
        raise ConnectionError(f"Could not fetch tables for {use_case} from business-logic microservice, statuscode: {response.status_code}!")

    data = json.loads(response.text)

    return [row["name"] for row in data]


def _fetch_layers(use_case: str, table: str) -> List[Layer]:
    jwt = TokenManager.getInstance().getToken()

    url = f'https://{network_constants.BUSINESS_LOGIC_HOSTNAME}:{network_constants.BUSINESS_LOGIC_REST_PORT}/api/use-cases/{use_case}/tables/{table}/layers'

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

    if response.status_code != 200:
        raise ConnectionError(f"Could not fetch layers for {use_case}//{table} from business-logic microservice, statuscode: {response.status_code}!")

    data = json.loads(response.text)
    
    return [Layer.from_business_logic_dict(row) for row in data]


def _fetch_nodes(use_case: str, table: str, layer_name: str) -> List[Dict]:
    jwt = TokenManager.getInstance().getToken()

    url = f'https://{network_constants.SEMANTIC_LINKING_HOSTNAME}:{network_constants.SEMANTIC_LINKING_REST_PORT}/api/use-cases/{use_case}/tables/{table}/layers/{layer_name}/nodes'

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

    if response.status_code != 200:
        raise ConnectionError(f"Could not fetch nodes for {use_case}//{table}//{layer_name} from semantic-linking microservice, statuscode: {response.status_code}!")

    return response.json()


def fetch_nodes_from_semantic_linking(selected_use_cases: List[str] = None, selected_use_case_tables: List[str] = None):
    '''Empties the db and inserts layers and nodes from BusinessLogic and SemanticLinking'''
    repository = Repository()
    # please dont delete all layers/ nodes anymore @10.11.2020
    # repository.delete_all_layers()
    # repository.delete_all_nodes()

    use_cases = _fetch_use_cases()
    for use_case in use_cases:
        if selected_use_cases is not None and use_case not in selected_use_cases:
            continue

        print(f"Fetching for use-case {use_case}")

        tables = _fetch_tables(use_case)
        for table in tables:
            if selected_use_case_tables is not None and table not in selected_use_case_tables:
                continue

            layers = _fetch_layers(use_case, table)
            for layer in layers:

                try:
                    print(f"Fetching nodes for layer {use_case}//{table}//{layer.layer_name}.")

                    # check if layer already exists in DB, add it if not
                    reference_layer = repository.get_layer_by_name(use_case, table, layer.layer_name)
                    if reference_layer == None:
                        repository.add_layer(layer)
                    else:
                        raise Exception(f"Layer should be unique, but was not: {reference_layer}")

                    nodes = _fetch_nodes(use_case, table, layer.layer_name)
                    for node in nodes:
                        node['use_case_table'] = node['table']
                        del node['table']

                    for node in nodes:
                        repository.add_layer_node(node)
                        
                except ConnectionError as e:
                    print(str(e))
                    continue
            