#!/usr/bin/env python3
"""
Language Shell - Semantic Communication Layer
Standalone version for cubesquare package.

HEX_ID: 4C414E47 (LANG)

Bridges machine ideas to human-readable language:
- DATA (patterns) -> LANGUAGE (human-readable sentences)
- IDEAS (machine) -> COMMUNICATION (visual + text)
- I-Logic states -> VOCABULARY (word choice and tone)

Components:
    1. SEMANTIC VOCABULARY - Word/phrase templates for each I-Logic state
    2. IDEA TEMPLATES - Convert idea types to sentences
    3. VISUAL MATRIX - Display ideas in spatial grid
    4. ACCEPTANCE HANDLER - Execute actions on idea acceptance

Commands:
    status     - Show language shell status
    vocabulary - Show semantic vocabulary by I-Logic state
    translate  - Translate idea to sentence
    propose    - Propose an idea using hex protocol
    accept     - Accept idea by ID
    reject     - Reject idea by ID
    query      - Query idea details
    matrix     - Show visual idea matrix
"""

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


# =============================================================================
# CONSTANTS
# =============================================================================

PHI = 1.618033988749895
PRIME_131 = 131

# =============================================================================
# SEMANTIC VOCABULARY - Words by I-Logic state
# =============================================================================

VOCABULARY = {
    'DIRECT': {
        'verbs': ['preserve', 'maintain', 'keep', 'continue', 'sustain'],
        'adjectives': ['stable', 'consistent', 'reliable', 'steady'],
        'tone': 'assertive',
        'emotion': 'confidence',
    },
    'INVERSE': {
        'verbs': ['reverse', 'undo', 'disconnect', 'separate', 'remove'],
        'adjectives': ['isolated', 'disconnected', 'orphaned', 'missing'],
        'tone': 'cautionary',
        'emotion': 'concern',
    },
    'SPIRAL': {
        'verbs': ['expand', 'elevate', 'abstract', 'generalize', 'grow'],
        'adjectives': ['expanding', 'growing', 'hierarchical', 'layered'],
        'tone': 'enthusiastic',
        'emotion': 'excitement',
    },
    'ANTI_SPIRAL': {
        'verbs': ['consolidate', 'merge', 'unify', 'combine', 'focus'],
        'adjectives': ['clustered', 'grouped', 'related', 'similar'],
        'tone': 'focused',
        'emotion': 'determination',
    },
    'ABSORBED': {
        'verbs': ['observe', 'note', 'record', 'acknowledge', 'accept'],
        'adjectives': ['neutral', 'balanced', 'centered', 'passive'],
        'tone': 'neutral',
        'emotion': 'calm',
    },
}

# Idea type to I-Logic mapping
IDEA_I_LOGIC = {
    'ELEVATE': 'SPIRAL',
    'SPECIALIZE': 'ANTI_SPIRAL',
    'BRIDGE': 'INVERSE',
    'CONSOLIDATE': 'ANTI_SPIRAL',
    'SPLIT': 'INVERSE',
    'HEAL': 'DIRECT',
}

# =============================================================================
# IDEA TEMPLATES
# =============================================================================

