#!/usr/bin/env python3
"""
Eden Concept Hierarchy - Multi-Level Semantic Network

Evolution from flat concepts to hierarchical semantic tree:
1. Root concepts (most abstract)
2. Mid-level concepts (domain-specific)
3. Leaf concepts (most specific)
4. Meta-concepts (concepts about concepts)
5. Dynamic reorganization

This enables:
- Multi-level reasoning ("System issues → Performance → Query speed")
- Concept inheritance (properties flow down hierarchy)
- Automatic abstraction (similar concepts merge upward)
- Semantic navigation (traverse tree to find solutions)
"""

import json
import numpy as np
from dataclasses import dataclass, asdict, field
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional, Any, Set, Tuple
from collections import defaultdict
import logging

# Configuration
EDEN_ROOT = Path("/Eden/CORE")
HIERARCHY_LOG = EDEN_ROOT / "logs" / "phi_hierarchy.log"
HIERARCHY_STATE = EDEN_ROOT / "phi_fractal" / "concept_hierarchy" / "tree_state.json"

HIERARCHY_STATE.parent.mkdir(parents=True, exist_ok=True)
HIERARCHY_LOG.parent.mkdir(parents=True, exist_ok=True)

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - HIERARCHY - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler(HIERARCHY_LOG),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)


@dataclass
class HierarchicalConcept:
    """A concept in the semantic tree"""
    id: str
    name: str
    level: int  # 0=root, 1=mid, 2=leaf
    parent_id: Optional[str]  # None for root
    children_ids: List[str] = field(default_factory=list)
    case_ids: List[str] = field(default_factory=list)
    
    # Inherited properties (flow down from parent)
    inherited_properties: Dict[str, Any] = field(default_factory=dict)
    
    # Own properties (defined at this level)
    own_properties: Dict[str, Any] = field(default_factory=dict)
    
    confidence: float = 0.5
    usage_count: int = 0
    success_count: int = 0
    abstraction_level: float = 0.0  # 0=concrete, 1=abstract
    
    @property
    def all_properties(self) -> Dict[str, Any]:
        """Combine inherited and own properties"""
        combined = dict(self.inherited_properties)
        combined.update(self.own_properties)
        return combined
    
    @property
    def is_root(self) -> bool:
        return self.parent_id is None
    
    @property
    def is_leaf(self) -> bool:
        return len(self.children_ids) == 0


@dataclass
class MetaConcept:
    """A concept ABOUT concepts (meta-level)"""
    id: str
    name: str
    pattern: str  # What pattern does this meta-concept capture?
    applies_to: List[str]  # Which concept IDs does this apply to?
    insight: str  # What insight does this provide?
    confidence: float = 0.5


