# global imports (dont't worry, red is normal)
import network_constants as netconst
from database.MongoRepositoryBase import MongoRepositoryBase
from database.entities.transaction import Transaction

import pymongo
import json
import time
from typing import List, Dict

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

    def __init__(self):
        super().__init__(netconst.TRACE_RETRIEVAL_DB_HOSTNAME,
                         netconst.TRACE_RETRIEVAL_DB_PORT,
                         'traceRetrievalDb')

        self._transaction_collection = 'transactions'
        self._failed_transaction_collection = 'transactions_failed'
        self._duplicated_transaction_collection = "transactions_duplicated"
    
    def delete_all_transactions(self):
        collection = self._database[self._transaction_collection]
        collection.delete_many({})

    def add_transaction(self, transaction: Transaction):
        '''
        Adds a transaction to mongodb repository.

        @throws
        KeyError - Duplicate transaction ID
        '''
        use_case= transaction.use_case
        table = transaction.table
        reference = self.get_transaction_with_id(transaction.id(),use_case, table)
        if reference == None:
            super().insert_entry(self._transaction_collection, transaction.to_serializable_dict())
        else:
            raise KeyError(f"ID {transaction['UniqueID']} already exists")
    
    def all_transactions_for_use_case(self, use_case: str) -> List[Transaction]:
        result = super().get_entries(self._transaction_collection, projection={'_id': False}, selection={"use_case": use_case})
        return [Transaction.from_serializable_dict(row) for row in list(result)]
    
    def get_transaction_with_id(self, unique_id: str, use_case:str, table:str ) -> Transaction:
        result = list(super().get_entries(self._transaction_collection, projection={'_id': False}, selection={"id": unique_id,"use_case": use_case, "table":table}))

        if len(result) >= 1:
            return Transaction.from_serializable_dict(result[0])
        
        return None
    
    def add_failed_transaction(self, transaction: Dict):
        transaction["timestamp"] = time.time()
        super().insert_entry(self._failed_transaction_collection, transaction)
    
    def all_failed_transactions_for_use_case(self, use_case: str) -> List[Dict]:
        result = super().get_entries(
            self._failed_transaction_collection, 
            projection={'_id': False}, 
            selection={"ApplicationType": use_case}
        ).sort("timestamp", 1)

        return list(result)
    
    def delete_all_failed_transactions(self, use_case:str):
        collection = self._database[self._failed_transaction_collection]
        collection.delete_many({"ApplicationType": use_case})

    def add_duplicated_transaction(self, transaction: Transaction):
        #transaction["timestamp"] = time.time()
        super().insert_entry(self._duplicated_transaction_collection, transaction.to_serializable_dict())
    
    def all_duplicated_transactions_for_use_case(self, use_case: str) -> List[Dict]:
        result = super().get_entries(self._duplicated_transaction_collection, projection={'_id': False}, selection={"use_case": use_case})
        return [Transaction.from_serializable_dict(row) for row in list(result)]
    
    def delete_all_duplicated_transactions(self, use_case:str):
        collection = self._database[self._duplicated_transaction_collection]
        collection.delete_many({"ApplicationType": use_case})