from pymongo import MongoClient
from pymongo.collection import Collection
from pymongo.database import Database

from app.config import settings
from app.log import get_logger

log = get_logger(__name__)
db_client: MongoClient = None # type: ignore  # noqa: PGH003


class MongoDBSingleton:
    _instance = None
    _client: MongoClient = None # type: ignore

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            connection_string = settings.MONGODB_URI
            cls._instance._client = MongoClient(connection_string)
            log.info("Connected to MongoDB")
        return cls._instance

    def get_main_db(self) -> Database:
        db_name = settings.MONGODB_DB
        return self._client[db_name]

    def get_collection(self, collection_name: str) -> Collection:
        """Get a collection from the main database."""
        db = self.get_main_db()
        if collection_name not in db.list_collection_names():
            db.create_collection(collection_name)
        return db[collection_name]

    def get_existing_document(self, collection: Collection, identifier) -> dict:
        """
        Retrieves an existing document from a MongoDB collection.

        Args:
            collection (Collection): The MongoDB collection.
            identifier: The identifier of the document.

        Returns:
            dict: The existing document if found, else None.
        """
        return collection.find_one({"_id": identifier}) # type: ignore

    def is_data_in_db(self, collection_name: str, identifier: int) -> bool:
        """
        Checks if data exists in a MongoDB collection.

        Args:
            collection_name (str): The name of the collection.
            identifier: The identifier of the document.

        Returns:
            bool: True if data exists, False otherwise.
        """
        if not isinstance(identifier, (int, str)):  # Check if identifier is int or str
            raise TypeError("Identifier must be an integer or string.")

        collection = self.get_collection(collection_name)
        return collection.find_one({"_id": identifier}) is not None
    
        
    async def insert_or_update_document(self, collection: Collection, identifier, data):
        existing_document = self.get_existing_document(collection, identifier)
        if existing_document:
            collection.update_one({"_id": identifier}, {"$set": data})
        else:
            new_document = {"_id": identifier, **data}
            collection.insert_one(new_document)