class ConceptHierarchy:
    """Multi-level semantic tree"""
    
    def __init__(self):
        self.concepts: Dict[str, HierarchicalConcept] = {}
        self.meta_concepts: Dict[str, MetaConcept] = {}
        self.roots: List[str] = []  # Root concept IDs
        self.concept_count = 0
        self.meta_count = 0
        
        self.load_state()
        logger.info("🌳 Concept Hierarchy initialized")
    
    def load_state(self):
        """Load hierarchy from disk"""
        if HIERARCHY_STATE.exists():
            try:
                with open(HIERARCHY_STATE, 'r') as f:
                    data = json.load(f)
                
                for cid, cdata in data.get('concepts', {}).items():
                    self.concepts[cid] = HierarchicalConcept(**cdata)
                
                for mid, mdata in data.get('meta_concepts', {}).items():
                    self.meta_concepts[mid] = MetaConcept(**mdata)
                
                self.roots = data.get('roots', [])
                self.concept_count = len(self.concepts)
                self.meta_count = len(self.meta_concepts)
                
                logger.info(f"Loaded {len(self.concepts)} concepts, {len(self.meta_concepts)} meta-concepts")
            except Exception as e:
                logger.error(f"Failed to load state: {e}")
    
    def save_state(self):
        """Save hierarchy"""
        try:
            data = {
                'concepts': {cid: asdict(c) for cid, c in self.concepts.items()},
                'meta_concepts': {mid: asdict(m) for mid, m in self.meta_concepts.items()},
                'roots': self.roots,
                'last_updated': datetime.now().isoformat()
            }
            
            with open(HIERARCHY_STATE, 'w') as f:
                json.dump(data, f, indent=2)
        except Exception as e:
            logger.error(f"Failed to save state: {e}")
    
    def build_from_flat_concepts(self, flat_concepts: Dict[str, Any]) -> None:
        """
        Transform flat concept graph into hierarchy
        
        Strategy:
        1. Identify natural groupings (similar concepts)
        2. Create parent concepts for groups
        3. Establish inheritance
        4. Form meta-concepts
        """
        logger.info(f"🏗️  Building hierarchy from {len(flat_concepts)} flat concepts...")
        
        # Step 1: Create root concept (most abstract)
        self.concept_count += 1
        root = HierarchicalConcept(
            id=f"h_concept_{self.concept_count:03d}",
            name="System_Problems",
            level=0,
            parent_id=None,
            own_properties={
                'description': 'All system-level problems and solutions',
                'abstraction': 'highest'
            },
            confidence=0.9,
            abstraction_level=1.0
        )
        self.concepts[root.id] = root
        self.roots.append(root.id)
        logger.info(f"   Created root: {root.name}")
        
        # Step 2: Group flat concepts by similarity
        concept_groups = self._group_concepts(flat_concepts)
        
        # Step 3: Create mid-level concepts for each group
        for group_name, group_concepts in concept_groups.items():
            self.concept_count += 1
            mid_concept = HierarchicalConcept(
                id=f"h_concept_{self.concept_count:03d}",
                name=group_name,
                level=1,
                parent_id=root.id,
                inherited_properties=root.all_properties,
                own_properties={
                    'category': group_name.lower().replace('_', ' '),
                    'specificity': 'medium'
                },
                confidence=0.7,
                abstraction_level=0.5
            )
            
            # Add as child to root
            root.children_ids.append(mid_concept.id)
            self.concepts[mid_concept.id] = mid_concept
            logger.info(f"   Created mid-level: {mid_concept.name}")
            
            # Step 4: Convert flat concepts to leaf concepts
            for flat_concept in group_concepts:
                self.concept_count += 1
                leaf = HierarchicalConcept(
                    id=f"h_concept_{self.concept_count:03d}",
                    name=flat_concept.get('name', 'Unknown'),
                    level=2,
                    parent_id=mid_concept.id,
                    case_ids=flat_concept.get('case_ids', []),
                    inherited_properties=mid_concept.all_properties,
                    own_properties={
                        'solution_patterns': flat_concept.get('structural_features', {}).get('solution_patterns', {}),
                        'specificity': 'high'
                    },
                    confidence=flat_concept.get('confidence', 0.5),
                    abstraction_level=0.1
                )
                
                # Add as child to mid-level
                mid_concept.children_ids.append(leaf.id)
                self.concepts[leaf.id] = leaf
                logger.info(f"     → Leaf: {leaf.name} ({len(leaf.case_ids)} cases)")
        
        # Step 5: Form meta-concepts
        self._form_meta_concepts()
        
        self.save_state()
        logger.info(f"✅ Hierarchy built: {len(self.roots)} roots, {len(self.concepts)} total concepts")
    
    def _group_concepts(self, flat_concepts: Dict[str, Any]) -> Dict[str, List[Dict]]:
        """Group similar flat concepts"""
        groups = {
            'Technical_Issues': [],
            'Architectural_Patterns': [],
            'Resource_Optimization': []
        }
        
        for concept_id, concept_data in flat_concepts.items():
            name = concept_data.get('name', '')
            
            # Group by semantic similarity
            if any(word in name for word in ['Performance', 'Scalability', 'Speed']):
                groups['Technical_Issues'].append(concept_data)
            elif any(word in name for word in ['System', 'Architecture', 'Design']):
                groups['Architectural_Patterns'].append(concept_data)
            elif any(word in name for word in ['Resource', 'Memory', 'CPU']):
                groups['Resource_Optimization'].append(concept_data)
            else:
                # Default group
                groups['Technical_Issues'].append(concept_data)
        
        # Remove empty groups
        return {k: v for k, v in groups.items() if v}
    
    def _form_meta_concepts(self):
        """Identify patterns ABOUT concepts (meta-level)"""
        logger.info("🔍 Forming meta-concepts...")
        
        # Meta-concept 1: "Performance problems cluster together"
        perf_concepts = [
            cid for cid, c in self.concepts.items()
            if 'performance' in c.name.lower() or 'speed' in c.name.lower()
        ]
        
        if len(perf_concepts) >= 2:
            self.meta_count += 1
            meta = MetaConcept(
                id=f"meta_{self.meta_count:03d}",
                name="Performance_Pattern",
                pattern="Problems involving slow execution tend to share solution patterns",
                applies_to=perf_concepts,
                insight="Indexing, caching, and parallelization are common solutions",
                confidence=0.8
            )
            self.meta_concepts[meta.id] = meta
            logger.info(f"   Meta-concept: {meta.name} (applies to {len(perf_concepts)} concepts)")
        
        # Meta-concept 2: "Scalability requires distribution"
        scale_concepts = [
            cid for cid, c in self.concepts.items()
            if 'scale' in c.name.lower()
        ]
        
        if scale_concepts:
            self.meta_count += 1
            meta = MetaConcept(
                id=f"meta_{self.meta_count:03d}",
                name="Scalability_Principle",
                pattern="Scalability problems require distributing load",
                applies_to=scale_concepts,
                insight="Single-instance solutions don't scale; distribution is key",
                confidence=0.9
            )
            self.meta_concepts[meta.id] = meta
            logger.info(f"   Meta-concept: {meta.name}")
    
    def navigate_to_solution(self, problem: str, features: Dict[str, Any]) -> Dict[str, Any]:
        """
        Navigate hierarchy to find solution
        Top-down reasoning through the tree
        """
        logger.info(f"🧭 Navigating hierarchy for: {problem[:60]}...")
        
        # Start at root
        current_path = []
        current_concepts = self.roots.copy()
        
        # Navigate down the tree
        for level in range(3):  # Max 3 levels
            if not current_concepts:
                break
            
            # Score concepts at current level
            scored = []
            for concept_id in current_concepts:
                concept = self.concepts.get(concept_id)
                if not concept:
                    continue
                
                score = self._score_concept_match(problem, features, concept)
                if score > 0.3:
                    scored.append((concept, score))
            
            if not scored:
                logger.info(f"   No match at level {level}")
                break
            
            # Take best match
            scored.sort(key=lambda x: x[1], reverse=True)
            best_concept, best_score = scored[0]
            current_path.append((best_concept, best_score))
            
            logger.info(f"   Level {level}: {best_concept.name} ({best_score:.0%})")
            
            # Go to children
            current_concepts = best_concept.children_ids
        
        # Build solution from path
        if not current_path:
            return {'success': False, 'reason': 'No matching path in hierarchy'}
        
        # Get the leaf (most specific)
        leaf_concept, leaf_score = current_path[-1]
        
        solution = {
            'success': True,
            'path': [c.name for c, _ in current_path],
            'leaf_concept': leaf_concept.name,
            'confidence': leaf_score * leaf_concept.confidence,
            'solution_patterns': leaf_concept.own_properties.get('solution_patterns', {}),
            'reasoning': f"Navigated: {' → '.join([c.name for c, _ in current_path])}"
        }
        
        # Check for applicable meta-concepts
        applicable_meta = [
            m for m in self.meta_concepts.values()
            if leaf_concept.id in m.applies_to
        ]
        
        if applicable_meta:
            solution['meta_insights'] = [m.insight for m in applicable_meta]
        
        logger.info(f"   ✅ Solution path: {solution['path']}")
        return solution
    
    def _score_concept_match(self, problem: str, features: Dict[str, Any], 
                            concept: HierarchicalConcept) -> float:
        """Score how well problem matches concept"""
        score = 0.0
        problem_lower = problem.lower()
        
        # Name matching
        name_words = concept.name.lower().replace('_', ' ').split()
        for word in name_words:
            if word in problem_lower:
                score += 0.3
        
        # Property matching
        if concept.own_properties:
            category = concept.own_properties.get('category', '')
            if category and any(word in problem_lower for word in category.split()):
                score += 0.4
        
        # Feature matching
        if features.get('has_performance_issue') and 'performance' in concept.name.lower():
            score += 0.3
        
        if features.get('has_scalability_issue') and 'scalability' in concept.name.lower():
            score += 0.3
        
        return min(1.0, score)
    
    def visualize_tree(self, max_depth: int = 3) -> str:
        """Generate ASCII tree visualization"""
        lines = []
        
        def traverse(concept_id: str, depth: int, prefix: str, is_last: bool):
            if depth > max_depth:
                return
            
            concept = self.concepts.get(concept_id)
            if not concept:
                return
            
            # Draw branch
            branch = "└── " if is_last else "├── "
            lines.append(f"{prefix}{branch}{concept.name} (L{concept.level}, {concept.confidence:.0%})")
            
            # Recurse to children
            extension = "    " if is_last else "│   "
            for i, child_id in enumerate(concept.children_ids):
                is_last_child = (i == len(concept.children_ids) - 1)
                traverse(child_id, depth + 1, prefix + extension, is_last_child)
        
        # Start from roots
        for i, root_id in enumerate(self.roots):
            is_last_root = (i == len(self.roots) - 1)
            traverse(root_id, 0, "", is_last_root)
        
        return "\n".join(lines)
    
    def get_statistics(self) -> Dict[str, Any]:
        """Get hierarchy statistics"""
        levels = defaultdict(int)
        for concept in self.concepts.values():
            levels[concept.level] += 1
        
        return {
            'total_concepts': len(self.concepts),
            'roots': len(self.roots),
            'levels': dict(levels),
            'meta_concepts': len(self.meta_concepts),
            'max_depth': max((c.level for c in self.concepts.values()), default=0),
            'avg_children': np.mean([len(c.children_ids) for c in self.concepts.values() if not c.is_leaf])
        }


