#!/usr/bin/env python3
"""
Eden Transfer Map
Cross-domain learning: Learn in one domain, apply to another

Purpose: Build shared embedding space where concepts transfer across domains
Examples:
  - Learn "caching" in web dev → Apply to database design
  - Learn "recursion" in Python → Transfer to JavaScript
  - Understand "sorting" in arrays → Apply to linked lists
"""

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

# Configuration
EDEN_ROOT = Path("/Eden/CORE")
TRANSFER_LOG = EDEN_ROOT / "logs" / "phi_transfer.log"
TRANSFER_STATE = EDEN_ROOT / "phi_fractal" / "transfer_embedding" / "transfer_state.json"
EMBEDDINGS_DIR = EDEN_ROOT / "phi_fractal" / "transfer_embedding" / "embeddings"

# Create directories
TRANSFER_STATE.parent.mkdir(parents=True, exist_ok=True)
EMBEDDINGS_DIR.mkdir(parents=True, exist_ok=True)
TRANSFER_LOG.parent.mkdir(parents=True, exist_ok=True)

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


@dataclass
class Concept:
    """A learnable concept that can transfer across domains"""
    name: str
    domain: str  # e.g., "python", "javascript", "web_dev", "database"
    description: str
    properties: List[str]  # Key properties of the concept
    examples: List[str]
    embedding: Optional[np.ndarray] = None
    learned_from: Optional[str] = None
    
    def to_dict(self):
        d = asdict(self)
        if self.embedding is not None:
            d['embedding'] = self.embedding.tolist()
        return d


@dataclass
class TransferResult:
    """Result of transferring knowledge from one domain to another"""
    source_concept: str
    source_domain: str
    target_domain: str
    transferred_knowledge: Dict[str, Any]
    confidence: float
    analogies: List[str]
    success: bool


class ConceptEncoder:
    """Encodes concepts into shared embedding space"""
    
    def __init__(self, embedding_dim: int = 128):
        self.embedding_dim = embedding_dim
        self.domain_weights = {}
        
    def encode_concept(self, concept: Concept) -> np.ndarray:
        """
        Encode a concept into embedding space
        Simple but effective: weighted combination of features
        """
        # Create feature vector
        features = []
        
        # Domain embedding (one-hot style)
        domain_hash = hash(concept.domain) % 10
        features.extend([1.0 if i == domain_hash else 0.0 for i in range(10)])
        
        # Property embeddings
        for prop in concept.properties[:10]:  # Limit to 10 properties
            prop_hash = hash(prop) % 20
            features.extend([1.0 if i == prop_hash else 0.0 for i in range(20)])
        
        # Pad to embedding_dim
        while len(features) < self.embedding_dim:
            features.append(0.0)
        features = features[:self.embedding_dim]
        
        # Add some noise for generalization
        embedding = np.array(features) + np.random.normal(0, 0.01, self.embedding_dim)
        
        # Normalize
        norm = np.linalg.norm(embedding)
        if norm > 0:
            embedding = embedding / norm
            
        return embedding
    
    def similarity(self, emb1: np.ndarray, emb2: np.ndarray) -> float:
        """Compute cosine similarity between embeddings"""
        dot_product = np.dot(emb1, emb2)
        return float(dot_product)  # Already normalized


