"""
Triplet Store Methods Module

This module provides all triplet store methods as simple, reusable functions for
registering stores, adding triplets, querying, and managing triplet stores. It supports
multiple approaches and integrates with the method registry for extensibility.

Supported Methods:

Store Registration:
    - "default": Default store registration using TripletStore
    - "blazegraph": Blazegraph-specific registration
    - "jena": Jena-specific registration
    - "rdf4j": RDF4J-specific registration

Triplet Addition:
    - "default": Default triplet addition
    - "single": Single triplet addition
    - "batch": Batch triplet addition
    - "bulk": Bulk triplet addition

Triplet Retrieval:
    - "default": Default triplet retrieval
    - "pattern": Pattern-based retrieval
    - "sparql": SPARQL-based retrieval

Triplet Deletion:
    - "default": Default triplet deletion
    - "single": Single triplet deletion
    - "batch": Batch triplet deletion

Triplet Update:
    - "default": Default triplet update (delete-then-add)

SPARQL Query:
    - "default": Default SPARQL query execution
    - "optimized": Optimized query execution

Query Optimization:
    - "default": Default query optimization

Bulk Loading:
    - "default": Default bulk loading
    - "batch": Batch-based bulk loading

Validation:
    - "default": Default validation
    - "triplet": Triplet structure validation

Algorithms Used:

Store Registration:
    - Store Type Detection: Backend type identification
    - Store Factory: Create appropriate store instance based on store type

Triplet Operations:
    - Triplet Validation: Required field checking, confidence validation
    - Batch Processing: Chunking algorithm, batch size optimization
    - Pattern Matching: Subject/predicate/object filtering

SPARQL Query:
    - Query Validation: Syntax checking
    - Query Optimization: Cost estimation
    - Query Caching: LRU-style eviction

Bulk Loading:
    - Progress Tracking: Load percentage calculation
    - Retry Mechanism: Exponential backoff

Key Features:
    - Unified triplet store interface
    - Consistent interface across all methods

Main Functions:
    - register_store: Store registration wrapper
    - add_triplet: Triplet addition wrapper
    - add_triplets: Multiple triplet addition wrapper
    - get_triplets: Triplet retrieval wrapper
    - delete_triplet: Triplet deletion wrapper
    - execute_query: SPARQL query execution wrapper
    - bulk_load: Bulk loading wrapper
    - get_triplet_store_method: Get triplet store method by task and name
    - list_available_methods: List registered methods

Example Usage:
    >>> from semantica.triplet_store.methods import register_store, add_triplet, execute_query
    >>> store = register_store("main", "blazegraph", "http://localhost:9999/blazegraph")
    >>> result = add_triplet(triplet, store_id="main")
    >>> query_result = execute_query(sparql_query, store_backend)
"""

from typing import Any, Dict, List, Optional, Union

from ..semantic_extract.triplet_extractor import Triplet
from .bulk_loader import BulkLoader, LoadProgress
from .config import triplet_store_config
from .query_engine import QueryEngine, QueryPlan, QueryResult
from .registry import method_registry
from .triplet_store import TripletStore

# Global store registry
_global_stores: Dict[str, TripletStore] = {}
_default_store_id: Optional[str] = None
_global_query_engine: Optional[QueryEngine] = None
_global_bulk_loader: Optional[BulkLoader] = None


def _get_store(store_id: Optional[str] = None) -> TripletStore:
    """Get triplet store instance."""
    global _default_store_id
    
    target_id = store_id or _default_store_id
    
    if not target_id:
        # Try to create a default store from config if none registered
        if not _global_stores:
            config = triplet_store_config.get_all()
            default_backend = config.get("default_backend", "blazegraph")
            default_endpoint = config.get(f"{default_backend}_endpoint", "http://localhost:9999/blazegraph")
            
            store = TripletStore(backend=default_backend, endpoint=default_endpoint)
            _global_stores["default"] = store
            _default_store_id = "default"
            return store
            
        raise ValueError("No store identifier provided and no default store set")
        
    if target_id not in _global_stores:
        raise ValueError(f"Store not found: {target_id}")
        
    return _global_stores[target_id]


def _get_query_engine() -> QueryEngine:
    """Get or create global QueryEngine instance."""
    global _global_query_engine
    if _global_query_engine is None:
        # We need a store backend for the engine, but QueryEngine in this module
        # seems to be initialized with config in the old code.
        # In the new code, TripletStore has its own query_engine.
        # If we use this standalone function, we might need to rely on the store's engine.
        # But let's keep a standalone one if needed, or better, delegate to store.
        config = triplet_store_config.get_all()
        # QueryEngine now expects a backend, but we can initialize it without one 
        # if we pass the backend at execution time? 
        # Checking QueryEngine implementation... it takes `store_backend` in __init__.
        # So we can't easily have a global one without a store.
        # We'll rely on the store's engine.
        pass
    return None # Deprecated use of global engine