IDEA_TEMPLATES = {
    'ELEVATE': {
        'short': "Create parent interface for {prefix}* ({count} items)",
        'long': """
RECOMMENDATION: Elevate {prefix}* to a common abstraction

WHAT: {count} files share the hex prefix {prefix}, indicating they belong
      to the same conceptual family.

WHY:  Without a parent interface, changes must be made to each file
      individually. This violates DRY and increases maintenance burden.

HOW:  1. Identify common methods/properties across the {count} files
      2. Create abstract base class or interface at {prefix}_base.py
      3. Have each file inherit from the new base
      4. Move shared logic to the base class

FILES AFFECTED: {files}

CONFIDENCE: {confidence}%
""",
        'action': "Create {prefix}_base.py with shared interface",
    },
    'BRIDGE': {
        'short': "Connect isolated node {hex_id}",
        'long': """
RECOMMENDATION: Bridge {hex_id} to ecosystem

WHAT: Node {hex_id} has no connections to other nodes in the ecosystem.
      It exists in isolation, which may indicate:
      - Orphaned code (unused)
      - Missing imports/dependencies
      - New code not yet integrated

WHY:  Isolated nodes are harder to discover, test, and maintain.
      They may become stale or inconsistent with the rest of the system.

HOW:  1. Review {file_path} to understand its purpose
      2. Identify which other nodes should reference it
      3. Add import/dependency edges OR
      4. If truly unused, consider removal

CATEGORY: {from_cat} -> {to_cat}

CONFIDENCE: {confidence}%
""",
        'action': "Add relationship edge from {hex_id} to related nodes",
    },
    'CONSOLIDATE': {
        'short': "Merge {count} related tokens at bucket {bucket}",
        'long': """
RECOMMENDATION: Consolidate cluster at Morton bucket {bucket}

WHAT: {count} tokens cluster together at Morton bucket {bucket},
      indicating strong spatial/semantic proximity.

WHY:  Clustered tokens often represent related concepts that could
      benefit from a unified abstraction or shared utility.

HOW:  1. Review the {count} tokens in this cluster
      2. Identify common patterns or shared logic
      3. Create a consolidating module or utility
      4. Refactor individual tokens to use shared code

TOKENS: {tokens}

CONFIDENCE: {confidence}%
""",
        'action': "Create shared utility for cluster at bucket {bucket}",
    },
    'SPECIALIZE': {
        'short': "Specialize deep hierarchy at {prefix}",
        'long': """
RECOMMENDATION: Specialize hierarchy at {prefix}

WHAT: Deep hierarchy detected at prefix {prefix} (depth {depth}).
      This suggests a well-developed abstraction that may benefit
      from more specific implementations.

WHY:  Deep hierarchies sometimes indicate over-abstraction.
      Consider if all levels are necessary or if some can be
      collapsed or specialized for specific use cases.

HOW:  1. Review the hierarchy structure
      2. Identify if intermediate levels add value
      3. Consider creating specialized implementations
      4. Flatten unnecessary abstraction layers

DEPTH: {depth} levels
CHILDREN: {count}

CONFIDENCE: {confidence}%
""",
        'action': "Review and potentially flatten hierarchy at {prefix}",
    },
    'HEAL': {
        'short': "Fix issue at {hex_id}",
        'long': """
RECOMMENDATION: Heal {hex_id}

WHAT: An issue has been detected at {hex_id}.

WHY:  The system detected a condition requiring healing intervention.

HOW:  Apply INVERSE x INVERSE = DIRECT transformation.

CONFIDENCE: {confidence}%
""",
        'action': "Apply healing transformation to {hex_id}",
    },
}

# ANSI colors for I-Logic states
I_LOGIC_COLORS = {
    'DIRECT': '\033[94m',      # Blue (+1)
    'INVERSE': '\033[91m',     # Red (-1)
    'SPIRAL': '\033[92m',      # Green (+i)
    'ANTI_SPIRAL': '\033[93m', # Yellow (-i)
    'ABSORBED': '\033[90m',    # Gray (0)
    'RESET': '\033[0m',
}


# =============================================================================
# DATA CLASSES
# =============================================================================

@dataclass
class AcceptedIdea:
    """Record of an accepted idea."""
    idea_id: str
    idea_type: str
    action_taken: str
    accepted_at: datetime
    result: str = ""
    success: bool = False


# =============================================================================
# LANGUAGE SHELL
# =============================================================================

