import network_constants as netconst
from database.MongoRepositoryBase import MongoRepositoryBase
from db.entities import Layer

import pymongo
import json
from typing import List, Dict

# init logging to file
import logging
LOG_FORMAT = ('%(levelname) -5s %(asctime)s %(name)s:%(funcName) -35s %(lineno) -5d:  %(message)s')
logging.basicConfig(level=logging.INFO, format=LOG_FORMAT)
LOGGER = logging.getLogger(__name__)

class Repository(MongoRepositoryBase):
    '''This is a repository for MongoDb.'''

    def __init__(self):
        super().__init__(netconst.SEMANTIC_LINKING_DB_HOSTNAME,
                         netconst.SEMANTIC_LINKING_DB_PORT,
                         'semanticLinkingDb')
        self._layer_collection = 'layers'
        self._layer_nodes_collection = 'layer_nodes'


    # region Layers
    def add_layer(self, layer: Layer):
        super().insert_entry(self._layer_collection, layer.to_serializable_dict(for_db=True))

    def get_layers(self) -> List[Layer]:
        entries = super().get_entries(self._layer_collection)
        return [Layer(e) for e in entries]
    
    def get_layers_for_use_case_and_table(self, use_case: str, table: str) -> List[Layer]:
        result = super().get_entries(self._layer_collection, projection={'_id': False}, selection={"use_case": use_case, "table": table})
        return [Layer(e) for e in result]

    def get_layer(self, layer_name) -> Layer:
        entries = super().get_entries(self._layer_collection, selection={'layer_name': layer_name})
        entries = [Layer(e) for e in entries]
        if entries is not None and len(entries) > 0:
            return entries[0]
        else:
            return None
    
    def get_layers_by_use_case(self, use_case: str) -> List[Layer]:
        entries = super().get_entries(self._layer_collection, selection={'use_case': use_case})
        return [Layer(e) for e in entries]

    def get_layers_use_case_and_table(self, use_case: str, table: str) -> Layer:
        return [Layer(e) for e in list(super().get_entries(self._layer_collection, selection={'use_case': use_case, 'table': table}))]

    def get_layers_by_name_and_use_case_and_table(self, layer_name: str, use_case: str, table: str) -> Layer:
        entries = super().get_entries(self._layer_collection, selection={'layer_name': layer_name, 'use_case': use_case, 'table': table})
        entries = list(entries) # materialize
        if len(entries) > 1:
            LOGGER.error(f"Use case + layer name was not unique for {use_case} and {layer_name}.")
            return None
        return Layer(entries[0]) if len(entries) == 1 else None
    
    def delete_layer(self, layer:Layer):
        collection = self._database[self._layer_collection]
        collection.delete_one({"layer_name": layer.layer_name, "use_case": layer.use_case, "table": layer.table})
    
    def delete_layers_for_use_case(self, use_case: str):
        collection = self._database[self._layer_collection]
        collection.delete_many({"use_case": use_case})

    def add_layer_node(self, node: dict):
        super().insert_entry(self._layer_nodes_collection, node)

    def add_layer_nodes(self, nodes: List[dict]):
        super().insert_many(self._layer_nodes_collection, nodes)

    def get_layer_nodes(self, layer_name: str) -> dict:
        '''Returns all nodes for the layer.'''
        entries = super().get_entries(self._layer_nodes_collection, selection={'layer_name': layer_name},
                                      projection={'_id': 0})
        return [e for e in entries]
    
    def get_nodes_for_use_case(self, use_case: str) -> List[Dict]:
        entries = super().get_entries(self._layer_nodes_collection, selection={'use_case': use_case},
                                      projection={'_id': 0})
        return list(entries)
    
    def get_nodes_for_use_case_and_table(self, use_case: str, table: str) -> List[Dict]:
        '''Returns all nodes for the layer.'''
        entries = super().get_entries(self._layer_nodes_collection, selection={'use_case': use_case, 'table': table},
                                      projection={'_id': 0})
        return list(entries)
    
    def get_nodes_for_use_case_and_table_and_layer(self, layer_name: str, use_case: str, table: str) -> List[Dict]:
        '''Returns all nodes for the layer.'''
        entries = super().get_entries(self._layer_nodes_collection, selection={'layer_name': layer_name, 'use_case': use_case, 'table': table},
                                      projection={'_id': 0})
        return list(entries)

    # endregion
