from typing import List, Tuple
from google.cloud import firestore
from google.auth import credentials
from google.cloud.firestore import DocumentReference

from app import settings


class ORDER_DIR:
    ASC = firestore.Query.ASCENDING
    DESC = firestore.Query.DESCENDING


class Collection:
    __default_order_by__ = None
    __default_order_dir__ = ORDER_DIR.DESC

    def __init__(self, collection_name: str, db: firestore.Client = None) -> None:
        self.init(collection_name, db)

    def init(self, collection_name: str, db: firestore.Client = None) -> None:
        if db is None:
            if settings.APP_ENV == 'production':
                # Note: in production client config id is inferred from env credentials
                db = firestore.Client()
            else:
                anon_credentials = credentials.AnonymousCredentials()
                db = firestore.Client(project=settings.FIRESTORE_PROJECT_ID, credentials=anon_credentials)
        self.collection = db.collection(collection_name)

    def create(self, **doc) -> dict:
        """Add a new document to the collection. If no "id" argument is given
        a new id will be auto generated. Returns the newly created document.
        """
        doc_ref: DocumentReference = self.collection.document(doc.get('id'))
        doc['id'] = doc_ref.id
        doc_ref.set(doc)
        return doc
 
    def update(self, id: str, **doc) -> dict: 
        """Update document with given id."""
        doc_ref: DocumentReference = self.collection.document(id)
        doc_ref.update(doc)
        return doc

    def get(self, id: str) -> dict: 
        """Return document in the collection with given id, or None."""
        doc_ref: DocumentReference = self.collection.document(id)
        return doc_ref.get().to_dict()

    def all(self, 
            filters: List[Tuple] = [], 
            order_by: str = None, 
            order_dir: str = None
        ) -> List[dict]: 
        """Return all documents in collection, filtered by given filters."""
        if order_by is None: order_by = self.__default_order_by__
        if order_dir is None: order_dir = self.__default_order_dir__

        query = self.collection.select(['id', 'title', 'tags']).order_by(order_by, direction=order_dir)
        for filter in filters:
            if (isinstance(filter, tuple) or isinstance(filter, list)) and len(filter) == 3:
                query = query.where(*filter)
        docs = (query.stream() if query 
                    else self.collection.stream())
        return [doc.to_dict() for doc in docs]

    def delete(self, id: str) -> bool: 
        """Delete document in collection with given id."""
        doc_ref: DocumentReference = self.collection.document(id)
        if not doc_ref.get().exists:
            return False
        doc_ref.delete()
        return True



from typing import List, Tuple
from google.cloud import firestore
from google.auth import credentials
from google.cloud.firestore import DocumentReference

from app import settings


class ORDER_DIR:
    ASC = firestore.Query.ASCENDING
    DESC = firestore.Query.DESCENDING


class Collection:
    __default_order_by__ = None
    __default_order_dir__ = ORDER_DIR.DESC

    def __init__(self, collection_name: str, db: firestore.Client = None) -> None:
        self.init(collection_name, db)

    def init(self, collection_name: str, db: firestore.Client = None) -> None:
        if db is None:
            if settings.APP_ENV == 'production':
                # Note: in production client config id is inferred from env credentials
                db = firestore.Client()
            else:
                anon_credentials = credentials.AnonymousCredentials()
                db = firestore.Client(project=settings.FIRESTORE_PROJECT_ID, credentials=anon_credentials)
        self.collection = db.collection(collection_name)

    def create(self, **doc) -> dict:
        """Add a new document to the collection. If no "id" argument is given
        a new id will be auto generated. Returns the newly created document.
        """
        doc_ref: DocumentReference = self.collection.document(doc.get('id'))
        doc['id'] = doc_ref.id
        doc_ref.set(doc)
        return doc
 
    def update(self, id: str, **doc) -> dict: 
        """Update document with given id."""
        doc_ref: DocumentReference = self.collection.document(id)
        doc_ref.update(doc)
        return doc

    def get(self, id: str) -> dict: 
        """Return document in the collection with given id, or None."""
        doc_ref: DocumentReference = self.collection.document(id)
        return doc_ref.get().to_dict()

    def all(self, 
            filters: List[Tuple] = [], 
            order_by: str = None, 
            order_dir: str = None
        ) -> List[dict]: 
        """Return all documents in collection, filtered by given filters."""
        if order_by is None: order_by = self.__default_order_by__
        if order_dir is None: order_dir = self.__default_order_dir__

        query = self.collection.select(['id', 'title', 'tags']).order_by(order_by, direction=order_dir)
        for filter in filters:
            if (isinstance(filter, tuple) or isinstance(filter, list)) and len(filter) == 3:
                query = query.where(*filter)
        docs = (query.stream() if query 
                    else self.collection.stream())
        return [doc.to_dict() for doc in docs]

    def delete(self, id: str) -> bool: 
        """Delete document in collection with given id."""
        doc_ref: DocumentReference = self.collection.document(id)
        if not doc_ref.get().exists:
            return False
        doc_ref.delete()
        return True



# Note: in production client config id is inferred from env credentials Add a new document to the collection. If no "id" argument is given         a new id will be auto generated. Returns the newly created document. Update document with given id. Return document in the collection with given id, or None. Return all documents in collection, filtered by given filters. Delete document in collection with given id.