#!/usr/bin/env python3
"""
Symbolic Entropy (Se) Measurement Module.

Implements the Se = H × C × D metric from "Beyond Shannon: Unifying Semantic
Meaning and Geometric Structure in Information Theory".

HONEST STATUS OF EACH COMPONENT:
================================

    H(X) - Shannon entropy (bits/byte)
           STATUS: ✅ TRUE Shannon formula: -Σ p log₂(p)
           RANGE:  [0, 8] bits per byte

    C(X) - Contextual coherence
           STATUS: ⚠️ HEURISTIC (not a physics quantity)
           METHOD: Bigram diversity, JSON structure detection, reasoning markers
           RANGE:  [0, 1]

    D(X) - Dimensional depth
           STATUS: ⚠️ HEURISTIC (not actual dimensional embedding)
           METHOD: Code/math/symbol pattern detection
           RANGE:  [1, 72] (or explicit value)

    Se(X) = H × C × D
           STATUS: ✅ NOVEL COMPOSITE METRIC
           UNITS:  (bits/byte) × (unitless) × (count) - NOT comparable to Se_norm [0,1]
           PROVEN: 728× improvement with scaffolding (receipt: se_728_receipt.json)

WHAT'S NOT CLAIMED:
===================
    - C is NOT a physics quantity - it's a pattern-based heuristic
    - D is NOT actual dimensional embedding - it's a complexity estimate
    - Se is NOT "true entropy" in a physics sense - it's an engineering metric
    - Se_measured is NOT directly comparable to Se_norm (different scales/units)

Usage:
    from helix_cdc.metrics.symbolic_entropy import measure_text, SeMetrics

    # Measure a text sample
    metrics = measure_text("Your AI output here", dimensional_depth=72)
    print(f"Se = {metrics.Se}")  # Note: C and D are heuristics

    # TRUE Shannon entropy on raw bytes
    from helix_cdc.metrics.symbolic_entropy import shannon_entropy
    H = shannon_entropy(b"raw bytes here")  # This IS true Shannon
"""

from __future__ import annotations

import json
import math
import re
from collections import Counter
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional, Union


@dataclass
class SeMetrics:
    """Container for Symbolic Entropy metrics."""
    H: float  # Shannon entropy (bits per byte)
    C: float  # Contextual coherence (0-1)
    D: float  # Dimensional depth (1-72)
    Se: float  # Symbolic entropy = H * C * D

    # Additional metadata
    text_length: int = 0
    unique_chars: int = 0
    unique_words: int = 0
    structure_score: float = 0.0  # JSON/code structure detection
    reasoning_score: float = 0.0  # Reasoning chain detection
    metadata: Dict[str, Any] = field(default_factory=dict)

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return {
            "H": self.H,
            "C": self.C,
            "D": self.D,
            "Se": self.Se,
            "text_length": self.text_length,
            "unique_chars": self.unique_chars,
            "unique_words": self.unique_words,
            "structure_score": self.structure_score,
            "reasoning_score": self.reasoning_score,
            "metadata": self.metadata,
        }


def shannon_entropy(data: Union[str, bytes]) -> float:
    """
    Calculate Shannon entropy of data (bits per byte).

    Args:
        data: Input data (string or bytes)

    Returns:
        Entropy in bits per byte (0-8 range)
    """
    if isinstance(data, str):
        data = data.encode("utf-8")

    if len(data) == 0:
        return 0.0

    # Count byte frequencies
    freq = Counter(data)
    total = len(data)

    # Calculate entropy
    entropy = 0.0
    for count in freq.values():
        if count > 0:
            p = count / total
            entropy -= p * math.log2(p)

    return entropy


