#!/usr/bin/env python3
"""
Se Edge Case Tests (QA Hardening)

Tests Se = H × C × D for edge cases:
- Tiny payloads (1-64 bytes)
- All-255 vs all-zero bytes
- Boundary conditions
"""
import sys
import math
import zlib
from pathlib import Path
from collections import Counter

# Add scripts to path
sys.path.insert(0, str(Path(__file__).parent.parent / 'scripts'))
from se_stage import compute_shannon_entropy, compute_coherence


def test_tiny_payloads():
    """Test Se computation on 1-256 byte payloads"""
    print("Testing tiny payloads (1-256 bytes)...")

    results = []
    for size in [1, 2, 4, 8, 16, 32, 64, 128, 256]:
        # All zeros
        data_zeros = b'\x00' * size
        H_zeros = compute_shannon_entropy(data_zeros)
        C_zeros = compute_coherence(data_zeros)

        # All 255s
        data_ff = b'\xFF' * size
        H_ff = compute_shannon_entropy(data_ff)
        C_ff = compute_coherence(data_ff)

        # Random pattern
        data_pattern = bytes([i % 256 for i in range(size)])
        H_pattern = compute_shannon_entropy(data_pattern)
        C_pattern = compute_coherence(data_pattern)

        results.append({
            'size': size,
            'zeros': {'H': H_zeros, 'C': C_zeros},
            'ff': {'H': H_ff, 'C': C_ff},
            'pattern': {'H': H_pattern, 'C': C_pattern}
        })

        print(f"  {size:3d} bytes:")
        print(f"    Zeros:   H={H_zeros:.6f}, C={C_zeros:.6f}")
        print(f"    All-FF:  H={H_ff:.6f}, C={C_ff:.6f}")
        print(f"    Pattern: H={H_pattern:.6f}, C={C_pattern:.6f}")

    # Validate expectations
    errors = []

    # All zeros/all-FF should have H=0
    for r in results:
        if r['zeros']['H'] != 0.0:
            errors.append(f"Size {r['size']}: zeros H={r['zeros']['H']}, expected 0.0")
        if r['ff']['H'] != 0.0:
            errors.append(f"Size {r['size']}: all-FF H={r['ff']['H']}, expected 0.0")

    # All zeros/all-FF should have increasing C as size grows
    # (zlib overhead dominates for tiny payloads, so we don't expect high C until >= 256 bytes)
    for r in results:
        # For larger payloads, expect high compression
        if r['size'] >= 256:
            if r['zeros']['C'] < 0.9:
                errors.append(f"Size {r['size']}: zeros C={r['zeros']['C']:.6f}, expected >= 0.9")
            if r['ff']['C'] < 0.9:
                errors.append(f"Size {r['size']}: all-FF C={r['ff']['C']:.6f}, expected >= 0.9")
        # For tiny payloads, just verify C is in [0,1]
        else:
            if not (0.0 <= r['zeros']['C'] <= 1.0):
                errors.append(f"Size {r['size']}: zeros C={r['zeros']['C']:.6f} out of bounds")
            if not (0.0 <= r['ff']['C'] <= 1.0):
                errors.append(f"Size {r['size']}: all-FF C={r['ff']['C']:.6f} out of bounds")

    if errors:
        print(f"  ❌ FAILED: {len(errors)} edge cases violated expectations")
        for e in errors:
            print(f"     {e}")
        return False
    else:
        print(f"  ✅ PASSED: All tiny payloads behave correctly")
        return True


def test_zero_vs_ff():
    """Test that all-zero and all-255 produce identical Se values"""
    print("\nTesting all-zero vs all-255 equivalence...")

    sizes = [1, 16, 64, 256, 1024, 4096]
    errors = []

    for size in sizes:
        data_zeros = b'\x00' * size
        data_ff = b'\xFF' * size

        H_zeros = compute_shannon_entropy(data_zeros)
        H_ff = compute_shannon_entropy(data_ff)

        C_zeros = compute_coherence(data_zeros)
        C_ff = compute_coherence(data_ff)

        # H should be identical (both are single-byte distributions)
        if H_zeros != H_ff:
            errors.append(f"Size {size}: H_zeros={H_zeros} != H_ff={H_ff}")

        # C may differ slightly due to zlib internals for tiny payloads
        # For larger payloads (>=256), they should converge
        tolerance = 0.10 if size < 256 else 0.01
        if abs(C_zeros - C_ff) > tolerance:
            errors.append(f"Size {size}: C_zeros={C_zeros:.6f} vs C_ff={C_ff:.6f} (delta > {tolerance})")

        print(f"  {size:5d} bytes: H={H_zeros:.6f}, C_zeros={C_zeros:.6f}, C_ff={C_ff:.6f}")

    if errors:
        print(f"  ❌ FAILED: {len(errors)} mismatches")
        for e in errors:
            print(f"     {e}")
        return False
    else:
        print(f"  ✅ PASSED: All-zero and all-FF produce identical metrics")
        return True