def _get_bulk_loader() -> BulkLoader:
    """Get or create global BulkLoader instance."""
    global _global_bulk_loader
    if _global_bulk_loader is None:
        _global_bulk_loader = BulkLoader()
    return _global_bulk_loader


def register_store(
    store_id: str, store_type: str, endpoint: str, method: str = "default", **options
) -> TripletStore:
    """
    Register a triplet store.

    Args:
        store_id: Store identifier
        store_type: Store type (blazegraph, jena, rdf4j)
        endpoint: Store endpoint URL
        method: Registration method name (default: "default")
        **options: Additional options

    Returns:
        Registered store
    """
    # Check registry for custom method
    custom_method = method_registry.get("register", method)
    if custom_method:
        return custom_method(store_id, store_type, endpoint, **options)

    # Default implementation
    store = TripletStore(backend=store_type, endpoint=endpoint, **options)
    
    global _default_store_id
    _global_stores[store_id] = store
    
    if _default_store_id is None:
        _default_store_id = store_id
        
    return store


def add_triplet(
    triplet: Triplet, store_id: Optional[str] = None, method: str = "default", **options
) -> Dict[str, Any]:
    """
    Add single triplet to store.

    Args:
        triplet: Triplet to add
        store_id: Store identifier (uses default if not provided)
        method: Addition method name (default: "default")
        **options: Additional options

    Returns:
        Operation result
    """
    # Check registry for custom method
    custom_method = method_registry.get("add", method)
    if custom_method:
        return custom_method(triplet, store_id=store_id, **options)

    # Default implementation
    store = _get_store(store_id)
    return store.add_triplet(triplet, **options)


def add_triplets(
    triplets: List[Triplet],
    store_id: Optional[str] = None,
    method: str = "default",
    **options,
) -> Dict[str, Any]:
    """
    Add multiple triplets to store.

    Args:
        triplets: List of triplets to add
        store_id: Store identifier
        method: Addition method name (default: "default")
        **options: Additional options

    Returns:
        Operation result
    """
    # Check registry for custom method
    custom_method = method_registry.get("add", method)
    if custom_method:
        return custom_method(triplets, store_id=store_id, **options)

    # Default implementation
    store = _get_store(store_id)
    return store.add_triplets(triplets, **options)


def get_triplets(
    subject: Optional[str] = None,
    predicate: Optional[str] = None,
    object: Optional[str] = None,
    store_id: Optional[str] = None,
    method: str = "default",
    **options,
) -> List[Triplet]:
    """
    Get triplets matching criteria.

    Args:
        subject: Optional subject URI
        predicate: Optional predicate URI
        object: Optional object URI
        store_id: Store identifier
        method: Retrieval method name (default: "default")
        **options: Additional options

    Returns:
        List of matching triplets
    """
    # Check registry for custom method
    custom_method = method_registry.get("get", method)
    if custom_method:
        return custom_method(subject, predicate, object, store_id=store_id, **options)

    # Default implementation
    store = _get_store(store_id)
    return store.get_triplets(subject, predicate=predicate, object=object, **options)


def delete_triplet(
    triplet: Triplet, store_id: Optional[str] = None, method: str = "default", **options
) -> Dict[str, Any]:
    """
    Delete triplet from store.

    Args:
        triplet: Triplet to delete
        store_id: Store identifier
        method: Deletion method name (default: "default")
        **options: Additional options

    Returns:
        Operation result
    """
    # Check registry for custom method
    custom_method = method_registry.get("delete", method)
    if custom_method:
        return custom_method(triplet, store_id=store_id, **options)

    # Default implementation
    # TripletStore doesn't explicitly have delete_triplet yet in my read of it?
    # Let's check TripletStore again. It has add, add_triplets, get_triplets.
    # It might be missing delete_triplet! I need to check.
    # If it is missing, I should add it.
    store = _get_store(store_id)
    if hasattr(store, "delete_triplet"):
        return store.delete_triplet(triplet, **options)
    # Fallback or error if not implemented
    raise NotImplementedError("delete_triplet not implemented in TripletStore yet")