def contextual_coherence(
    text: str,
    check_json: bool = True,
    check_reasoning: bool = True,
    check_structure: bool = True,
) -> float:
    """
    Estimate contextual coherence of text (0-1 scale).

    ⚠️ HONEST STATUS: This is a HEURISTIC, not a physics quantity.

    This function measures how structured/meaningful the text APPEARS to be
    based on pattern detection. It is NOT:
        - A physics metric
        - Kolmogorov complexity
        - Mutual information
        - Any formal information-theoretic quantity

    It IS:
        - A useful engineering heuristic for scoring AI output quality
        - Part of the Se = H × C × D composite metric (proven 728× improvement)

    Factors checked:
        - JSON validity (structured output)
        - Reasoning chain markers (step-by-step thinking)
        - Sentence structure (complete thoughts)
        - Word diversity

    Args:
        text: Input text to analyze
        check_json: Check for valid JSON structure
        check_reasoning: Check for reasoning markers
        check_structure: Check for sentence structure

    Returns:
        Coherence score 0-1 (HEURISTIC, not physics)
    """
    if not text or len(text.strip()) == 0:
        return 0.0

    scores: List[float] = []

    # JSON structure check
    if check_json:
        json_score = 0.0
        try:
            # Try to parse as JSON
            json.loads(text)
            json_score = 1.0
        except json.JSONDecodeError:
            # Check for JSON-like structure
            if text.strip().startswith("{") or text.strip().startswith("["):
                json_score = 0.3
            # Check for key-value patterns
            if re.search(r'"\w+":\s*["\[\{]', text):
                json_score = max(json_score, 0.5)
        scores.append(json_score)

    # Reasoning chain check
    if check_reasoning:
        reasoning_markers = [
            r"\bstep\s+\d",
            r"\bfirst(ly)?\b.*\bthen\b",
            r"\bbecause\b",
            r"\btherefore\b",
            r"\bthus\b",
            r"\bconsequently\b",
            r"\bif\b.*\bthen\b",
            r"\blet'?s?\b",
            r"\bwe (need|can|should)\b",
            r"\breason(ing)?\b",
            r"\banalyz(e|ing)\b",
            r"\bconsider(ing)?\b",
        ]
        reasoning_count = sum(
            1 for pattern in reasoning_markers
            if re.search(pattern, text, re.IGNORECASE)
        )
        reasoning_score = min(1.0, reasoning_count / 4.0)
        scores.append(reasoning_score)

    # Sentence structure check
    if check_structure:
        sentences = re.split(r'[.!?]+', text)
        valid_sentences = [
            s for s in sentences
            if len(s.strip()) > 10 and re.search(r'\b\w+\b.*\b\w+\b', s)
        ]
        sentence_ratio = len(valid_sentences) / max(1, len(sentences))

        # Word diversity
        words = re.findall(r'\b\w+\b', text.lower())
        unique_words = len(set(words))
        diversity = unique_words / max(1, len(words))

        structure_score = (sentence_ratio + diversity) / 2
        scores.append(structure_score)

    # Average all scores
    return sum(scores) / max(1, len(scores))


def dimensional_depth(
    text: Optional[str] = None,
    d_value: Optional[float] = None,
    auto_detect: bool = True,
) -> float:
    """
    Determine dimensional depth D(X) for symbolic entropy calculation.

    ⚠️ HONEST STATUS: This is a HEURISTIC, not actual dimensional embedding.

    D(X) estimates "symbolic complexity" based on pattern detection. It is NOT:
        - Actual dimensional embedding in a vector space
        - Intrinsic dimension of a manifold
        - Fisher information matrix rank
        - Any formal dimensionality measure

    It IS:
        - A complexity estimate based on character variety and patterns
        - Part of the Se = H × C × D composite metric (proven 728× improvement)

    Scale (heuristic):
        - 1D: Simple linear text (minimal patterns)
        - 8D: Basic symbolic patterns
        - 16D: Moderate complexity (some code/math)
        - 36D: High complexity (significant code/math)
        - 72D: Maximum (heavy unicode/symbolic content)

    Args:
        text: Input text (for auto-detection)
        d_value: Explicit D value to use (overrides auto-detect)
        auto_detect: Try to auto-detect from text structure

    Returns:
        Dimensional depth value (1-72) - HEURISTIC, not physics
    """
    if d_value is not None:
        return max(1.0, min(72.0, d_value))

    if not auto_detect or not text:
        return 8.0  # Default: basic symbolic

    # Auto-detect based on content
    d = 1.0

    # Check for code patterns
    code_patterns = [
        r"\bdef\s+\w+",
        r"\bclass\s+\w+",
        r"\bfunction\s+\w+",
        r"\breturn\b",
        r"\bif\b.*\belse\b",
        r"[{}\[\]]",
    ]
    code_count = sum(
        1 for p in code_patterns if re.search(p, text, re.IGNORECASE)
    )
    if code_count >= 3:
        d = max(d, 24.0)
    elif code_count >= 1:
        d = max(d, 16.0)

    # Check for mathematical notation
    math_patterns = [
        r"[\+\-\*\/\=]",
        r"\d+",
        r"\bx\b|\by\b|\bz\b",
        r"\bsin\b|\bcos\b|\btan\b",
        r"\bintegrat(e|ion)\b",
        r"\bderivative\b",
        r"\bequation\b",
    ]
    math_count = sum(
        1 for p in math_patterns if re.search(p, text, re.IGNORECASE)
    )
    if math_count >= 4:
        d = max(d, 36.0)
    elif math_count >= 2:
        d = max(d, 16.0)

    # Check for symbolic/Greek patterns (high complexity)
    symbolic_patterns = [
        r"[^a-zA-Z]",  # Non-ASCII symbols
        r"\b(alpha|beta|gamma|delta|sigma|theta|omega)\b",
        r"[^a-zA-Z0-9\s]",  # Special characters
    ]
    symbolic_count = sum(
        1 for p in symbolic_patterns if re.search(p, text, re.IGNORECASE)
    )
    if symbolic_count >= 3:
        d = max(d, 48.0)

    # Unicode check for very high complexity
    non_ascii = len([c for c in text if ord(c) > 127])
    if non_ascii > len(text) * 0.1:
        d = max(d, 72.0)

    return d