class TransferMap:
    """Main Transfer Map system for cross-domain learning"""
    
    def __init__(self):
        self.encoder = ConceptEncoder()
        self.concepts: Dict[str, Concept] = {}
        self.transfer_history: List[TransferResult] = []
        self.domain_mappings: Dict[Tuple[str, str], List[str]] = defaultdict(list)
        
        self._load_state()
        logger.info("🌐 Transfer Map initialized")
    
    def _load_state(self):
        """Load saved transfer state"""
        if TRANSFER_STATE.exists():
            try:
                with open(TRANSFER_STATE, 'r') as f:
                    data = json.load(f)
                
                # Load concepts
                for name, concept_data in data.get('concepts', {}).items():
                    concept = Concept(**concept_data)
                    if concept_data.get('embedding'):
                        concept.embedding = np.array(concept_data['embedding'])
                    self.concepts[name] = concept
                
                logger.info(f"Loaded {len(self.concepts)} concepts from state")
            except Exception as e:
                logger.error(f"Failed to load state: {e}")
    
    def _save_state(self):
        """Save transfer state"""
        try:
            data = {
                'concepts': {name: concept.to_dict() for name, concept in self.concepts.items()},
                'transfer_count': len(self.transfer_history),
                'last_updated': datetime.now().isoformat()
            }
            
            with open(TRANSFER_STATE, 'w') as f:
                json.dump(data, f, indent=2)
            
            logger.info(f"Saved {len(self.concepts)} concepts to state")
        except Exception as e:
            logger.error(f"Failed to save state: {e}")
    
    def learn_concept(self, concept: Concept) -> Concept:
        """Learn a new concept in a specific domain"""
        logger.info(f"📚 Learning concept: {concept.name} in {concept.domain}")
        
        # Encode the concept
        concept.embedding = self.encoder.encode_concept(concept)
        concept.learned_from = concept.domain
        
        # Store it
        concept_key = f"{concept.domain}:{concept.name}"
        self.concepts[concept_key] = concept
        
        self._save_state()
        logger.info(f"   ✅ Learned! Embedding shape: {concept.embedding.shape}")
        
        return concept
    
    def find_similar_concepts(self, concept: Concept, n: int = 5) -> List[Tuple[Concept, float]]:
        """Find similar concepts across all domains"""
        if concept.embedding is None:
            concept.embedding = self.encoder.encode_concept(concept)
        
        similarities = []
        for stored_concept in self.concepts.values():
            if stored_concept.embedding is not None:
                sim = self.encoder.similarity(concept.embedding, stored_concept.embedding)
                similarities.append((stored_concept, sim))
        
        # Sort by similarity
        similarities.sort(key=lambda x: x[1], reverse=True)
        return similarities[:n]
    
    def transfer_to_domain(self, source_concept: str, target_domain: str) -> TransferResult:
        """
        Transfer knowledge from one domain to another
        
        Example:
          transfer_to_domain("python:recursion", "javascript")
          → Returns how recursion applies in JavaScript
        """
        logger.info(f"🔄 Transferring {source_concept} → {target_domain}")
        
        # Find source concept
        if source_concept not in self.concepts:
            logger.error(f"   ❌ Source concept not found: {source_concept}")
            return TransferResult(
                source_concept=source_concept,
                source_domain="unknown",
                target_domain=target_domain,
                transferred_knowledge={},
                confidence=0.0,
                analogies=[],
                success=False
            )
        
        source = self.concepts[source_concept]
        
        # Find similar concepts in target domain
        target_concepts = [
            (c, self.encoder.similarity(source.embedding, c.embedding))
            for c in self.concepts.values()
            if c.domain == target_domain and c.embedding is not None
        ]
        
        # Generate transfer knowledge
        transferred_knowledge = {
            'core_properties': source.properties,
            'description': source.description,
            'adaptation_needed': True if target_concepts else False,
            'similar_in_target': []
        }
        
        analogies = []
        confidence = 0.5
        
        if target_concepts:
            # Sort by similarity
            target_concepts.sort(key=lambda x: x[1], reverse=True)
            best_match = target_concepts[0]
            
            transferred_knowledge['similar_in_target'] = [
                {'name': c.name, 'similarity': float(sim)}
                for c, sim in target_concepts[:3]
            ]
            
            confidence = float(best_match[1])
            analogies = [
                f"{source.name} in {source.domain} is like {best_match[0].name} in {target_domain}",
                f"Both involve: {', '.join(set(source.properties) & set(best_match[0].properties))}"
            ]
        else:
            # No similar concepts - pure transfer
            analogies = [
                f"Applying {source.name} concept to new domain: {target_domain}",
                f"Key properties to maintain: {', '.join(source.properties[:3])}"
            ]
        
        result = TransferResult(
            source_concept=source_concept,
            source_domain=source.domain,
            target_domain=target_domain,
            transferred_knowledge=transferred_knowledge,
            confidence=confidence,
            analogies=analogies,
            success=True
        )
        
        self.transfer_history.append(result)
        
        # Track domain mappings
        mapping_key = (source.domain, target_domain)
        self.domain_mappings[mapping_key].append(source_concept)
        
        logger.info(f"   ✅ Transfer complete! Confidence: {confidence:.2f}")
        
        return result
    
    def get_transfer_report(self) -> Dict[str, Any]:
        """Get transfer map statistics"""
        return {
            'total_concepts': len(self.concepts),
            'domains': list(set(c.domain for c in self.concepts.values())),
            'transfers_performed': len(self.transfer_history),
            'domain_mappings': {
                f"{src}→{tgt}": len(concepts)
                for (src, tgt), concepts in self.domain_mappings.items()
            }
        }


