import firebase_admin
from firebase_admin import credentials, firestore
import uuid

from .utils.logger import logger

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

    This class provides methods to create, read, update and delete documents
    in Firebase Firestore collections.
    """

    def __init__(self, service_account_path: str) -> None:
        """Initialize Firebase app with service account credentials.

        Args:
            service_account_path: Path to the service account JSON key file.
        """
        self.service_account_path = service_account_path
        self.initialize_firestore()

    def initialize_firestore(self) -> None:
        """Initialize Firebase and Firestore client.

        Initializes Firebase app if not already initialized and creates
        a Firestore client instance.
        """
        if not firebase_admin._apps:
            cred = credentials.Certificate(self.service_account_path)
            firebase_admin.initialize_app(cred)
            logger.info("Firebase app initialized.")
        else:
            logger.info("Firebase app already initialized.")

        # Firestore synchronous client
        self.firestore_client = firestore.client()

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

        Args:
            collection_name: Name of the collection to add document to.
            data: Dictionary containing document data.
            unique_key: Key to store the generated unique ID.

        Returns:
            str: The generated unique ID for the document.

        Raises:
            ValueError: If data already contains the unique_key.
        """
        if unique_key in data:
            raise ValueError(f"The data already contains the key '{unique_key}'")

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

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

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

        Args:
            collection_name: Name of the collection to retrieve documents from.

        Returns:
            A list of dictionaries containing document data.
        """
        self.initialize_firestore()  # Ensure Firestore is initialized
        try:
            collection_ref = self.firestore_client.collection(collection_name)
            docs = collection_ref.get()
            logger.info(f"Retrieved {len(docs)} documents from '{collection_name}'")
            return [doc.to_dict() for doc in docs]
        except Exception as e:
            logger.error(
                f"Error retrieving documents from '{collection_name}': {str(e)}"
            )
            return []

    def get_document_by_id(self, collection_name: str, document_id: str) -> dict | None:
        """Retrieve a document by ID from the specified collection.

        Args:
            collection_name: Name of the collection to retrieve document from.
            document_id: ID of the document to retrieve.

        Returns:
            Dictionary containing document data if found, None otherwise.
        """
        self.initialize_firestore()  # Ensure Firestore is initialized
        try:
            collection_ref = self.firestore_client.collection(collection_name)
            doc_ref = collection_ref.document(document_id)
            doc_snap = doc_ref.get()
            if doc_snap.exists:
                logger.info(
                    f"Document with ID {document_id} retrieved from '{collection_name}'"
                )
                return doc_snap.to_dict()
            else:
                logger.warning(
                    f"Document with ID '{document_id}' does not exist in '{collection_name}'."
                )
                return None
        except Exception as e:
            logger.error(
                f"Error retrieving document from '{collection_name}': {str(e)}"
            )
            return None

    def update_document_by_id(
        self, collection_name: str, document_id: str, data: dict
    ) -> None:
        """Update a document by ID in the specified collection.

        Args:
            collection_name: Name of the collection containing the document.
            document_id: ID of the document to update.
            data: Dictionary containing updated field values.

        Raises:
            Exception: If document update fails.
        """
        self.initialize_firestore()  # Ensure Firestore is initialized
        try:
            collection_ref = self.firestore_client.collection(collection_name)
            doc_ref = collection_ref.document(document_id)
            doc_ref.update(data)
            logger.info(
                f"Document with ID {document_id} updated in '{collection_name}'"
            )
        except Exception as e:
            logger.error(
                f"Error updating document with ID {document_id} in '{collection_name}': {str(e)}"
            )
            raise

    def delete_document_by_id(self, collection_name: str, document_id: str) -> None:
        """Delete a document by ID from the specified collection.

        Args:
            collection_name: Name of the collection containing the document.
            document_id: ID of the document to delete.

        Raises:
            Exception: If document deletion fails.
        """
        self.initialize_firestore()  # Ensure Firestore is initialized
        try:
            collection_ref = self.firestore_client.collection(collection_name)
            doc_ref = collection_ref.document(document_id)
            doc_ref.delete()
            logger.info(
                f"Document with ID {document_id} deleted from '{collection_name}'"
            )
        except Exception as e:
            logger.error(
                f"Error deleting document with ID {document_id} from '{collection_name}': {str(e)}"
            )
            raise
