#!/usr/bin/env python3
"""
tools_symbolic.py - KRISPER + Bio_Poetica Adapters (WO#GINNIE_04)
==================================================================

Symbolic knowledge system adapters for Echo Ginnie:
- KRISPER scoring and suggestions
- Bio_Poetica expansion and parsing
- Symbolic entropy measurement
"""

import json
import re
import math
from pathlib import Path
from typing import Any, Dict, List, Optional
from collections import Counter

# Try to import actual implementations
try:
    from helix_cdc.advanced.bridge.krisper_bridge import KRISPERBridge
    KRISPER_AVAILABLE = True
except ImportError:
    KRISPER_AVAILABLE = False

try:
    import sys
    sys.path.insert(0, str(Path.home() / "helix-cdc/gitforest-repo/forest/krisper"))
    from bio_poetica import BioPoeticaParser
    BIO_POETICA_AVAILABLE = True
except ImportError:
    BIO_POETICA_AVAILABLE = False


def krisper_score(snippet: str) -> Dict[str, Any]:
    """
    Score a code snippet using KRISPER metrics.

    Evaluates:
    - Clarity (naming, structure)
    - Symmetry (balanced patterns)
    - Reuse potential
    - Helix alignment

    Args:
        snippet: Code or text to score

    Returns:
        Scores and suggestions
    """
    if not snippet:
        return {"status": "error", "message": "Empty snippet"}

    # Calculate metrics
    lines = snippet.split("\n")
    non_empty_lines = [l for l in lines if l.strip()]

    # Clarity: shorter names, less nesting
    avg_line_len = sum(len(l) for l in non_empty_lines) / max(1, len(non_empty_lines))
    max_indent = max((len(l) - len(l.lstrip())) for l in lines) if lines else 0
    clarity = max(0, 1.0 - (avg_line_len / 100) - (max_indent / 20))

    # Symmetry: balanced brackets, consistent patterns
    opens = snippet.count("(") + snippet.count("{") + snippet.count("[")
    closes = snippet.count(")") + snippet.count("}") + snippet.count("]")
    bracket_balance = 1.0 - min(1.0, abs(opens - closes) / max(1, opens + closes))

    # Pattern repetition (good = DRY)
    words = re.findall(r'\b\w+\b', snippet.lower())
    word_freq = Counter(words)
    common_patterns = sum(1 for w, c in word_freq.items() if c > 2 and len(w) > 3)
    reuse = min(1.0, common_patterns / 10)

    # Helix alignment: uses helix/jazz/krisper concepts
    helix_terms = ["helix", "jazz", "krisper", "fibpi", "valley", "wave", "seed", "regen"]
    helix_hits = sum(1 for term in helix_terms if term in snippet.lower())
    helix_alignment = min(1.0, helix_hits / 3)

    # Overall score
    overall = (clarity * 0.3 + bracket_balance * 0.2 + reuse * 0.2 + helix_alignment * 0.3)

    return {
        "status": "ok",
        "scores": {
            "clarity": round(clarity, 3),
            "symmetry": round(bracket_balance, 3),
            "reuse": round(reuse, 3),
            "helix_alignment": round(helix_alignment, 3),
            "overall": round(overall, 3),
        },
        "metrics": {
            "lines": len(non_empty_lines),
            "avg_line_len": round(avg_line_len, 1),
            "max_indent": max_indent,
            "helix_terms_found": helix_hits,
        },
        "krisper_available": KRISPER_AVAILABLE,
    }


def krisper_suggest(snippet: str) -> Dict[str, Any]:
    """
    Get KRISPER suggestions for improving code.

    Args:
        snippet: Code to analyze

    Returns:
        List of suggestions
    """
    if not snippet:
        return {"status": "error", "message": "Empty snippet"}

    suggestions = []

    # Check for common issues
    lines = snippet.split("\n")

    # Long lines
    long_lines = [i + 1 for i, l in enumerate(lines) if len(l) > 100]
    if long_lines:
        suggestions.append({
            "type": "clarity",
            "message": f"Lines {long_lines[:3]} exceed 100 chars",
            "severity": "minor",
        })

    # Deep nesting
    max_indent = max((len(l) - len(l.lstrip())) for l in lines if l.strip())
    if max_indent > 16:
        suggestions.append({
            "type": "complexity",
            "message": f"Deep nesting detected (indent={max_indent})",
            "severity": "major",
        })

    # Missing docstrings for functions
    if "def " in snippet and '"""' not in snippet and "'''" not in snippet:
        suggestions.append({
            "type": "documentation",
            "message": "Functions should have docstrings",
            "severity": "minor",
        })

    # No Helix patterns
    helix_terms = ["helix", "jazz", "krisper", "fibpi", "valley", "wave"]
    if not any(term in snippet.lower() for term in helix_terms):
        suggestions.append({
            "type": "helix_alignment",
            "message": "Consider using Helix patterns (Jazz timing, FibPi3D, etc.)",
            "severity": "info",
        })

    # Hardcoded paths
    if "/home/" in snippet or "C:\\" in snippet:
        suggestions.append({
            "type": "portability",
            "message": "Avoid hardcoded absolute paths",
            "severity": "major",
        })

    return {
        "status": "ok",
        "suggestion_count": len(suggestions),
        "suggestions": suggestions,
    }