def test_transfer_map():
    """Test the Transfer Map with example concepts"""
    print("\n" + "=" * 70)
    print("🌐 EDEN TRANSFER MAP - TEST MODE")
    print("=" * 70 + "\n")
    
    tm = TransferMap()
    
    # Define test concepts across domains
    test_concepts = [
        # Python domain
        Concept(
            name="recursion",
            domain="python",
            description="Function calling itself to solve problem",
            properties=["self-referential", "base_case", "recursive_case", "stack_based"],
            examples=["factorial", "fibonacci", "tree_traversal"]
        ),
        Concept(
            name="list_comprehension",
            domain="python",
            description="Compact way to create lists",
            properties=["functional", "concise", "iterable", "transformation"],
            examples=["[x*2 for x in range(10)]"]
        ),
        
        # JavaScript domain
        Concept(
            name="callback",
            domain="javascript",
            description="Function passed as argument to another function",
            properties=["functional", "async", "higher_order", "closure"],
            examples=["setTimeout", "map", "filter"]
        ),
        Concept(
            name="recursion",
            domain="javascript",
            description="Function calling itself",
            properties=["self-referential", "base_case", "recursive_case"],
            examples=["factorial", "tree_traversal"]
        ),
        
        # Web development
        Concept(
            name="caching",
            domain="web_dev",
            description="Store frequently accessed data for quick retrieval",
            properties=["performance", "memory", "invalidation", "strategy"],
            examples=["redis", "memcached", "browser_cache"]
        ),
        
        # Database
        Concept(
            name="indexing",
            domain="database",
            description="Data structure for fast lookup",
            properties=["performance", "memory", "query_optimization", "trade_off"],
            examples=["b-tree", "hash_index"]
        ),
    ]
    
    # Learn concepts
    print("📚 Learning concepts across domains...\n")
    for concept in test_concepts:
        tm.learn_concept(concept)
        print(f"   ✅ {concept.domain}:{concept.name}")
    
    print("\n" + "=" * 70)
    print("🔄 Testing cross-domain transfer...\n")
    
    # Test transfers
    transfers = [
        ("python:recursion", "javascript"),
        ("python:list_comprehension", "javascript"),
        ("web_dev:caching", "database"),
    ]
    
    for source, target in transfers:
        print(f"Transfer: {source} → {target}")
        result = tm.transfer_to_domain(source, target)
        
        print(f"   Confidence: {result.confidence:.2f}")
        print(f"   Analogies:")
        for analogy in result.analogies:
            print(f"      • {analogy}")
        print()
    
    # Show similar concepts
    print("=" * 70)
    print("🔍 Finding similar concepts...\n")
    
    python_recursion = tm.concepts["python:recursion"]
    similar = tm.find_similar_concepts(python_recursion, n=3)
    
    print(f"Concepts similar to python:recursion:")
    for concept, similarity in similar:
        print(f"   {concept.domain}:{concept.name} - {similarity:.3f} similarity")
    
    # Final report
    print("\n" + "=" * 70)
    print("📊 Transfer Map Report")
    print("=" * 70)
    
    report = tm.get_transfer_report()
    print(f"\nTotal concepts learned: {report['total_concepts']}")
    print(f"Domains covered: {', '.join(report['domains'])}")
    print(f"Transfers performed: {report['transfers_performed']}")
    
    if report['domain_mappings']:
        print("\nDomain transfer mappings:")
        for mapping, count in report['domain_mappings'].items():
            print(f"   {mapping}: {count} transfers")
    
    print(f"\n💾 State saved to: {TRANSFER_STATE}\n")


def main():
    import sys
    if len(sys.argv) > 1 and sys.argv[1] == "test":
        test_transfer_map()
    else:
        print("Eden Transfer Map")
        print("\nUsage:")
        print("  python3 transfer_map.py test    # Run tests")
        print("\nIntegration:")
        print("  from transfer_map import TransferMap, Concept")
        print("  tm = TransferMap()")
        print("  tm.learn_concept(my_concept)")
        print("  tm.transfer_to_domain('python:recursion', 'javascript')")


if __name__ == "__main__":
    main()
