"""
Firebase/Firestore client for Mothership
"""
import os
from typing import Optional, Dict, Any, List
from datetime import datetime
from google.cloud import firestore
from google.oauth2 import service_account
from .types import StorageFilter


class FirebaseClient:
    """Singleton Firebase/Firestore client"""

    _instance: Optional["FirebaseClient"] = None
    _initialized: bool = False

    def __init__(self):
        """Private constructor"""
        if FirebaseClient._initialized:
            raise RuntimeError("FirebaseClient already initialized. Use getInstance()")

        self.db: Optional[firestore.Client] = None
        self._project_id: Optional[str] = None

    @classmethod
    def initialize(cls, project_id: Optional[str] = None, credentials_path: Optional[str] = None) -> "FirebaseClient":
        """
        Initialize Firebase client (call once at startup)

        Args:
            project_id: Firebase project ID (defaults to FIREBASE_PROJECT_ID env)
            credentials_path: Path to service account JSON (defaults to GOOGLE_APPLICATION_CREDENTIALS env)

        Returns:
            FirebaseClient instance

        Raises:
            ValueError: If project_id not provided and not in environment
        """
        if cls._initialized:
            return cls._instance

        instance = cls.__new__(cls)
        cls._instance = instance
        cls._initialized = True

        # Get project ID
        instance._project_id = project_id or os.getenv("FIREBASE_PROJECT_ID")
        if not instance._project_id:
            raise ValueError("Firebase project ID not provided")

        # Initialize Firestore client
        try:
            # Try using service account credentials if provided
            creds_path = credentials_path or os.getenv("GOOGLE_APPLICATION_CREDENTIALS")
            if creds_path and os.path.exists(creds_path):
                credentials = service_account.Credentials.from_service_account_file(creds_path)
                instance.db = firestore.Client(project=instance._project_id, credentials=credentials)
            else:
                # Try using environment variables for service account
                client_email = os.getenv("FIREBASE_CLIENT_EMAIL")
                private_key = os.getenv("FIREBASE_PRIVATE_KEY")

                if client_email and private_key:
                    # Replace escaped newlines with actual newlines
                    private_key = private_key.replace("\\n", "\n")

                    credentials = service_account.Credentials.from_service_account_info({
                        "type": "service_account",
                        "project_id": instance._project_id,
                        "private_key": private_key,
                        "client_email": client_email,
                        "token_uri": "https://oauth2.googleapis.com/token",
                    })
                    instance.db = firestore.Client(project=instance._project_id, credentials=credentials)
                else:
                    # Use default credentials (works in GCP environments)
                    instance.db = firestore.Client(project=instance._project_id)

        except Exception as e:
            cls._initialized = False
            cls._instance = None
            raise RuntimeError(f"Failed to initialize Firebase: {e}")

        return instance

    @classmethod
    def get_instance(cls) -> "FirebaseClient":
        """
        Get Firebase client instance

        Returns:
            FirebaseClient instance

        Raises:
            RuntimeError: If not initialized
        """
        if not cls._initialized or cls._instance is None:
            raise RuntimeError("FirebaseClient not initialized. Call initialize() first")
        return cls._instance

    @classmethod
    def is_initialized(cls) -> bool:
        """Check if Firebase client is initialized"""
        return cls._initialized

    def get_database(self) -> firestore.Client:
        """Get raw Firestore client"""
        if self.db is None:
            raise RuntimeError("Firebase not initialized")
        return self.db

    async def save_user(self, user_id: int, data: Dict[str, Any]) -> None:
        """
        Save/update user data

        Args:
            user_id: Telegram user ID
            data: User data to save
        """
        if self.db is None:
            raise RuntimeError("Firebase not initialized")

        user_ref = self.db.collection("users").document(str(user_id))
        user_data = {
            **data,
            "updatedAt": firestore.SERVER_TIMESTAMP
        }

        # Add createdAt if document doesn't exist
        doc = user_ref.get()
        if not doc.exists:
            user_data["createdAt"] = firestore.SERVER_TIMESTAMP

        user_ref.set(user_data, merge=True)

    async def get_user(self, user_id: int) -> Optional[Dict[str, Any]]:
        """
        Get user data

        Args:
            user_id: Telegram user ID

        Returns:
            User data or None if not found
        """
        if self.db is None:
            raise RuntimeError("Firebase not initialized")

        user_ref = self.db.collection("users").document(str(user_id))
        doc = user_ref.get()

        if doc.exists:
            return doc.to_dict()
        return None

    async def save_bot_data(self, bot_name: str, key: str, data: Dict[str, Any]) -> None:
        """
        Save/update bot data

        Args:
            bot_name: Name of the bot
            key: Data key
            data: Data to save
        """
        if self.db is None:
            raise RuntimeError("Firebase not initialized")

        doc_ref = self.db.collection("bots").document(bot_name).collection("data").document(key)
        doc_data = {
            **data,
            "updatedAt": firestore.SERVER_TIMESTAMP
        }

        # Add createdAt if document doesn't exist
        doc = doc_ref.get()
        if not doc.exists:
            doc_data["createdAt"] = firestore.SERVER_TIMESTAMP

        doc_ref.set(doc_data, merge=True)

    async def get_bot_data(self, bot_name: str, key: str) -> Optional[Dict[str, Any]]:
        """
        Get bot data

        Args:
            bot_name: Name of the bot
            key: Data key

        Returns:
            Data or None if not found
        """
        if self.db is None:
            raise RuntimeError("Firebase not initialized")

        doc_ref = self.db.collection("bots").document(bot_name).collection("data").document(key)
        doc = doc_ref.get()

        if doc.exists:
            return doc.to_dict()
        return None

    async def delete_bot_data(self, bot_name: str, key: str) -> None:
        """
        Delete bot data

        Args:
            bot_name: Name of the bot
            key: Data key
        """
        if self.db is None:
            raise RuntimeError("Firebase not initialized")

        doc_ref = self.db.collection("bots").document(bot_name).collection("data").document(key)
        doc_ref.delete()

    async def delete_all_bot_data(self, bot_name: str) -> None:
        """
        Delete all bot data

        Args:
            bot_name: Name of the bot
        """
        if self.db is None:
            raise RuntimeError("Firebase not initialized")

        collection_ref = self.db.collection("bots").document(bot_name).collection("data")

        # Delete in batches
        batch_size = 100
        docs = collection_ref.limit(batch_size).stream()
        deleted = 0

        for doc in docs:
            doc.reference.delete()
            deleted += 1

        if deleted >= batch_size:
            # Recursive call to delete remaining documents
            await self.delete_all_bot_data(bot_name)

    async def query_bot_data(self, bot_name: str, filters: List[StorageFilter]) -> List[Dict[str, Any]]:
        """
        Query bot data with filters

        Args:
            bot_name: Name of the bot
            filters: List of query filters

        Returns:
            List of matching documents
        """
        if self.db is None:
            raise RuntimeError("Firebase not initialized")

        collection_ref = self.db.collection("bots").document(bot_name).collection("data")
        query = collection_ref

        # Apply filters
        for f in filters:
            field = f["field"]
            operator = f["operator"]
            value = f["value"]

            # Map operators to Firestore operators
            if operator == "==":
                query = query.where(field, "==", value)
            elif operator == "!=":
                query = query.where(field, "!=", value)
            elif operator == ">":
                query = query.where(field, ">", value)
            elif operator == ">=":
                query = query.where(field, ">=", value)
            elif operator == "<":
                query = query.where(field, "<", value)
            elif operator == "<=":
                query = query.where(field, "<=", value)
            elif operator == "array-contains":
                query = query.where(field, "array_contains", value)
            elif operator == "in":
                query = query.where(field, "in", value)
            elif operator == "array-contains-any":
                query = query.where(field, "array_contains_any", value)

        # Execute query
        docs = query.stream()
        return [doc.to_dict() for doc in docs]

    async def get_user_bot_data(self, bot_name: str, user_id: int) -> List[Dict[str, Any]]:
        """
        Get all bot data for a specific user

        Args:
            bot_name: Name of the bot
            user_id: Telegram user ID

        Returns:
            List of user's documents
        """
        return await self.query_bot_data(bot_name, [
            {"field": "userId", "operator": "==", "value": user_id}
        ])