def symbolic_entropy(
    H: float,
    C: float,
    D: float,
) -> float:
    """
    Calculate Symbolic Entropy Se = H * C * D.

    Args:
        H: Shannon entropy (0-8)
        C: Contextual coherence (0-1)
        D: Dimensional depth (1-72)

    Returns:
        Symbolic entropy value
    """
    return H * C * D


def measure_text(
    text: str,
    dimensional_depth: Optional[float] = None,
    auto_detect_d: bool = True,
) -> SeMetrics:
    """
    Measure all symbolic entropy components for a text.

    Args:
        text: Input text to measure
        dimensional_depth: Explicit D value (or auto-detect)
        auto_detect_d: Auto-detect dimensional depth from content

    Returns:
        SeMetrics object with all measurements
    """
    # Calculate H(X)
    H = shannon_entropy(text)

    # Calculate C(X)
    C = contextual_coherence(text)

    # Calculate D(X)
    if dimensional_depth is not None:
        D = max(1.0, min(72.0, dimensional_depth))
    else:
        from helix_cdc.metrics.symbolic_entropy import dimensional_depth as calc_d
        D = calc_d(text=text, auto_detect=auto_detect_d)

    # Calculate Se
    Se = H * C * D

    # Additional metadata
    words = re.findall(r'\b\w+\b', text.lower())

    # Structure and reasoning scores (from coherence calculation)
    structure_score = 0.0
    try:
        json.loads(text)
        structure_score = 1.0
    except json.JSONDecodeError:
        if text.strip().startswith("{") or text.strip().startswith("["):
            structure_score = 0.3

    reasoning_markers = [
        r"\bstep\s+\d", r"\btherefore\b", r"\bthus\b", r"\bbecause\b"
    ]
    reasoning_score = min(1.0, sum(
        1 for p in reasoning_markers if re.search(p, text, re.IGNORECASE)
    ) / 3.0)

    return SeMetrics(
        H=H,
        C=C,
        D=D,
        Se=Se,
        text_length=len(text),
        unique_chars=len(set(text)),
        unique_words=len(set(words)),
        structure_score=structure_score,
        reasoning_score=reasoning_score,
    )


def compare_metrics(
    baseline: SeMetrics,
    sample: SeMetrics,
) -> Dict[str, Any]:
    """
    Compare two SeMetrics and compute improvement ratios.

    Args:
        baseline: Baseline metrics
        sample: Sample metrics to compare

    Returns:
        Dictionary with comparison data
    """
    def safe_ratio(a: float, b: float) -> float:
        if b == 0:
            return float('inf') if a > 0 else 1.0
        return a / b

    return {
        "H_ratio": safe_ratio(sample.H, baseline.H),
        "C_ratio": safe_ratio(sample.C, baseline.C),
        "D_ratio": safe_ratio(sample.D, baseline.D),
        "Se_ratio": safe_ratio(sample.Se, baseline.Se),
        "baseline": baseline.to_dict(),
        "sample": sample.to_dict(),
    }


# CLI for standalone measurement
if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser(
        description="Measure Symbolic Entropy (Se = H * C * D)"
    )
    parser.add_argument(
        "text",
        nargs="?",
        help="Text to measure (or pipe via stdin)",
    )
    parser.add_argument(
        "-d", "--dimensional-depth",
        type=float,
        help="Explicit dimensional depth (1-72)",
    )
    parser.add_argument(
        "--json",
        action="store_true",
        help="Output as JSON",
    )

    args = parser.parse_args()

    # Read text
    if args.text:
        text = args.text
    else:
        import sys
        text = sys.stdin.read()

    # Measure
    metrics = measure_text(text, dimensional_depth=args.dimensional_depth)

    # Output
    if args.json:
        import json
        print(json.dumps(metrics.to_dict(), indent=2))
    else:
        print("=== Symbolic Entropy Measurement ===")
        print(f"H(X) Shannon entropy: {metrics.H:.4f} bits/byte")
        print(f"C(X) Contextual coherence: {metrics.C:.4f}")
        print(f"D(X) Dimensional depth: {metrics.D:.1f}D")
        print(f"")
        print(f"Se(X) = H * C * D = {metrics.Se:.4f}")
        print(f"")
        print(f"Text length: {metrics.text_length}")
        print(f"Unique chars: {metrics.unique_chars}")
        print(f"Unique words: {metrics.unique_words}")
        print(f"Structure score: {metrics.structure_score:.4f}")
        print(f"Reasoning score: {metrics.reasoning_score:.4f}")