def bio_poetica_expand(concept: str) -> Dict[str, Any]:
    """
    Expand a concept using Bio_Poetica symbolic patterns.

    Args:
        concept: Concept or phrase to expand

    Returns:
        Expanded symbolic representation
    """
    if not concept:
        return {"status": "error", "message": "Empty concept"}

    # Basic expansion patterns
    expansions = []

    # Split into words
    words = concept.lower().split()

    # Generate poetic expansions
    for word in words:
        if word in ["compress", "encode", "pack"]:
            expansions.append(f"when {word} arrives:\n  fold into crystal lattice\n  emit completion")
        elif word in ["read", "load", "fetch"]:
            expansions.append(f"when {word} requested:\n  lift from substrate\n  grow into memory")
        elif word in ["write", "save", "store"]:
            expansions.append(f"when {word} commanded:\n  pack essence as seed\n  remember in vault")
        elif word in ["run", "execute", "fire"]:
            expansions.append(f"when {word} triggered:\n  wait for valley\n  release on wave")

    if not expansions:
        # Generic expansion
        expansions.append(f"when {concept}:\n  observe the flow\n  act with intention\n  emit trace")

    return {
        "status": "ok",
        "concept": concept,
        "expansions": expansions,
        "bio_poetica_available": BIO_POETICA_AVAILABLE,
    }


def bio_poetica_parse(poem: str) -> Dict[str, Any]:
    """
    Parse a Bio_Poetica poem into structured elements.

    Args:
        poem: Poem text to parse

    Returns:
        Parsed elements
    """
    if not poem:
        return {"status": "error", "message": "Empty poem"}

    if BIO_POETICA_AVAILABLE:
        try:
            parser = BioPoeticaParser()
            elements = parser.parse(poem)
            return {
                "status": "ok",
                "element_count": len(elements),
                "elements": [{"type": e.type, "content": e.content} for e in elements],
            }
        except Exception as e:
            pass  # Fall through to basic parsing

    # Basic parsing
    elements = []
    lines = poem.split("\n")

    patterns = {
        "when": "trigger",
        "emit": "action",
        "remember": "state",
        "grow": "transform",
        "lift": "elevate",
        "pack": "compress",
    }

    for line in lines:
        stripped = line.strip()
        if not stripped:
            continue

        element_type = "text"
        for keyword, etype in patterns.items():
            if stripped.lower().startswith(keyword):
                element_type = etype
                break

        elements.append({
            "type": element_type,
            "content": stripped,
        })

    return {
        "status": "ok",
        "element_count": len(elements),
        "elements": elements,
    }


def measure_symbolic_entropy(text: str) -> Dict[str, Any]:
    """
    Measure symbolic entropy (Se = H x C x D).

    Args:
        text: Text to measure

    Returns:
        Entropy metrics
    """
    if not text:
        return {"status": "error", "message": "Empty text"}

    # H(X) - Shannon entropy
    char_freq = Counter(text)
    total_chars = len(text)
    h_x = 0.0
    for count in char_freq.values():
        p = count / total_chars
        if p > 0:
            h_x -= p * math.log2(p)

    # C(X) - Contextual coherence (simplified)
    # Higher if structured patterns present
    words = re.findall(r'\b\w+\b', text.lower())
    unique_words = len(set(words))
    total_words = len(words)
    vocabulary_ratio = unique_words / max(1, total_words)

    # Check for structural markers
    structure_markers = ["def ", "class ", "if ", "for ", "when ", "emit "]
    structure_hits = sum(1 for m in structure_markers if m in text)
    c_x = min(1.0, (0.3 + vocabulary_ratio * 0.3 + structure_hits * 0.1))

    # D(X) - Dimensional depth (simplified)
    # Based on symbolic density
    symbol_chars = sum(1 for c in text if c in "{}[]()<>@#$%^&*")
    greek_pattern = re.findall(r'[α-ωΑ-Ω]', text)
    math_pattern = re.findall(r'[∑∫∂∇×÷±∞≈≠≤≥]', text)

    d_x = 1.0 + len(greek_pattern) * 2 + len(math_pattern) * 3 + symbol_chars * 0.1
    d_x = min(72.0, d_x)  # Cap at 72D

    # Se = H x C x D
    se = h_x * c_x * d_x

    return {
        "status": "ok",
        "metrics": {
            "H_x": round(h_x, 3),
            "C_x": round(c_x, 3),
            "D_x": round(d_x, 1),
            "Se": round(se, 2),
        },
        "interpretation": {
            "entropy_level": "high" if h_x > 4 else "medium" if h_x > 2 else "low",
            "coherence": "structured" if c_x > 0.5 else "mixed" if c_x > 0.3 else "unstructured",
            "dimensions": f"{d_x:.0f}D",
        },
    }


# Tool registry
SYMBOLIC_TOOLS = {
    "krisper_score": krisper_score,
    "krisper_suggest": krisper_suggest,
    "bio_poetica_expand": bio_poetica_expand,
    "bio_poetica_parse": bio_poetica_parse,
    "symbolic_entropy": measure_symbolic_entropy,
}


def get_symbolic_tool(name: str):
    """Get a symbolic tool by name."""
    return SYMBOLIC_TOOLS.get(name)


def list_symbolic_tools() -> List[str]:
    """List available symbolic tools."""
    return list(SYMBOLIC_TOOLS.keys())
