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

# Add project root to path
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))

from semantica.context import AgentContext, ContextGraph

@dataclass
class VectorSearchResult:
    id: str
    content: str
    score: float
    metadata: Dict[str, Any]

class MockVectorStore:
    def __init__(self):
        self.items: Dict[str, Any] = {}
        
    def add(self, items: List[Any]) -> List[str]:
        ids = []
        for item in items:
            # Simple ID generation if not present
            if not item.memory_id:
                item.memory_id = f"mem_{len(self.items)}"
            self.items[item.memory_id] = item
            ids.append(item.memory_id)
        print(f"MockVectorStore: Added {len(ids)} items")
        return ids
        
    def search(
        self, 
        query: str, 
        limit: int = 10, 
        filters: Optional[Dict[str, Any]] = None
    ) -> List[VectorSearchResult]:
        print(f"MockVectorStore: Searching for '{query}'")
        results = []
        # Simple keyword match simulation
        query_terms = query.lower().split()
        for item in self.items.values():
            if any(term in item.content.lower() for term in query_terms):
                results.append(VectorSearchResult(
                    id=item.memory_id,
                    content=item.content,
                    score=0.8,
                    metadata=item.metadata
                ))
        return results[:limit]
        
    def delete(self, ids: List[str]) -> bool:
        count = 0
        for mid in ids:
            if mid in self.items:
                del self.items[mid]
                count += 1
        return count > 0

def test_synchronous_context():
    print("--- Testing Synchronous Context Module ---")
    
    # 1. Initialize Components
    vs = MockVectorStore()
    kg = ContextGraph()
    
    context = AgentContext(vector_store=vs, knowledge_graph=kg)
    print("AgentContext initialized successfully")
    
    # 2. Store Memory
    print("\n--- Testing Store ---")
    mem_id = context.store(
        "Python is a popular programming language.", 
        conversation_id="test_conv",
        extract_entities=True
    )
    print(f"Stored memory with ID: {mem_id}")
    
    # 3. Store Documents (with graph update)
    print("\n--- Testing Document Store with Graph ---")
    docs = [
        {
            "content": "Machine learning uses Python heavily.",
            "entities": [
                {"text": "Machine learning", "type": "CONCEPT"},
                {"text": "Python", "type": "TOOL"}
            ],
            "relationships": [
                {"source": "Machine learning", "target": "Python", "type": "uses"}
            ]
        },
        {
            "content": "TensorFlow is a Python library for ML.",
            "entities": [
                {"text": "TensorFlow", "type": "TOOL"},
                {"text": "Python", "type": "TOOL"},
                {"text": "ML", "type": "CONCEPT"}
            ],
            "relationships": [
                {"source": "TensorFlow", "target": "Python", "type": "based_on"}
            ]
        }
    ]
    stats = context.store(
        docs, 
        extract_entities=True,
        link_entities=True
    )
    print(f"Stored documents stats: {stats}")
    
    # 4. Verify Graph
    print("\n--- Verifying Graph ---")
    graph_stats = kg.stats()
    print(f"Graph stats: {graph_stats}")
    if graph_stats["node_count"] > 0:
        print("SUCCESS: Graph nodes created")
    else:
        print("WARNING: No graph nodes created (expected if entity extraction is mocked/empty)")

    # 5. Retrieve
    print("\n--- Testing Retrieval ---")
    results = context.retrieve("Python", max_results=5)
    print(f"Retrieved {len(results)} results")
    for r in results:
        print(f"- {r['content']} (Score: {r['score']})")

    # 6. Test Short-Term Memory
    print("\n--- Testing Short-Term Memory (Hierarchical) ---")
    st_id = context.store(
        "This is a fleeting thought.", 
        skip_vector=True
    )
    print(f"Stored short-term only memory: {st_id}")
    
    # Verify it's not in vector store (mock check)
    is_in_vector = any("fleeting" in item.content for item in vs.items.values())
    print(f"Is in vector store: {is_in_vector} (Expected: False)")
    
    # Retrieve it (should come from short-term buffer)
    st_results = context.retrieve("fleeting thought")
    print(f"Retrieved {len(st_results)} results for short-term query")
    for r in st_results:
        source_note = f" [Source: {r.get('source', 'unknown')}]" if 'source' in r else ""
        print(f"- {r['content']} (Score: {r['score']}){source_note}")

    # 7. Test Token Management
    print("\n--- Testing Token Management ---")
    # Initialize a new memory with small token limit
    from semantica.context import AgentMemory
    
    # Create isolated memory instance for testing
    token_memory = AgentMemory(
        vector_store=vs, 
        token_limit=10,  # Small limit (~40 chars)
        short_term_limit=5 # Count limit
    )
    
    token_memory.store("First small item", skip_vector=True) # ~16 chars = 4 tokens
    token_memory.store("Second small item", skip_vector=True) # ~17 chars = 4 tokens
    # Total ~8 tokens. Limit 10. Should fit.
    print(f"Items after 2 small: {len(token_memory.short_term_memory)}")
    
    token_memory.store("Third small item", skip_vector=True) # ~16 chars = 4 tokens
    # Total ~12 tokens. Limit 10. Should prune oldest ("First small item").
    print(f"Items after 3rd small: {len(token_memory.short_term_memory)}")
    remaining = [m.content for m in token_memory.short_term_memory]
    print(f"Remaining items: {remaining}")
    
    if len(token_memory.short_term_memory) == 2 and remaining[0] == "Second small item":
        print("SUCCESS: Token pruning worked")
    else:
        print(f"FAILURE: Token pruning failed. Items: {len(token_memory.short_term_memory)}")

    print("\n--- Test Complete ---")

if __name__ == "__main__":
    try:
        test_synchronous_context()
    except Exception as e:
        print(f"\nERROR: Test failed with exception: {e}")
        import traceback
        traceback.print_exc()
        sys.exit(1)
