import pymongo
import network_constants as netconst
from database.MongoRepositoryBase import MongoRepositoryBase
import json

from db.entities import *
# from processing.similarityFiles.miscFunctions import *
from typing import List


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

    def __init__(self):
        super().__init__(netconst.ROLESTAGE_DISCOVERY_DB_HOSTNAME,
                         netconst.ROLESTAGE_DISCOVERY_DB_PORT,
                         'roleStageDb')

        self._layer_collection = 'layers'
        self._layer_nodes_collection = 'layer_nodes'
        self._clusters_collection = 'clusters'
        self._time_slice_collection = 'time_slices'
        self._connected_clusters_collection ='connected_clusters'
        self._similarity_collection = 'similarity'
        self._connected_run = 'connected_run'

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

    def get_layers(self) -> List[Layer]:
        entries = super().get_entries(self._layer_collection)
        return [Layer(e) for e in entries]

    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 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]

#endregion

#region Clusters
    def add_clusters(self, clusters: List[Cluster]):
        cluster_dicts = [c.to_serializable_dict(for_db=True) for c in clusters]
        super().insert_many(self._clusters_collection, cluster_dicts)
        
    def get_clusters_for_layer(self, layer_name: str) -> List[Cluster]:
        entries = super().get_entries(self._clusters_collection, selection={'layer_name': layer_name}, projection={'_id': 0})
        return [Cluster(cluster_dict=e, from_db=True) for e in entries]
    
#endregion

#region TimeSlice
    def add_time_slice(self, timeslice: TimeSlice):
        super().insert_entry(self._time_slice_collection, timeslice.to_serializable_dict(for_db=True))

    def get_time_slices(self) -> List[TimeSlice]:
        '''Returns all time slices.'''
        entries = super().get_entries(self._time_slice_collection)
        return [TimeSlice(None, None, time_slice_dict=e, from_db=True) for e in entries]

    def get_time_slices_by_name(self, layer_name) -> List[TimeSlice]:
        '''Returns all time slices with the given layer_name.'''
        entries = super().get_entries(self._time_slice_collection, selection={'layer_name': layer_name})
        return [TimeSlice(None, None, time_slice_dict=e, from_db=True) for e in entries]

    def remove_all_time_slices(self):
        super().drop_collection(self._time_slice_collection)

#endregion

#region clusterConnected
    def add_connected_clusters(self, clusterDictArray):
        ''' Add Connected Clusters Data to DB '''
        result = super().insert_many(self._connected_clusters_collection, clusterDictArray)
        return result
        
    def get_connected_clusters(self, run_id: str=None):#, layer_name: str):
        ''' Get Connected Clusters Data from DB '''
        if (run_id == None):
            entries = super().get_entries(self._connected_clusters_collection, projection={'_id': 0})
        else:
            entries = super().get_entries(self._similarity_collection, selection={'cluster_runId' : run_id}, projection={'_id': 0})
        
        output = []
        for ent in entries:
            output.append(ent)
        return output
        # print(ent)
        #return [Cluster(cluster_dict=e, from_db=True) for e in entries]

#endregion

#region similarity
    def add_similarity(self, inputDict):
        ''' Add Similarity Data to DB '''
        #checkIfConnClustDictIsSerializable(outputJSON)      
        result = super().insert_many(self._similarity_collection, inputDict)
        #print(str(result))
        #super().insert_entry(self._connected_clusters_collection, outputJSON)
        return result
    
    def get_similarity(self,skipNr,batchSize, cluster_layer: str= None, run_id: str=None):
        ''' Get Similarity Data from DB '''
        
        if (run_id == None):
            if(cluster_layer == None):
                entries = super().get_entries(self._similarity_collection, projection={'_id': 0})
            else:
                entries = super().get_entries(self._similarity_collection, selection={'cluster_layer' : cluster_layer}, projection={'_id': 0})
        else:
            if(cluster_layer == None):
                entries = super().get_entries(self._similarity_collection, selection={'runId' : run_id}, projection={'_id': 0})
            else:
                entries = super().get_entries(self._similarity_collection, selection={'cluster_layer' : cluster_layer, 'runId' : run_id}, projection={'_id': 0})
            
        #
        return list(entries.sort([('_id', -1)]).skip(skipNr).limit(batchSize))
        """
        output = []
        for e in entries:
            output.append(e)
        return output
        """


#endregion

#region connected_run

    def convert_run_to_json_support(self, run_from_db: dict) -> dict:
        ''' Converts the ObjectId from MongoDb to its string repr. '''
        run_from_db['_id'] = str(run_from_db['_id'])
        return run_from_db

    def add_connected_run(self, conRunTimestamp):
        ''' Add Connected Run Data to DB '''
        result = super().insert_entry(self._connected_run, conRunTimestamp)
        return result

    def get_connected_run(self, run_id: str= None):
        ''' Get Connected Run Data from DB '''
        if (run_id == None):
            entries = super().get_entries(self._connected_run)
        else:
            entries = super().get_entries(self._connected_run, selection={'_id' : run_id}, projection={'_id': 1, 'Datetime': 1})

        output = []
        for e in entries:
            output.append(self.convert_run_to_json_support(e))
        return output


#endregion