from analogai.foundation.infrastructure.storage.storage_adapter import StorageAdapter
from typing import List, Dict, Any, Optional
from typeguard import typechecked
from analogai.foundation.common.logging.app_logger import app_logger
from analogai.foundation.common.design_patterns.singleton import Singleton
import atexit
import threading

class InMemoryStorageAdapterImpl(StorageAdapter, metaclass=Singleton):
    def __init__(self, db_path: Optional[str] = None):
        self._storage: Dict[str, Dict[str, Dict[str, Any]]] = {}
        self._lock = threading.Lock()
        atexit.register(self.close)

    def _get_collection(self, collection: str) -> Dict[str, Dict[str, Any]]:
        if collection not in self._storage:
            with self._lock:
                if collection not in self._storage:
                    self._storage[collection] = {}
        return self._storage[collection]

    def _match_nested(self, data: dict, path: str, value: Any) -> bool:
        parts = path.split('.')
        current = data
        for part in parts:
            if not isinstance(current, dict):
                return False
            current = current.get(part)
            if current is None:
                return False
        return current == value

    def _match_all(self, data: dict, query: Dict[str, Any]) -> bool:
        for attr_key, attr_value in query.items():
            if not self._match_nested(data, attr_key, attr_value):
                return False
        return True

    @typechecked
    def store(self, collection: str, data: Dict[str, Any]) -> Dict[str, Any]:
        collection_data = self._get_collection(collection)
        with self._lock:
            collection_data[data['id']] = data.copy()
        app_logger.debug(f"Stored object in {collection}: {data['id']}")
        return data

    @typechecked
    def retrieve_by_id(self, collection: str, id: str) -> Optional[Dict[str, Any]]:
        collection_data = self._get_collection(collection)
        with self._lock:
            result = collection_data.get(id)
        if result:
            app_logger.debug(f"Retrieved by ID {id} from {collection}: {result}")
            return result.copy()
        app_logger.debug(f"Retrieved by ID {id} from {collection}: None")
        return None

    @typechecked
    def retrieve_by_attributes(self, collection: str, query: Dict[str, Any]) -> List[Dict[str, Any]]:
        app_logger.debug(f"Retrieving by attributes from {collection}: {query}")
        collection_data = self._get_collection(collection)
        
        with self._lock:
            if not query:
                results = [doc.copy() for doc in collection_data.values()]
            else:
                results = [
                    doc.copy() for doc in collection_data.values()
                    if self._match_all(doc, query)
                ]
        
        app_logger.debug(f"Found {len(results)} results in {collection}")
        return results

    @typechecked
    def delete(self, collection: str, id: str) -> bool:
        collection_data = self._get_collection(collection)
        with self._lock:
            if id in collection_data:
                del collection_data[id]
                app_logger.debug(f"Deleted {id} from {collection}")
                return True
            app_logger.debug(f"Failed to delete {id} from {collection}")
            return False

    @typechecked
    def clear(self) -> bool:
        app_logger.debug("Clearing in-memory database")
        with self._lock:
            self._storage.clear()
        app_logger.debug("Cleared entire in-memory database")
        return True

    def close(self) -> None:
        self.clear()
        if InMemoryStorageAdapterImpl in Singleton._instances:
            Singleton._instances.pop(InMemoryStorageAdapterImpl, None)

    def __del__(self):
        try:
            self.close()
        except Exception:
            pass
