#!/usr/bin/env python3
"""
Bio-Symbolic Entropy (Se_bio) - Tuned for Biological Signal Detection

The original symbolic_entropy.py measures AI text output (Greek, equations, vows).
This module engineers Se = H * C * D for BIOLOGICAL mutation analysis.

Components (bio-tuned):
    H(X) - Shannon entropy of Helix64 manifold (information density)
    C_bio(X) - Biological coherence: pattern regularity, compression structure
    D_bio(X) - Biological depth: position criticality, structural context

The key insight: In biology, we care about:
- Position sensitivity (DNA-contact, core, surface)
- Physicochemical disruption patterns
- Compression behavior (regular vs chaotic)
- Hotspot signatures

NOT about:
- JSON structure
- Reasoning markers ("therefore", "because")
- Sentence patterns
"""

import math
import hashlib
from dataclasses import dataclass, field
from typing import Dict, Any, List, Tuple, Optional
from collections import Counter


@dataclass
class BioSeMetrics:
    """Container for Bio-Symbolic Entropy metrics."""
    H: float          # Shannon entropy (bits per byte)
    C_bio: float      # Biological coherence (0-1)
    D_bio: float      # Biological depth (1-72)
    Se_bio: float     # Bio-symbolic entropy = H * C_bio * D_bio

    # Component breakdowns
    compression_regularity: float = 0.0  # How compressible is the pattern?
    run_structure: float = 0.0           # Run-length pattern score
    delta_variance: float = 0.0          # Variance in byte deltas
    position_criticality: float = 0.0    # Based on known annotations

    metadata: Dict[str, Any] = field(default_factory=dict)

    def to_dict(self) -> Dict[str, Any]:
        return {
            "H": self.H,
            "C_bio": self.C_bio,
            "D_bio": self.D_bio,
            "Se_bio": self.Se_bio,
            "compression_regularity": self.compression_regularity,
            "run_structure": self.run_structure,
            "delta_variance": self.delta_variance,
            "position_criticality": self.position_criticality,
            "metadata": self.metadata,
        }


def shannon_entropy_bytes(data: bytes) -> float:
    """Shannon entropy of raw bytes (0-8 bits/byte)."""
    if len(data) == 0:
        return 0.0
    freq = Counter(data)
    total = len(data)
    entropy = 0.0
    for count in freq.values():
        if count > 0:
            p = count / total
            entropy -= p * math.log2(p)
    return entropy


def compression_regularity(data: bytes) -> float:
    """
    Measure how "regular" the byte pattern is (0-1).

    Regular patterns (low entropy, repetitive) = high score
    Chaotic patterns (high entropy, random) = low score

    For mutations: regular patterns suggest the position tolerates
    variation; chaotic patterns suggest disruption.
    """
    if len(data) < 2:
        return 0.5

    # Method 1: Run-length analysis
    runs = []
    current_run = 1
    for i in range(1, len(data)):
        if data[i] == data[i-1]:
            current_run += 1
        else:
            runs.append(current_run)
            current_run = 1
    runs.append(current_run)

    # Average run length (longer runs = more regular)
    avg_run = sum(runs) / len(runs)
    run_score = min(1.0, avg_run / 4.0)  # Normalize, cap at 4

    # Method 2: Byte frequency concentration
    freq = Counter(data)
    max_freq = max(freq.values())
    concentration = max_freq / len(data)  # How dominant is the top byte?

    # Method 3: Delta variance (low variance = regular pattern)
    deltas = [(data[i] - data[i-1]) & 0xFF for i in range(1, len(data))]
    if deltas:
        mean_delta = sum(deltas) / len(deltas)
        variance = sum((d - mean_delta)**2 for d in deltas) / len(deltas)
        # Normalize: high variance = low regularity
        delta_score = 1.0 / (1.0 + math.sqrt(variance) / 50.0)
    else:
        delta_score = 0.5

    # Combine signals
    regularity = (run_score * 0.3 + concentration * 0.3 + delta_score * 0.4)
    return regularity


def run_structure_score(data: bytes) -> float:
    """
    Detect run-length structure in the data.

    High score = data has clear run patterns (suggests structural motif)
    Low score = data is scattered (suggests disruption)
    """
    if len(data) < 4:
        return 0.5

    runs = []
    current_byte = data[0]
    current_run = 1

    for i in range(1, len(data)):
        if data[i] == current_byte:
            current_run += 1
        else:
            runs.append((current_byte, current_run))
            current_byte = data[i]
            current_run = 1
    runs.append((current_byte, current_run))

    # Score based on run distribution
    run_lengths = [r[1] for r in runs]

    if not run_lengths:
        return 0.5

    # Variance in run lengths (consistent runs = higher score)
    mean_run = sum(run_lengths) / len(run_lengths)
    if len(run_lengths) > 1:
        run_var = sum((r - mean_run)**2 for r in run_lengths) / len(run_lengths)
        consistency = 1.0 / (1.0 + math.sqrt(run_var))
    else:
        consistency = 1.0

    # Bonus for longer average runs
    length_bonus = min(1.0, mean_run / 3.0)

    return (consistency * 0.6 + length_bonus * 0.4)