def update_triplet(
    old_triplet: Triplet,
    new_triplet: Triplet,
    store_id: Optional[str] = None,
    method: str = "default",
    **options,
) -> Dict[str, Any]:
    """
    Update triplet in store.

    Args:
        old_triplet: Original triplet
        new_triplet: Updated triplet
        store_id: Store identifier
        method: Update method name (default: "default")
        **options: Additional options

    Returns:
        Operation result
    """
    # Check registry for custom method
    custom_method = method_registry.get("update", method)
    if custom_method:
        return custom_method(old_triplet, new_triplet, store_id=store_id, **options)

    # Default implementation
    # Delete then Add
    delete_res = delete_triplet(old_triplet, store_id=store_id, **options)
    add_res = add_triplet(new_triplet, store_id=store_id, **options)
    
    return {
        "success": delete_res.get("success", False) and add_res.get("success", False),
        "delete_result": delete_res,
        "add_result": add_res
    }


def execute_query(
    query: str, store_backend: Any = None, method: str = "default", **options
) -> QueryResult:
    """
    Execute SPARQL query.

    Args:
        query: SPARQL query string
        store_backend: Triplet store backend instance or TripletStore object
        method: Query method name (default: "default")
        **options: Additional options

    Returns:
        Query result
    """
    # Check registry for custom method
    custom_method = method_registry.get("query", method)
    if custom_method:
        return custom_method(query, store_backend, **options)

    # Default implementation
    if isinstance(store_backend, TripletStore):
        return store_backend.query_engine.execute_query(query, store_backend._store_backend, **options)
    elif hasattr(store_backend, "execute_query"):
         # It might be the backend itself
         # But QueryEngine expects a backend.
         # If store_backend is None, use default store
         if store_backend is None:
             store = _get_store()
             return store.query_engine.execute_query(query, store._store_backend, **options)
         
         # If it's a backend, we need an engine.
         # We can create one on the fly or use global if we had one.
         engine = QueryEngine(store_backend)
         return engine.execute_query(query, store_backend, **options)
         
    # Fallback
    store = _get_store()
    return store.query_engine.execute_query(query, store._store_backend, **options)


def optimize_query(query: str, method: str = "default", **options) -> str:
    """
    Optimize SPARQL query.

    Args:
        query: Original SPARQL query
        method: Optimization method name (default: "default")
        **options: Additional options

    Returns:
        Optimized query
    """
    # Check registry for custom method
    custom_method = method_registry.get("optimize", method)
    if custom_method:
        return custom_method(query, **options)

    # Default implementation
    # We need a store to get its engine
    store = _get_store()
    return store.query_engine.optimize_query(query, **options)


def plan_query(query: str, **options) -> QueryPlan:
    """
    Create query execution plan.

    Args:
        query: SPARQL query
        **options: Planning options

    Returns:
        Query execution plan
    """
    store = _get_store()
    return store.query_engine.plan_query(query, **options)


def bulk_load(
    triplets: List[Triplet], store_backend: Any = None, method: str = "default", **options
) -> LoadProgress:
    """
    Load triplets in bulk.

    Args:
        triplets: List of triplets to load
        store_backend: Triplet store backend instance
        method: Loading method name (default: "default")
        **options: Additional options

    Returns:
        Load progress information
    """
    # Check registry for custom method
    custom_method = method_registry.get("bulk_load", method)
    if custom_method:
        return custom_method(triplets, store_backend, **options)

    # Default implementation
    if store_backend is None:
        store = _get_store()
        return store.bulk_loader.load_triplets(triplets, store._store_backend, **options)
        
    loader = _get_bulk_loader()
    return loader.load_triplets(triplets, store_backend, **options)


def validate_triplets(
    triplets: List[Triplet], method: str = "default", **options
) -> Dict[str, Any]:
    """
    Validate triplets before loading.

    Args:
        triplets: List of triplets to validate
        method: Validation method name (default: "default")
        **options: Validation options

    Returns:
        Validation results
    """
    # Check registry for custom method
    custom_method = method_registry.get("validate", method)
    if custom_method:
        return custom_method(triplets, **options)

    # Default implementation
    loader = _get_bulk_loader()
    return loader.validate_before_load(triplets, **options)


def get_triplet_store_method(task: str, method_name: str) -> Optional[Any]:
    """
    Get triplet store method by task and name.

    Args:
        task: Task type (register, add, get, delete, update, query, optimize, bulk_load, validate)
        method_name: Method name

    Returns:
        Method function or None if not found
    """
    return method_registry.get(task, method_name)


def list_available_methods(task: Optional[str] = None) -> Dict[str, List[str]]:
    """
    List all available triplet store methods.

    Args:
        task: Optional task type to filter by

    Returns:
        Dictionary mapping task types to lists of method names
    """
    return method_registry.list_all(task)