class LanguageShell:
    """
    Language Shell - Semantic Communication Layer.

    Converts machine ideas to human language and handles idea acceptance.

    Commands:
        status     - Show language shell status
        vocabulary - Show semantic vocabulary
        translate  - Translate idea to sentence
        propose    - Propose idea
        accept     - Accept idea
        reject     - Reject idea
        query      - Query idea details
        matrix     - Show visual matrix
    """

    def __init__(self):
        """Initialize language shell."""
        self.hex_id = '4C414E47'
        self.vocabulary = VOCABULARY
        self.templates = IDEA_TEMPLATES
        self.accepted_ideas: List[AcceptedIdea] = []
        self.pending_ideas: Dict[str, Dict] = {}

    def _idea_to_sentence(self, idea: Dict, verbose: bool = False) -> str:
        """Convert idea to human-readable sentence."""
        idea_type = idea.get('type', 'UNKNOWN')
        template = self.templates.get(idea_type, {})

        if verbose and 'long' in template:
            text = template['long']
        elif 'short' in template:
            text = template['short']
        else:
            text = f"Idea: {idea_type} - {idea.get('title', 'No title')}"

        # Fill template
        text = text.format(
            prefix=idea.get('prefix', '??'),
            count=idea.get('count', 0),
            confidence=int(idea.get('confidence', 0) * 100),
            files=idea.get('files', 'N/A'),
            hex_id=idea.get('hex_id', '????????'),
            file_path=idea.get('file_path', 'unknown'),
            from_cat=idea.get('from_cat', '?'),
            to_cat=idea.get('to_cat', '?'),
            bucket=idea.get('bucket', 0),
            tokens=idea.get('tokens', []),
            depth=idea.get('depth', 0),
        )

        return text.strip()

    def _get_ilogic_vocab(self, idea_type: str) -> Dict:
        """Get vocabulary for idea type's I-Logic state."""
        ilogic = IDEA_I_LOGIC.get(idea_type, 'ABSORBED')
        return self.vocabulary.get(ilogic, self.vocabulary['ABSORBED'])

    def status(self, as_json: bool = False) -> Dict[str, Any]:
        """Get language shell status."""
        result = {
            'hex_id': self.hex_id,
            'system': 'Language Shell',
            'version': '1.0.0',
            'protocol': 'CG:HEX|V1|AGENT:4C414E47',
            'vocabulary_states': len(self.vocabulary),
            'idea_templates': len(self.templates),
            'pending_ideas': len(self.pending_ideas),
            'accepted_ideas': len(self.accepted_ideas),
            'commands': {
                '+I:ID': 'Propose idea',
                '!I:ID:OK': 'Accept idea (triggers action)',
                '!I:ID:NO': 'Reject idea',
                '?I:ID': 'Query idea details',
            },
        }

        if as_json:
            return json.dumps(result, indent=2)
        return result

    def vocabulary_info(self, state: Optional[str] = None, as_json: bool = False) -> Dict[str, Any]:
        """Get vocabulary information, optionally for specific I-Logic state."""
        if state:
            state = state.upper()
            if state in self.vocabulary:
                result = {
                    'state': state,
                    'vocabulary': self.vocabulary[state],
                }
            else:
                result = {'error': f'Unknown state: {state}', 'available': list(self.vocabulary.keys())}
        else:
            result = {
                'states': list(self.vocabulary.keys()),
                'vocabulary': self.vocabulary,
            }

        if as_json:
            return json.dumps(result, indent=2)
        return result

    def translate(self, idea: Dict, verbose: bool = False, as_json: bool = False) -> Dict[str, Any]:
        """Translate idea to human-readable sentence."""
        sentence = self._idea_to_sentence(idea, verbose)
        idea_type = idea.get('type', 'UNKNOWN')
        ilogic = IDEA_I_LOGIC.get(idea_type, 'ABSORBED')
        vocab = self._get_ilogic_vocab(idea_type)

        result = {
            'idea_type': idea_type,
            'ilogic': ilogic,
            'tone': vocab.get('tone', 'neutral'),
            'emotion': vocab.get('emotion', 'calm'),
            'sentence': sentence,
        }

        if as_json:
            return json.dumps(result, indent=2)
        return result

    def propose(self, idea: Dict, as_json: bool = False) -> Dict[str, Any]:
        """Propose an idea using hex protocol."""
        idea_id = idea.get('id', f"IDEA-{datetime.now().strftime('%Y%m%d%H%M%S')}")
        idea_type = idea.get('type', 'UNKNOWN')

        # Store for later acceptance
        self.pending_ideas[idea_id] = idea

        # Get vocabulary
        ilogic = IDEA_I_LOGIC.get(idea_type, 'ABSORBED')
        vocab = self._get_ilogic_vocab(idea_type)
        verb = vocab.get('verbs', ['do'])[0]

        # Short summary
        summary = self._idea_to_sentence(idea, verbose=False)

        result = {
            'proposal': f'+I:{idea_id}|{idea_type}|{summary}',
            'idea_id': idea_id,
            'idea_type': idea_type,
            'ilogic': ilogic,
            'verb': verb,
            'summary': summary,
            'pending': True,
        }

        if as_json:
            return json.dumps(result, indent=2)
        return result

    def accept(self, idea_id: str, as_json: bool = False) -> Dict[str, Any]:
        """Accept an idea and trigger action."""
        if idea_id not in self.pending_ideas:
            result = {'error': f'Idea not found: {idea_id}', 'response': f'!I:{idea_id}:ERR|Idea not found'}
            if as_json:
                return json.dumps(result, indent=2)
            return result

        idea = self.pending_ideas[idea_id]
        idea_type = idea.get('type', 'UNKNOWN')
        template = self.templates.get(idea_type, {})

        # Get action
        action = template.get('action', 'No action defined')
        action = action.format(
            prefix=idea.get('prefix', '??'),
            hex_id=idea.get('hex_id', '????????'),
            bucket=idea.get('bucket', 0),
        )

        # Record acceptance
        accepted = AcceptedIdea(
            idea_id=idea_id,
            idea_type=idea_type,
            action_taken=action,
            accepted_at=datetime.now(),
            result="Queued for implementation",
            success=True,
        )
        self.accepted_ideas.append(accepted)
        del self.pending_ideas[idea_id]

        result = {
            'response': f'!I:{idea_id}:OK|ACTION: {action}',
            'idea_id': idea_id,
            'action': action,
            'success': True,
        }

        if as_json:
            return json.dumps(result, indent=2)
        return result

    def reject(self, idea_id: str, reason: str = "deferred", as_json: bool = False) -> Dict[str, Any]:
        """Reject an idea with reason."""
        if idea_id in self.pending_ideas:
            del self.pending_ideas[idea_id]

        result = {
            'response': f'!I:{idea_id}:NO:{reason}',
            'idea_id': idea_id,
            'reason': reason,
        }

        if as_json:
            return json.dumps(result, indent=2)
        return result

    def query(self, idea_id: str, as_json: bool = False) -> Dict[str, Any]:
        """Get detailed explanation of idea."""
        if idea_id not in self.pending_ideas:
            result = {'error': f'Idea not found: {idea_id}', 'response': f'?I:{idea_id}:ERR|Idea not found'}
            if as_json:
                return json.dumps(result, indent=2)
            return result

        idea = self.pending_ideas[idea_id]
        detailed = self._idea_to_sentence(idea, verbose=True)
        idea_type = idea.get('type', 'UNKNOWN')
        ilogic = IDEA_I_LOGIC.get(idea_type, 'ABSORBED')

        result = {
            'response': f'?I:{idea_id}:K8',
            'idea_id': idea_id,
            'idea_type': idea_type,
            'ilogic': ilogic,
            'detailed': detailed,
        }

        if as_json:
            return json.dumps(result, indent=2)
        return result

    def matrix(self, ideas: List[Dict], width: int = 48, height: int = 12, as_json: bool = False) -> Dict[str, Any]:
        """Generate visual matrix representation of ideas."""
        # Symbol mapping
        symbols = {
            'ELEVATE': '^',
            'BRIDGE': '=',
            'CONSOLIDATE': '+',
            'SPECIALIZE': 'v',
            'SPLIT': '/',
            'HEAL': '*',
        }

        # Build grid info
        grid_items = []
        for i, idea in enumerate(ideas[:width * height]):
            idea_hash = hash(idea.get('id', str(i)))
            x = abs(idea_hash) % width
            y = abs(idea_hash >> 8) % height
            idea_type = idea.get('type', 'UNKNOWN')
            ilogic = IDEA_I_LOGIC.get(idea_type, 'ABSORBED')
            symbol = symbols.get(idea_type, '?')

            grid_items.append({
                'x': x,
                'y': y,
                'idea_id': idea.get('id', f'IDEA-{i}'),
                'type': idea_type,
                'ilogic': ilogic,
                'symbol': symbol,
            })

        result = {
            'width': width,
            'height': height,
            'idea_count': len(ideas),
            'displayed': len(grid_items),
            'legend': symbols,
            'color_map': {
                'DIRECT': 'Blue (+1)',
                'INVERSE': 'Red (-1)',
                'SPIRAL': 'Green (+i)',
                'ANTI_SPIRAL': 'Yellow (-i)',
                'ABSORBED': 'Gray (0)',
            },
            'grid_items': grid_items,
        }

        if as_json:
            return json.dumps(result, indent=2)
        return result

    def run(self, command: str, *args, as_json: bool = False) -> Any:
        """Run a language shell command."""
        commands = {
            'status': lambda: self.status(as_json),
            'vocabulary': lambda: self.vocabulary_info(args[0] if args else None, as_json),
            'translate': lambda: self.translate(args[0], as_json=as_json) if args else {'error': 'Idea dict required'},
            'propose': lambda: self.propose(args[0], as_json) if args else {'error': 'Idea dict required'},
            'accept': lambda: self.accept(args[0], as_json) if args else {'error': 'Idea ID required'},
            'reject': lambda: self.reject(args[0], args[1] if len(args) > 1 else 'deferred', as_json) if args else {'error': 'Idea ID required'},
            'query': lambda: self.query(args[0], as_json) if args else {'error': 'Idea ID required'},
            'matrix': lambda: self.matrix(args[0] if args else [], as_json=as_json),
        }

        if command in commands:
            return commands[command]()
        else:
            return {'error': f'Unknown command: {command}', 'available': list(commands.keys())}