def delta_variance_score(data: bytes) -> float:
    """
    Measure variance in byte-to-byte deltas.

    Low variance = smooth transitions = structured
    High variance = jumpy = potentially disruptive

    Returns inverted score: high disruption = high score (we want to FLAG disruptions)
    """
    if len(data) < 2:
        return 0.5

    deltas = [(data[i] - data[i-1]) & 0xFF for i in range(1, len(data))]

    # Handle wrap-around (e.g., 250 -> 10 is really delta of 16, not 240)
    adjusted_deltas = []
    for d in deltas:
        if d > 128:
            d = 256 - d
        adjusted_deltas.append(d)

    mean_delta = sum(adjusted_deltas) / len(adjusted_deltas)
    variance = sum((d - mean_delta)**2 for d in adjusted_deltas) / len(adjusted_deltas)

    # Higher variance = more "disruptive" pattern = higher score
    # Normalize to 0-1 range
    disruption_score = min(1.0, math.sqrt(variance) / 64.0)

    return disruption_score


def bio_contextual_coherence(
    data: bytes,
    position: int = 0,
    hotspots: set = None,
    dna_contact: set = None,
    core_residues: set = None,
) -> Tuple[float, Dict[str, float]]:
    """
    Biological coherence C_bio(X) - replaces text-based C(X).

    Measures:
    1. Compression regularity (pattern structure)
    2. Run structure (motif detection)
    3. Delta variance (disruption signature)
    4. Position context (hotspot/DNA-contact bonus)

    Returns:
        (C_bio score, component breakdown dict)
    """
    hotspots = hotspots or set()
    dna_contact = dna_contact or set()
    core_residues = core_residues or set()

    # Component 1: Compression regularity
    comp_reg = compression_regularity(data)

    # Component 2: Run structure
    run_struct = run_structure_score(data)

    # Component 3: Delta variance (inverted - high variance = low coherence)
    delta_var = delta_variance_score(data)
    coherence_from_delta = 1.0 - delta_var  # Flip: low variance = high coherence

    # Component 4: Position criticality bonus
    position_bonus = 0.0
    if position in hotspots:
        position_bonus = 0.3  # Hotspots get coherence PENALTY (more "disruptive")
    elif position in dna_contact:
        position_bonus = 0.2
    elif position in core_residues:
        position_bonus = 0.1

    # For VES, we want DISRUPTION to score high
    # So we INVERT coherence: low coherence (chaotic) = high signal
    raw_coherence = (comp_reg * 0.3 + run_struct * 0.3 + coherence_from_delta * 0.4)

    # Apply position bonus as a BOOST to incoherence at critical sites
    # If position is critical, we REDUCE coherence (increase disruption signal)
    C_bio = raw_coherence * (1.0 - position_bonus)

    components = {
        "compression_regularity": comp_reg,
        "run_structure": run_struct,
        "delta_variance": delta_var,
        "coherence_from_delta": coherence_from_delta,
        "position_bonus": position_bonus,
        "raw_coherence": raw_coherence,
    }

    return C_bio, components


def bio_dimensional_depth(
    position: int,
    total_positions: int = 500,
    hotspots: set = None,
    dna_contact: set = None,
    core_residues: set = None,
    fibpi3d_coords: Tuple[float, float, float] = None,
) -> Tuple[float, float]:
    """
    Biological dimensional depth D_bio(X).

    Instead of detecting code/math patterns, we use:
    1. Structural criticality (hotspot > DNA-contact > core > surface)
    2. FibPi3D spatial position
    3. Sequence context

    Returns:
        (D_bio value 1-72, position_criticality score)
    """
    hotspots = hotspots or set()
    dna_contact = dna_contact or set()
    core_residues = core_residues or set()

    # Base dimensional depth from position type
    if position in hotspots:
        base_d = 64.0  # Maximum criticality
        criticality = 1.0
    elif position in dna_contact:
        base_d = 48.0
        criticality = 0.75
    elif position in core_residues:
        base_d = 32.0
        criticality = 0.5
    else:
        base_d = 16.0  # Surface/unknown
        criticality = 0.25

    # Modulate by FibPi3D if available
    if fibpi3d_coords:
        x, y, z = fibpi3d_coords
        spatial_factor = math.sqrt(x*x + y*y + z*z)
        # Scale D by spatial position (0-1 range)
        base_d = base_d * (0.8 + 0.4 * spatial_factor)

    # Clamp to 1-72 range
    D_bio = max(1.0, min(72.0, base_d))

    return D_bio, criticality