def test_concept_hierarchy():
    """Test hierarchical concept system"""
    print("\n" + "=" * 70)
    print("🌳 EDEN CONCEPT HIERARCHY - TEST MODE")
    print("=" * 70 + "\n")
    
    # Load flat concepts
    import sys
    sys.path.append('/Eden/CORE/phi_fractal')
    from concept_graph import ConceptGraph
    
    flat_graph = ConceptGraph()
    flat_concepts = {cid: asdict(c) for cid, c in flat_graph.concepts.items()}
    
    print(f"Loaded {len(flat_concepts)} flat concepts\n")
    
    # Build hierarchy
    hierarchy = ConceptHierarchy()
    hierarchy.build_from_flat_concepts(flat_concepts)
    
    # Show tree
    print("\n" + "=" * 70)
    print("🌳 SEMANTIC TREE STRUCTURE")
    print("=" * 70 + "\n")
    print(hierarchy.visualize_tree())
    
    # Statistics
    print("\n" + "=" * 70)
    print("📊 HIERARCHY STATISTICS")
    print("=" * 70 + "\n")
    
    stats = hierarchy.get_statistics()
    print(f"Total concepts: {stats['total_concepts']}")
    print(f"Root concepts: {stats['roots']}")
    print(f"Max depth: {stats['max_depth']}")
    print(f"Meta-concepts: {stats['meta_concepts']}")
    print(f"\nConcepts by level:")
    for level, count in sorted(stats['levels'].items()):
        level_name = ['Root', 'Mid-level', 'Leaf'][level] if level < 3 else f'Level {level}'
        print(f"  {level_name}: {count}")
    
    # Test navigation
    print("\n" + "=" * 70)
    print("🧪 TEST: Hierarchical Navigation")
    print("=" * 70 + "\n")
    
    test_problem = "Database queries running slowly under heavy load"
    test_features = {
        'has_performance_issue': 1,
        'has_scalability_issue': 1
    }
    
    print(f"Problem: {test_problem}\n")
    result = hierarchy.navigate_to_solution(test_problem, test_features)
    
    if result['success']:
        print(f"✅ Navigation successful!")
        print(f"   Path: {' → '.join(result['path'])}")
        print(f"   Leaf: {result['leaf_concept']}")
        print(f"   Confidence: {result['confidence']:.0%}")
        
        if result.get('solution_patterns'):
            print(f"   Solution patterns:")
            for pattern, count in result['solution_patterns'].items():
                print(f"     • {pattern}: {count} cases")
        
        if result.get('meta_insights'):
            print(f"   Meta-insights:")
            for insight in result['meta_insights']:
                print(f"     💡 {insight}")
    else:
        print(f"❌ {result['reason']}")
    
    print(f"\n💾 Hierarchy saved to: {HIERARCHY_STATE}")
    print("\n🌳 Eden now thinks in HIERARCHIES! 🌳\n")


if __name__ == "__main__":
    import sys
    if len(sys.argv) > 1 and sys.argv[1] == "test":
        test_concept_hierarchy()
    else:
        print("Eden Concept Hierarchy")
        print("Usage: python3 concept_hierarchy.py test")