def test_entropy_bounds():
    """Test that H is always in [0, 8] range"""
    print("\nTesting Shannon entropy bounds (0 <= H <= 8)...")

    test_cases = [
        ('all-zero', b'\x00' * 1024),
        ('all-ff', b'\xFF' * 1024),
        ('binary (2 bytes)', b'\x00\xFF' * 512),
        ('ascii range', bytes(range(128)) * 8),
        ('full byte range', bytes(range(256)) * 4),
        ('urandom-like', bytes([
            (i * 7919 + 1337) % 256 for i in range(1024)
        ]))  # Pseudo-random
    ]

    errors = []
    for name, data in test_cases:
        H = compute_shannon_entropy(data)

        if not (0.0 <= H <= 8.0):
            errors.append(f"{name}: H={H:.6f} out of bounds [0, 8]")

        print(f"  {name:20s}: H={H:.6f}")

    if errors:
        print(f"  ❌ FAILED: {len(errors)} out-of-bounds H values")
        for e in errors:
            print(f"     {e}")
        return False
    else:
        print(f"  ✅ PASSED: All H values in [0, 8]")
        return True


def test_coherence_bounds():
    """Test that C is always in [0, 1] range"""
    print("\nTesting coherence bounds (0 <= C <= 1)...")

    test_cases = [
        ('all-zero', b'\x00' * 1024),
        ('all-ff', b'\xFF' * 1024),
        ('pattern AB', b'AB' * 512),
        ('highly random', bytes([
            (i * 7919 + 1337) % 256 for i in range(1024)
        ])),
        ('text-like', b'the quick brown fox jumps ' * 38)
    ]

    errors = []
    for name, data in test_cases:
        C = compute_coherence(data)

        if not (0.0 <= C <= 1.0):
            errors.append(f"{name}: C={C:.6f} out of bounds [0, 1]")

        raw_size = len(data)
        compressed_size = len(zlib.compress(data, level=9))
        ratio = compressed_size / raw_size

        print(f"  {name:20s}: C={C:.6f} (compressed {ratio*100:.1f}% of raw)")

    if errors:
        print(f"  ❌ FAILED: {len(errors)} out-of-bounds C values")
        for e in errors:
            print(f"     {e}")
        return False
    else:
        print(f"  ✅ PASSED: All C values in [0, 1]")
        return True


def test_zero_division_guards():
    """Test that zero-division guards work"""
    print("\nTesting zero-division guards...")

    errors = []

    # Empty data
    try:
        H_empty = compute_shannon_entropy(b'')
        C_empty = compute_coherence(b'')

        if H_empty != 0.0:
            errors.append(f"Empty data: H={H_empty}, expected 0.0")
        if C_empty != 0.0:
            errors.append(f"Empty data: C={C_empty}, expected 0.0")

        print(f"  Empty data: H={H_empty:.6f}, C={C_empty:.6f}")
    except ZeroDivisionError as e:
        errors.append(f"ZeroDivisionError on empty data: {e}")

    # Single byte
    try:
        H_one = compute_shannon_entropy(b'\x42')
        C_one = compute_coherence(b'\x42')

        if H_one != 0.0:
            errors.append(f"Single byte: H={H_one}, expected 0.0")

        print(f"  Single byte: H={H_one:.6f}, C={C_one:.6f}")
    except ZeroDivisionError as e:
        errors.append(f"ZeroDivisionError on single byte: {e}")

    if errors:
        print(f"  ❌ FAILED: {len(errors)} zero-division issues")
        for e in errors:
            print(f"     {e}")
        return False
    else:
        print(f"  ✅ PASSED: Zero-division guards work correctly")
        return True


def test_determinism():
    """Test that H and C are deterministic across multiple runs"""
    print("\nTesting determinism (10 runs)...")

    data = bytes([
        (i * 7919 + 1337) % 256 for i in range(1024)
    ])

    H_values = [compute_shannon_entropy(data) for _ in range(10)]
    C_values = [compute_coherence(data) for _ in range(10)]

    errors = []

    # All H values should be identical
    if len(set(H_values)) != 1:
        errors.append(f"Non-deterministic H: {set(H_values)}")

    # All C values should be identical
    if len(set(C_values)) != 1:
        errors.append(f"Non-deterministic C: {set(C_values)}")

    print(f"  H values (10 runs): {H_values[0]:.9f} (all identical: {len(set(H_values)) == 1})")
    print(f"  C values (10 runs): {C_values[0]:.9f} (all identical: {len(set(C_values)) == 1})")

    if errors:
        print(f"  ❌ FAILED: {len(errors)} determinism issues")
        for e in errors:
            print(f"     {e}")
        return False
    else:
        print(f"  ✅ PASSED: H and C are deterministic")
        return True


def main():
    print("="*60)
    print("Se Edge Case Tests (QA Hardening)")
    print("="*60)
    print()

    tests = [
        test_tiny_payloads,
        test_zero_vs_ff,
        test_entropy_bounds,
        test_coherence_bounds,
        test_zero_division_guards,
        test_determinism
    ]

    results = []
    for test_fn in tests:
        try:
            passed = test_fn()
            results.append((test_fn.__name__, passed))
        except Exception as e:
            print(f"  ❌ EXCEPTION: {e}")
            results.append((test_fn.__name__, False))

    print()
    print("="*60)
    print("RESULTS")
    print("="*60)

    passed_count = sum(1 for _, p in results if p)
    total_count = len(results)

    for name, passed in results:
        status = "✅ PASSED" if passed else "❌ FAILED"
        print(f"{status}: {name}")

    print()
    print(f"Total: {passed_count}/{total_count} tests passed")

    if passed_count == total_count:
        print("✅ ALL TESTS PASSED")
        return 0
    else:
        print(f"❌ {total_count - passed_count} TEST(S) FAILED")
        return 1


if __name__ == '__main__':
    sys.exit(main())