def measure_bio_se(
    data: bytes,
    position: int = 0,
    hotspots: set = None,
    dna_contact: set = None,
    core_residues: set = None,
    fibpi3d_coords: Tuple[float, float, float] = None,
) -> BioSeMetrics:
    """
    Measure Bio-Symbolic Entropy: Se_bio = H * C_bio * D_bio

    This is the bio-tuned version of Se = H * C * D.
    """
    hotspots = hotspots or set()
    dna_contact = dna_contact or set()
    core_residues = core_residues or set()

    # H: Shannon entropy of bytes
    H = shannon_entropy_bytes(data)

    # C_bio: Biological coherence
    C_bio, c_components = bio_contextual_coherence(
        data, position, hotspots, dna_contact, core_residues
    )

    # D_bio: Biological dimensional depth
    D_bio, position_criticality = bio_dimensional_depth(
        position, 500, hotspots, dna_contact, core_residues, fibpi3d_coords
    )

    # Se_bio = H * C_bio * D_bio
    # But for VES we want DISRUPTION signal, so we use:
    # Se_bio = H * (1 - C_bio) * D_bio
    # High entropy + low coherence + high criticality = high disruption

    disruption_coherence = 1.0 - C_bio  # Flip coherence to disruption
    Se_bio = H * disruption_coherence * D_bio

    return BioSeMetrics(
        H=H,
        C_bio=C_bio,
        D_bio=D_bio,
        Se_bio=Se_bio,
        compression_regularity=c_components["compression_regularity"],
        run_structure=c_components["run_structure"],
        delta_variance=c_components["delta_variance"],
        position_criticality=position_criticality,
        metadata={
            "c_components": c_components,
            "fibpi3d_coords": fibpi3d_coords,
        }
    )


# ============================================================================
# VES INTEGRATION
# ============================================================================

def compute_ves_bio_se(
    manifold_data: bytes,
    position: int,
    wt_aa: str,
    mut_aa: str,
    aa_properties: Dict[str, Dict[str, float]],
    fibpi3d_coords: Tuple[float, float, float],
    compression_ratio: float,
    hotspots: set = None,
    dna_contact: set = None,
    core_residues: set = None,
) -> Dict[str, Any]:
    """
    Compute VES using Bio-Symbolic Entropy.

    Formula:
        VES_bio = w1*Se_bio + w2*delta_hydro + w3*delta_charge + w4*delta_size + w5*D_bio

    Where Se_bio = H * (1-C_bio) * D_bio captures:
    - H: Information content of Helix64 encoding
    - (1-C_bio): Disruption signal (inverse of pattern regularity)
    - D_bio: Position criticality from biological annotations
    """
    hotspots = hotspots or set()
    dna_contact = dna_contact or set()
    core_residues = core_residues or set()

    # Measure bio-symbolic entropy
    bio_se = measure_bio_se(
        manifold_data,
        position,
        hotspots,
        dna_contact,
        core_residues,
        fibpi3d_coords,
    )

    # Physicochemical deltas
    wt_props = aa_properties.get(wt_aa, {'hydro': 0, 'charge': 0, 'size': 100})
    mut_props = aa_properties.get(mut_aa, {'hydro': 0, 'charge': 0, 'size': 100})

    delta_hydro = abs(mut_props['hydro'] - wt_props['hydro'])
    delta_charge = abs(mut_props['charge'] - wt_props['charge'])
    delta_size = abs(mut_props['size'] - wt_props['size']) / 100.0

    # FibPi3D distance
    x, y, z = fibpi3d_coords
    fibpi_distance = math.sqrt(x*x + y*y + z*z)

    # Normalize Se_bio for scoring (typical range 0-500)
    norm_Se_bio = bio_se.Se_bio / 500.0

    # VES Bio-SE formula
    ves_bio_se = (
        0.4 * norm_Se_bio +           # Bio-symbolic entropy (main signal)
        0.2 * (bio_se.D_bio / 72.0) + # Position criticality
        0.15 * delta_hydro +          # Physicochemical
        0.15 * delta_charge +
        0.05 * delta_size +
        0.05 * fibpi_distance
    )

    return {
        # Bio-Se components
        'H': bio_se.H,
        'C_bio': bio_se.C_bio,
        'D_bio': bio_se.D_bio,
        'Se_bio': bio_se.Se_bio,

        # Component breakdown
        'compression_regularity': bio_se.compression_regularity,
        'run_structure': bio_se.run_structure,
        'delta_variance': bio_se.delta_variance,
        'position_criticality': bio_se.position_criticality,

        # Physicochemical
        'delta_hydro': delta_hydro,
        'delta_charge': delta_charge,
        'delta_size': delta_size,
        'fibpi3d_distance': fibpi_distance,

        # Final score
        'ves_bio_se': ves_bio_se,

        # Compression
        'compression_ratio': compression_ratio,
    }


__all__ = [
    "BioSeMetrics",
    "shannon_entropy_bytes",
    "compression_regularity",
    "run_structure_score",
    "delta_variance_score",
    "bio_contextual_coherence",
    "bio_dimensional_depth",
    "measure_bio_se",
    "compute_ves_bio_se",
]
