"""
Triple Manager Module

This module provides comprehensive CRUD operations for RDF triples and triple
store management, enabling unified access to multiple triple store backends
through a common interface.

Key Features:
    - CRUD operations for RDF triples
    - Multi-store management and registration
    - Batch operations and bulk loading
    - Triple validation and consistency
    - Store adapter pattern
    - Error handling and recovery

Main Classes:
    - TripleManager: Main triple store management coordinator
    - TripleStore: Triple store configuration dataclass

Example Usage:
    >>> from semantica.triple_store import TripleManager
    >>> manager = TripleManager()
    >>> store = manager.register_store("main", "blazegraph", "http://localhost:9999/blazegraph")
    >>> result = manager.add_triple(triple, store_id="main")
    >>> triples = manager.get_triple(subject="http://example.org/entity1")
    >>> result = manager.add_triples(triple_list, store_id="main", batch_size=1000)

Author: Semantica Contributors
License: MIT
"""

from typing import Any, Dict, List, Optional
from dataclasses import dataclass, field

from ..utils.exceptions import ValidationError, ProcessingError
from ..utils.logging import get_logger
from ..utils.progress_tracker import get_progress_tracker
from ..semantic_extract.triple_extractor import Triple


@dataclass
class TripleStore:
    """Triple store configuration."""
    store_id: str
    store_type: str  # "blazegraph", "jena", "rdf4j", "virtuoso"
    endpoint: str
    config: Dict[str, Any] = field(default_factory=dict)
    connected: bool = False


