import uuid

import firebase_admin
from firebase_admin import credentials, firestore

from .utils.logger import logger

class FirestoreCollection:
    """Class for performing CRUD operations with Firestore collections.

    This class provides methods to create, read, update and delete documents
    in a Firestore collection while maintaining a unique identifier for each document.
    """

    def __init__(self, collection_name: str, unique_key: str, service_account_path: str):
        """
        Initializes the collection reference and Firebase app.

        Args:
            collection_name (str): The name of the Firestore collection.
            unique_key (str): The unique key field within the collection.
            service_account_path (str): Path to the service account JSON key.
        """
        self.collection_name = collection_name
        self.unique_key = unique_key

        # Initialize Firebase
        cred = credentials.Certificate(service_account_path)
        firebase_admin.initialize_app(cred)

        # Firestore synchronous client
        self.collection_ref = firestore.client().collection(collection_name)

    def add_document(self, data: dict) -> str:
        """Add a new document to the Firestore collection.

        Args:
            data (dict): Dictionary containing the document data.

        Returns:
            str: The unique identifier of the created document.

        Raises:
            ValueError: If the data already contains the unique key field.
            Exception: If there's an error while adding the document to Firestore.
        """
        if self.unique_key in data:
            raise ValueError(f"The data already contains the key '{self.unique_key}'")

        unique_id = str(uuid.uuid4())
        data[self.unique_key] = unique_id

        try:
            doc_ref = self.collection_ref.document(unique_id)
            doc_ref.set(data)
            logger.info(f"Document added with ID: {unique_id}")
            return unique_id
        except Exception as e:
            logger.error(f"Error adding document: {str(e)}")
            raise

    def get_documents(self) -> list[dict]:
        """Retrieve all documents from the Firestore collection.

        Returns:
            list[dict]: A list of dictionaries containing all documents' data.
            Returns an empty list if an error occurs.
        """
        try:
            docs = self.collection_ref.get()
            logger.info(f"Retrieved {len(docs)} documents from {self.collection_name}")
            return [doc.to_dict() for doc in docs]
        except Exception as e:
            logger.error(f"Error retrieving documents: {str(e)}")
            return []

    def get_document_by_id(self, document_id: str) -> dict | None:
        """Retrieve a specific document by its ID.

        Args:
            document_id (str): The unique identifier of the document.

        Returns:
            dict | None: The document data as a dictionary if found,
            None if the document doesn't exist or if an error occurs.
        """
        try:
            doc_ref = self.collection_ref.document(document_id)
            doc_snap = doc_ref.get()
            if doc_snap.exists:
                logger.info(f"Document with ID {document_id} retrieved")
                return doc_snap.to_dict()
            else:
                logger.warning(f"Document with ID '{document_id}' does not exist.")
                return None
        except Exception as e:
            logger.error(f"Error retrieving document: {str(e)}")
            return None

    def update_document_by_id(self, document_id: str, data: dict) -> None:
        """Update a specific document by its ID.

        Args:
            document_id (str): The unique identifier of the document to update.
            data (dict): Dictionary containing the fields to update.

        Raises:
            Exception: If there's an error while updating the document.
        """
        try:
            doc_ref = self.collection_ref.document(document_id)
            doc_ref.update(data)
            logger.info(f"Document with ID {document_id} updated")
        except Exception as e:
            logger.error(f"Error updating document with ID {document_id}: {str(e)}")
            raise

    def delete_document_by_id(self, document_id: str) -> None:
        """Delete a specific document by its ID.

        Args:
            document_id (str): The unique identifier of the document to delete.

        Raises:
            Exception: If there's an error while deleting the document.
        """
        try:
            doc_ref = self.collection_ref.document(document_id)
            doc_ref.delete()
            logger.info(f"Document with ID {document_id} deleted")
        except Exception as e:
            logger.error(f"Error deleting document with ID {document_id}: {str(e)}")
            raise