class TripleManager:
    """
    Triple store management system.
    
    • CRUD operations for RDF triples
    • Batch operations and bulk loading
    • Triple validation and consistency
    • Transaction support
    • Performance optimization
    • Error handling and recovery
    """
    
    def __init__(self, config: Optional[Dict[str, Any]] = None, **kwargs):
        """
        Initialize triple manager.
        
        Args:
            config: Configuration dictionary
            **kwargs: Additional configuration options:
                - default_store: Default triple store to use
        """
        self.logger = get_logger("triple_manager")
        self.config = config or {}
        self.config.update(kwargs)
        self.progress_tracker = get_progress_tracker()
        
        self.stores: Dict[str, TripleStore] = {}
        self.default_store_id = self.config.get("default_store")
    
    def register_store(
        self,
        store_id: str,
        store_type: str,
        endpoint: str,
        **config
    ) -> TripleStore:
        """
        Register a triple store.
        
        Args:
            store_id: Store identifier
            store_type: Store type
            endpoint: Store endpoint URL
            **config: Additional configuration
        
        Returns:
            Registered store
        """
        store = TripleStore(
            store_id=store_id,
            store_type=store_type,
            endpoint=endpoint,
            config=config
        )
        
        self.stores[store_id] = store
        
        if not self.default_store_id:
            self.default_store_id = store_id
        
        self.logger.info(f"Registered triple store: {store_id} ({store_type})")
        
        return store
    
    def add_triple(
        self,
        triple: Triple,
        store_id: Optional[str] = None,
        **options
    ) -> Dict[str, Any]:
        """
        Add single triple to store.
        
        Args:
            triple: Triple to add
            store_id: Store identifier (uses default if not provided)
            **options: Additional options
        
        Returns:
            Operation status
        """
        store = self._get_store(store_id)
        
        # Validate triple
        if not self._validate_triple(triple):
            raise ValidationError("Invalid triple")
        
        # Add to store (delegates to adapter)
        try:
            adapter = self._get_adapter(store)
            result = adapter.add_triple(triple, **options)
            
            return {
                "success": True,
                "store_id": store.store_id,
                "triple": triple,
                **result
            }
        except Exception as e:
            self.logger.error(f"Failed to add triple: {e}")
            raise ProcessingError(f"Failed to add triple: {e}")
    
    def add_triples(
        self,
        triples: List[Triple],
        store_id: Optional[str] = None,
        **options
    ) -> Dict[str, Any]:
        """
        Add multiple triples to store.
        
        Args:
            triples: List of triples
            store_id: Store identifier
            **options: Additional options:
                - batch_size: Batch size for bulk operations
                - validate: Validate triples before adding
        
        Returns:
            Operation status
        """
        tracking_id = self.progress_tracker.start_tracking(
            module="triple_store",
            submodule="TripleManager",
            message=f"Adding {len(triples)} triples to store"
        )
        
        try:
            store = self._get_store(store_id)
            
            # Validate triples
            if options.get("validate", True):
                self.progress_tracker.update_tracking(tracking_id, message="Validating triples...")
                valid_triples = [t for t in triples if self._validate_triple(t)]
                invalid_count = len(triples) - len(valid_triples)
                if invalid_count > 0:
                    self.logger.warning(f"{invalid_count} invalid triples filtered out")
            else:
                valid_triples = triples
            
            # Add to store
            adapter = self._get_adapter(store)
            batch_size = options.get("batch_size", 1000)
            total_batches = (len(valid_triples) + batch_size - 1) // batch_size
            
            results = []
            for i in range(0, len(valid_triples), batch_size):
                batch_num = i // batch_size + 1
                self.progress_tracker.update_tracking(tracking_id, message=f"Processing batch {batch_num}/{total_batches}...")
                batch = valid_triples[i:i + batch_size]
                result = adapter.add_triples(batch, **options)
                results.append(result)
            
            self.progress_tracker.stop_tracking(tracking_id, status="completed",
                                              message=f"Added {len(valid_triples)} triples in {len(results)} batches")
            return {
                "success": True,
                "store_id": store.store_id,
                "total_triples": len(valid_triples),
                "batches": len(results),
                "results": results
            }
        except Exception as e:
            self.logger.error(f"Failed to add triples: {e}")
            self.progress_tracker.stop_tracking(tracking_id, status="failed", message=str(e))
            raise ProcessingError(f"Failed to add triples: {e}")
    
    def get_triple(
        self,
        subject: str,
        predicate: Optional[str] = None,
        object: Optional[str] = None,
        store_id: Optional[str] = None,
        **options
    ) -> List[Triple]:
        """
        Get triples matching criteria.
        
        Args:
            subject: Subject URI
            predicate: Optional predicate URI
            object: Optional object URI
            store_id: Store identifier
            **options: Additional options
        
        Returns:
            List of matching triples
        """
        store = self._get_store(store_id)
        
        try:
            adapter = self._get_adapter(store)
            return adapter.get_triples(subject, predicate, object, **options)
        except Exception as e:
            self.logger.error(f"Failed to get triples: {e}")
            raise ProcessingError(f"Failed to get triples: {e}")
    
    def delete_triple(
        self,
        triple: Triple,
        store_id: Optional[str] = None,
        **options
    ) -> Dict[str, Any]:
        """
        Delete triple from store.
        
        Args:
            triple: Triple to delete
            store_id: Store identifier
            **options: Additional options
        
        Returns:
            Operation status
        """
        store = self._get_store(store_id)
        
        try:
            adapter = self._get_adapter(store)
            result = adapter.delete_triple(triple, **options)
            
            return {
                "success": True,
                "store_id": store.store_id,
                **result
            }
        except Exception as e:
            self.logger.error(f"Failed to delete triple: {e}")
            raise ProcessingError(f"Failed to delete triple: {e}")
    
    def update_triple(
        self,
        old_triple: Triple,
        new_triple: Triple,
        store_id: Optional[str] = None,
        **options
    ) -> Dict[str, Any]:
        """
        Update triple in store.
        
        Args:
            old_triple: Original triple
            new_triple: Updated triple
            store_id: Store identifier
            **options: Additional options
        
        Returns:
            Operation status
        """
        # Delete old and add new
        self.delete_triple(old_triple, store_id, **options)
        return self.add_triple(new_triple, store_id, **options)
    
    def _validate_triple(self, triple: Triple) -> bool:
        """Validate triple structure."""
        if not triple.subject or not triple.predicate or not triple.object:
            return False
        
        if triple.confidence < 0 or triple.confidence > 1:
            return False
        
        return True
    
    def _get_store(self, store_id: Optional[str] = None) -> TripleStore:
        """Get store by ID."""
        store_id = store_id or self.default_store_id
        
        if not store_id:
            raise ValidationError("No store specified and no default store configured")
        
        if store_id not in self.stores:
            raise ValidationError(f"Store not found: {store_id}")
        
        return self.stores[store_id]
    
    def _get_adapter(self, store: TripleStore) -> Any:
        """Get adapter for store type."""
        store_type = store.store_type.lower()
        
        if store_type == "blazegraph":
            from .blazegraph_adapter import BlazegraphAdapter
            return BlazegraphAdapter(endpoint=store.endpoint, **store.config)
        elif store_type == "jena":
            from .jena_adapter import JenaAdapter
            return JenaAdapter(**store.config)
        elif store_type == "rdf4j":
            from .rdf4j_adapter import RDF4JAdapter
            return RDF4JAdapter(endpoint=store.endpoint, **store.config)
        elif store_type == "virtuoso":
            from .virtuoso_adapter import VirtuosoAdapter
            return VirtuosoAdapter(endpoint=store.endpoint, **store.config)
        else:
            raise ValidationError(f"Unsupported store type: {store_type}")
    
    def get_store(self, store_id: str) -> Optional[TripleStore]:
        """Get store by ID."""
        return self.stores.get(store_id)
    
    def list_stores(self) -> List[str]:
        """List all store IDs."""
        return list(self.stores.keys())
