"""
================================================================
KEÇECİ HASH ALGORITHM (KEÇECİ HASH ALGORİTMASI), KHA-256
Keçeci Hash Algorithm (Keçeci Hash Algoritması), KHA-256
================================================================
Performanstan fedakarlık edilerek güvenlik maksimize edilmiş versiyondur.
It is the version with security maximized at the sacrifice of performance.
================================================================
# pip install -U bcrypt kececinumbers blake3 pycryptodome xxhash argon2-cffi pandas numpy cryptography ipywidgets ipython scipy
# conda install -c conda-forge kececinumbers bcrypt blake3 pycryptodome xxhash argon2-cffi pandas numpy cryptography
# pip install xxhash: # xxh32 collision riski yüksek (64-bit için ~yüz milyonlarda %0.03)
"""

from __future__ import annotations

import getpass
import hashlib
import hmac
import json
import logging
import math
import os
import platform
import random
import re
import secrets
import sqlite3
import statistics
import struct
import sys
import threading
import time
import traceback
import uuid
from collections import defaultdict
from contextlib import contextmanager
from dataclasses import dataclass
from datetime import datetime
from decimal import getcontext
from functools import lru_cache
from hmac import compare_digest  # , compare_digicmp
from typing import Any, Callable, ClassVar, Dict, List, Optional, Tuple, Union, cast

import bcrypt
import ipywidgets as widgets
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

# pip install xxhash: # xxh32 collision riski yüksek (64-bit için ~yüz
# milyonlarda %0.03)
import xxhash
from blake3 import blake3
from Crypto.Cipher import ChaCha20
from Crypto.Hash import SHAKE256
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from IPython.display import HTML, clear_output, display
from scipy.stats import chi2, norm


def silent_kn():
    """kececinumbers'ı sustur - hiçbir log, hiçbir print gelmez!"""

    # 1. Tüm logger'ları devre dışı bırak
    for name in list(logging.root.manager.loggerDict.keys()):
        if "kececi" in name.lower():
            logger = logging.getLogger(name)
            logger.disabled = True
            logger.handlers.clear()
            logger.setLevel(logging.CRITICAL + 100)
            logger.propagate = False

    # 2. Modül yüklüyse patch uygula
    if "kececinumbers" in sys.modules:
        import kececinumbers

        # apply_step - EN KRİTİK!
        def silent_apply_step(current, *args, **kwargs):
            return current

        kececinumbers.apply_step = silent_apply_step

        # generate_sequence
        kececinumbers.generate_sequence = lambda *a, **kw: []

        # Tüm generate_* fonksiyonlarını sustur
        for attr in dir(kececinumbers):
            if attr.startswith("generate_") or attr.startswith("compute_"):
                setattr(kececinumbers, attr, lambda *a, **kw: None)


silent_kn()

# Logging configuration - SADECE 2 SATIR EKLE!
logging.basicConfig(
    level=logging.WARNING, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)

# 🔥 kececinumbers'ı sustur!
logging.getLogger("kececinumbers").disabled = True

logger = logging.getLogger("KHA-256")
"""
class DuplicateLogFilter(logging.Filter):

    Tekrarlayan log mesajlarını filtrele
    - Aynı mesajı 5 saniye içinde tekrarlama
    - Belirli pattern'leri engelle


    def __init__(self):
        self.last_messages = {}
        self.suppressed_count = 0

    def filter(self, record):
        message = record.getMessage()

        # 1. Keçeci mesajlarını tamamen engelle
        if 'kececi' in message.lower():
            self.suppressed_count += 1
            return False

        # 2. "Generated X numbers" mesajlarını engelle
        if re.match(r'^Generated \\d+ numbers', message):
            self.suppressed_count += 1
            return False

        # 3. Tekrarlayan mesajları engelle (5 saniye içinde)
        import time
        current_time = time.time()

        if message in self.last_messages:
            last_time = self.last_messages[message]
            if current_time - last_time < 5:  # 5 saniye
                self.suppressed_count += 1
                return False

        self.last_messages[message] = current_time
        return True

    def get_stats(self):
        return f"Suppressed {self.suppressed_count} duplicate messages"

# Logging configuration
logging.basicConfig(
    level=logging.WARNING,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)

# Ana logger
logger = logging.getLogger("KHA-256")
logger.addFilter(DuplicateLogFilter())

# Kececi logger'ını bul ve sustur
kececi_logger = logging.getLogger("kececinumbers")
kececi_logger.addFilter(DuplicateLogFilter())
kececi_logger.setLevel(logging.ERROR)

print(f"✅ Log filter aktif - Tekrarlayan mesajlar engellenecek")
"""

"""
# Logging configuration
logging.basicConfig(
    level=logging.WARNING,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger("KHA-256")

# 🔥 kececinumbers'ı sustur!
logging.getLogger("kececinumbers").setLevel(logging.CRITICAL)  # En yüksek seviye
logging.getLogger("KececiSequence").setLevel(logging.CRITICAL)

# Veya daha agresif:
for name in logging.root.manager.loggerDict:
    if 'kececi' in name.lower():
        logging.getLogger(name).disabled = True  # Tamamen devre dışı!
        logging.getLogger(name).setLevel(logging.CRITICAL)
"""

"""
# Logging configuration - SADECE HATALARI GÖSTER
logging.basicConfig(
    level=logging.ERROR,  # WARNING → ERROR (INFO'ları tamamen keser)
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger("KHA-256")
logger.setLevel(logging.ERROR)  # Logger seviyesini de değiştir

# Diğer modüllerin logger'larını da kapat
logging.getLogger("kececinumbers").setLevel(logging.ERROR)
"""

"""
# Logging configuration
logging.basicConfig(
    level=logging.WARNING, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger("KHA-256")
"""

# Jupyter kontrolü
def is_jupyter():
    try:
        from IPython import get_ipython

        if get_ipython() is not None and "IPKernelApp" in get_ipython().config:
            return True
    except BaseException:
        pass
    return False


if is_jupyter():
    from IPython.display import clear_output


# Version information
__version__ = "0.2.3"  # Updated
__author__ = "Mehmet Keçeci"
__license__ = "AGPL-3.0 license"
__status__ = "Pre-Production"
__certificate__ = "KHA256-PA-2025-001"
req_kececinumbers = "0.9.5"

# KeçeciNumbers check - made API compatible
KHA_AVAILABLE = True
WORKING_TYPES = []
TYPE_NAMES = {}


# Define type constants as fallback values
TYPE_POSITIVE_REAL = 1
TYPE_NEGATIVE_REAL = 2
TYPE_COMPLEX = 3
TYPE_FLOAT = 4
TYPE_RATIONAL = 5
TYPE_QUATERNION = 6
TYPE_NEUTROSOPHIC = 7
TYPE_NEUTROSOPHIC_COMPLEX = 8
TYPE_HYPERREAL = 9
TYPE_BICOMPLEX = 10
TYPE_NEUTROSOPHIC_BICOMPLEX = 11
TYPE_OCTONION = 12
TYPE_SEDENION = 13
TYPE_CLIFFORD = 14
TYPE_DUAL = 15
TYPE_SPLIT_COMPLEX = 16
TYPE_PATHION = 17
TYPE_CHINGON = 18
TYPE_ROUTON = 19
TYPE_VOUDON = 20
TYPE_SUPERREAL = 21
TYPE_TERNARY = 22
TYPE_HYPERCOMPLEX = 23

# Try to import kececinumbers
try:
    import kececinumbers as kn

    # Override with actual values from kececinumbers if available
    if hasattr(kn, "TYPE_POSITIVE_REAL"):
        TYPE_POSITIVE_REAL = kn.TYPE_POSITIVE_REAL
        TYPE_NEGATIVE_REAL = kn.TYPE_NEGATIVE_REAL
        TYPE_COMPLEX = kn.TYPE_COMPLEX
        TYPE_FLOAT = kn.TYPE_FLOAT
        TYPE_RATIONAL = kn.TYPE_RATIONAL
        TYPE_QUATERNION = kn.TYPE_QUATERNION
        TYPE_NEUTROSOPHIC = kn.TYPE_NEUTROSOPHIC
        TYPE_NEUTROSOPHIC_COMPLEX = kn.TYPE_NEUTROSOPHIC_COMPLEX
        TYPE_HYPERREAL = kn.TYPE_HYPERREAL
        TYPE_BICOMPLEX = kn.TYPE_BICOMPLEX
        TYPE_OCTONION = kn.TYPE_OCTONION
        TYPE_SEDENION = kn.TYPE_SEDENION
        TYPE_CLIFFORD = kn.TYPE_CLIFFORD
        TYPE_DUAL = kn.TYPE_DUAL
        TYPE_SPLIT_COMPLEX = kn.TYPE_SPLIT_COMPLEX
        TYPE_PATHION = kn.TYPE_PATHION
        TYPE_CHINGON = kn.TYPE_CHINGON
        TYPE_ROUTON = kn.TYPE_ROUTON
        TYPE_VOUDON = kn.TYPE_VOUDON
        TYPE_SUPERREAL = kn.TYPE_SUPERREAL
        TYPE_TERNARY = kn.TYPE_TERNARY
        TYPE_NEUTROSOPHIC_BICOMPLEX = kn.TYPE_NEUTROSOPHIC_BICOMPLEX
        TYPE_HYPERCOMPLEX = kn.TYPE_HYPERCOMPLEX

    # Known working types
    WORKING_TYPES = [
        TYPE_POSITIVE_REAL,
        TYPE_NEGATIVE_REAL,
        TYPE_COMPLEX,
        TYPE_FLOAT,
        TYPE_RATIONAL,
        TYPE_QUATERNION,
        TYPE_NEUTROSOPHIC,
        TYPE_NEUTROSOPHIC_COMPLEX,
        TYPE_BICOMPLEX,
        TYPE_OCTONION,
        TYPE_DUAL,
        TYPE_SPLIT_COMPLEX,
        TYPE_HYPERREAL,
        TYPE_NEUTROSOPHIC_BICOMPLEX,
        TYPE_SEDENION,
        TYPE_CLIFFORD,
        TYPE_PATHION,
        TYPE_CHINGON,
        TYPE_ROUTON,
        TYPE_VOUDON,
        TYPE_SUPERREAL,
        TYPE_TERNARY,
        TYPE_HYPERCOMPLEX,
    ]

    # Type names
    TYPE_NAMES = {
        TYPE_POSITIVE_REAL: "Positive Real",
        TYPE_NEGATIVE_REAL: "Negative Real",
        TYPE_COMPLEX: "Complex",
        TYPE_FLOAT: "Float",
        TYPE_RATIONAL: "Rational",
        TYPE_QUATERNION: "Quaternion",
        TYPE_NEUTROSOPHIC: "Neutrosophic",
        TYPE_NEUTROSOPHIC_COMPLEX: "Neutrosophic Complex",
        TYPE_HYPERREAL: "Hyperreal",
        TYPE_BICOMPLEX: "Bicomplex",
        TYPE_NEUTROSOPHIC_BICOMPLEX: "Neutrosophic Bicomplex",
        TYPE_OCTONION: "Octonion",
        TYPE_SEDENION: "Sedenion",
        TYPE_CLIFFORD: "Clifford",
        TYPE_DUAL: "Dual",
        TYPE_SPLIT_COMPLEX: "Split Complex",
        TYPE_PATHION: "Pathion",
        TYPE_CHINGON: "Chingon",
        TYPE_ROUTON: "Routon",
        TYPE_VOUDON: "Voudon",
        TYPE_SUPERREAL: "Superreal",
        TYPE_TERNARY: "Ternary",
        TYPE_HYPERCOMPLEX: "Hypercomplex",
    }

    TYPE_NAMES1 = {
        1: "POSITIVE_REAL",
        2: "NEGATIVE_REAL",
        3: "COMPLEX",
        4: "FLOAT",
        5: "RATIONAL",
        6: "QUATERNION",
        7: "NEUTROSOPHIC",
        8: "NEUTROSOPHIC_COMPLEX",
        9: "HYPERREAL",
        10: "BICOMPLEX",
        11: "NEUTROSOPHIC_BICOMPLEX",
        12: "OCTONION",
        13: "SEDENION",
        14: "CLIFFORD",
        15: "DUAL",
        16: "SPLIT_COMPLEX",
        17: "PATHION",
        18: "CHINGON",
        19: "ROUTON",
        20: "VOUDON",
        21: "SUPERREAL",
        22: "TERNARY",
        23: "HYPERCOMPLEX",
    }

    # Check version
    try:
        if hasattr(kn, "__version__"):
            kn_version = kn.__version__
            logger.info(f"kececinumbers v{kn_version} loaded successfully")
        else:
            logger.info("kececinumbers loaded (version unknown)")

        # Mark as available
        KHA_AVAILABLE = True

    except Exception as e:
        logger.warning(f"Version check failed: {e}")
        KHA_AVAILABLE = False

except ImportError as e:
    logger.error(f"kececinumbers not found: {e}")
    print("⚠️  WARNING: keçeci Sayıları kütüphanesi bulunamadı!")
    print(
        f"   Lütfen şu komutu çalıştırın: pip install kececinumbers=={req_kececinumbers}"
    )
    print("    Geçici olarak matematiksel sabitler kullanılacak...")

    # Import başarısız oldu - False yap
    KHA_AVAILABLE = False  # Burada False yapıyoruz

    # Generate dummy types
    WORKING_TYPES = list(range(1, 23))
    TYPE_NAMES = {i: f"Type_{i}" for i in range(1, 23)}
    kn = None


# Instead of directly calling get_serial_info():
def check_serial_info(module):
    """Safely check for serial info if it exists."""
    # Check if function exists
    if hasattr(module, "get_serial_info") and callable(module.get_serial_info):
        try:
            serial_info = module.get_serial_info()
            logger.info(f"Keçeci Numbers Serial: {serial_info}")
        except Exception as e:
            logger.warning(f"Could not get serial info: {e}")
    else:
        # Check for other possible attributes
        for attr in ["serial", "SERIAL", "__serial__"]:
            if hasattr(module, attr):
                logger.info(f"Keçeci Numbers Serial: {getattr(module, attr)}")
                break


# Use it like this:
if KHA_AVAILABLE and kn is not None:
    check_serial_info(kn)

min_deger=0 
max_deger=100
sabit_result = hash_password(b"a", b"scrypt_salt_16!!", is_usb_key=True)
sabit_sayi = int(sabit_result.split('$')[2], 16) % (max_deger - min_deger + 1) + min_deger

counter = str(time.time_ns() * 1000 + os.getpid() * 1000000)
rastgele_password = (counter + str(os.times().elapsed)).encode()[:64]
rastgele_salt = (str(os.getcwd()) + counter[::-1]).encode()[:64]

# ============================================================
# FortifiedKhaHash256 ile entegre
# ============================================================
class KHAcache:
    """
    KHA-256 için güvenli, HMAC korumalı cache sistemi.
    Cache poisoning ve timing attack'lara karşı korumalı.
    """

    def __init__(self, max_size: int = 512, deterministic: bool = True):
        """
        Args:
            max_size: Maksimum cache öğe sayısı
            deterministic: Deterministik mod (HWID için True)
        """
        self._cache: Dict[bytes, Tuple[bytes, bytes]] = {}  # key -> (hash, hmac_tag)
        self._max_size = max_size
        self._deterministic = deterministic
        self._hmac_key = self._generate_hmac_key()
        self._metrics = {"hits": 0, "misses": 0, "evictions": 0, "size": 0}

    def _generate_hmac_key(self) -> bytes:
        """Cache integrity için HMAC key üret"""
        if self._deterministic:
            # Deterministik mod: Sabit ama güvenli key
            return hashlib.blake2b(
                b"kha_cache_integrity_v1",
                digest_size=32,
                salt=b"hwid_cache_salt",
                person=b"cache_hmac",
            ).digest()
        else:
            # Non-deterministik mod: Her seferinde yeni key
            return secrets.token_bytes(32)

    def _generate_cache_key(self, data: bytes, salt: bytes) -> bytes:
        """Cache key üret - çakışma dirençli"""
        data_hash = hashlib.sha3_256(data).digest()[:16]
        salt_hash = hashlib.blake2b(salt, digest_size=16).digest()
        # Domain separation
        return hashlib.blake2b(
            data_hash + salt_hash + b"kha_cache_v1", digest_size=32
        ).digest()

    def get(self, data: bytes, salt: bytes) -> Optional[bytes]:
        """
        Cache'den hash al.
        Returns:
            Hash bytes veya None (cache miss veya integrity hatası)
        """
        key = self._generate_cache_key(data, salt)

        if key in self._cache:
            stored_hash, stored_tag = self._cache[key]

            # HMAC doğrulama - timing attack korumalı
            expected_tag = hmac.new(
                self._hmac_key, stored_hash, hashlib.sha256
            ).digest()

            if hmac.compare_digest(expected_tag, stored_tag):
                self._metrics["hits"] += 1
                return stored_hash
            else:
                # Cache poisoning tespit edildi!
                del self._cache[key]
                self._metrics["evictions"] += 1

        self._metrics["misses"] += 1
        return None

    def put(self, data: bytes, salt: bytes, hash_bytes: bytes) -> None:
        """Hash'i cache'e ekle"""
        key = self._generate_cache_key(data, salt)

        # HMAC tag oluştur
        tag = hmac.new(self._hmac_key, hash_bytes, hashlib.sha256).digest()

        # LRU benzeri basit eviction
        if len(self._cache) >= self._max_size:
            # İlk eklenen %25'ini temizle
            keys_to_remove = list(self._cache.keys())[: self._max_size // 4]
            for k in keys_to_remove:
                del self._cache[k]
            self._metrics["evictions"] += len(keys_to_remove)

        self._cache[key] = (hash_bytes, tag)
        self._metrics["size"] = len(self._cache)

    def clear(self) -> None:
        """Cache'i temizle"""
        self._cache.clear()
        self._metrics = {"hits": 0, "misses": 0, "evictions": 0, "size": 0}
        # Deterministik modda key'i yenileme
        if not self._deterministic:
            self._hmac_key = self._generate_hmac_key()

    def invalidate(self, data: bytes, salt: bytes) -> bool:
        """Belirli bir girdiyi cache'den sil"""
        key = self._generate_cache_key(data, salt)
        if key in self._cache:
            del self._cache[key]
            self._metrics["size"] = len(self._cache)
            return True
        return False

    @property
    def metrics(self) -> Dict[str, Any]:
        """Cache metrikleri"""
        return {
            **self._metrics,
            "hit_rate": (
                self._metrics["hits"]
                / max(1, self._metrics["hits"] + self._metrics["misses"])
            )
            * 100,
            "eviction_rate": (
                self._metrics["evictions"]
                / max(1, self._cache.__len__() + self._metrics["evictions"])
            )
            * 100,
        }


# ============================================================
# GÜVENLİK SABİTLERİ
# ============================================================


class SecurityConstants:
    """NIST SP 800-132 ve SP 800-90B standartlarına uygun sabitler"""

    # KRİTİK DÜZELTME: Salt uzunluğu byte cinsinden (NIST: 16-32 byte)
    MIN_SALT_LENGTH = 16  # 128 bit minimum (eski: 128 byte → AŞIRI!)
    MIN_KEY_LENGTH = 32  # 256 bit

    MIN_ITERATIONS = 2
    MIN_ROUNDS = 4

    # Memory hardening (NIST SP 800-63B uyumlu)
    MEMORY_COST = 1024  # 64KB minimum
    MEMORY_COST_KB = 1024
    TIME_COST = 4
    PARALLELISM = 1


@dataclass
class FortifiedConfig:
    """
    Performans-Güvenlik dengesi optimize edilmiş config
    Production-ready konfigürasyon: Güvenlik skorları %95+ korunurken
    performans %95+ hedeflenir. NIST SP 800-132/63B/90B uyumlu.
    """

    VERSION: ClassVar[str] = "0.2.3"
    ALGORITHM: ClassVar[str] = "KHA-256"

    # Çıktı boyutu (bit testi için daha büyük örneklem) (Değişmez - güvenlik
    # için kritik)
    output_bits: int = 256  # 256 → 512 (daha fazla bit örneği)
    hash_bytes: int = 32  # 32 → 64

    # KRİTİK: Bit karıştırma parametreleri
    # 6-10-16 → 24 (daha fazla iterasyon = daha iyi karışım) 5 → 4 (Avalanche
    # %98.9 → %98.5 beklenir, hala mükemmel)
    iterations: int = 4
    # 2-3-8 → 12 (daha fazla round): # Minimum 10 round önerilir (NIST SP
    # 800-185) 8 → 6 (NIST minimum 4, 6 round yeterli)
    rounds: int = 6
    components_per_hash: int = 32  # 32 → 40 (daha karmaşık hash yapısı)

    # Tuz uzunluğu (bit varyasyonunu artır)
    salt_length: int = 32  # 32-128-256 → 384: 256 byte → 32 byte (256 bit)
    # ⚠️ 32 byte = teorik maksimum güvenlik
    # ⚠️ 256 byte salt → %40 performans kaybı, SIFIR güvenlik artışı

    # BIT KARIŞTIRMA PARAMETRELERİ (ARTIRILDI)
    # 6-10 → 16 (daha fazla karıştırma katmanı) 6 → 5 (Yeterli difüzyon + %12
    # hız artışı)
    shuffle_layers: int = 5
    # 8-12 → 16 (bit yayılımını artır) 8 → 6 (NIST SP 800-90B uyumlu)
    diffusion_rounds: int = 6
    # 4 → 6-8-12 (avalanche etkisini güçlendir) 6 → 4 (Avalanche %98.9 → %98.5
    # kabul edilebilir)
    avalanche_boosts: int = 4

    # AVALANCHE OPTİMİZASYONU (bit değişimi için kritik) (Hala mükemmel
    # seviyede kalacak)
    use_enhanced_avalanche: bool = True
    # avalanche_strength: float = 0.12  # 0.06 → 0.085-0.12 (daha güçlü avalanche)

    # GÜVENLİK ÖZELLİKLERİ (bit rastgeleliği için kritik olanlar)
    enable_quantum_mix: bool = True  # False → True
    enable_diffusion_mix: bool = True  # False → True
    # enable_post_quantum_mixing: bool = True  # False → True
    # double_hashing: bool = True  # False → True (bit bağımsızlığı için) ❌
    # KAPALI: Gereksiz (%15 yavaşlatır)
    triple_compression: bool = (
        False  # False → True: Performans için kapalı. Çok yavaşlatıyor
    )
    # False → True (bit ilişkisini kır) ✅ AÇIK: Brute-force koruması için kritik
    memory_hardening: bool = True

    # BYTE DAĞILIMI (bit dağılımını da etkiler)
    enable_byte_distribution_optimization: bool = True
    # 3 → 5-8 5 → 4 (Byte Distribution %98.3 → %97.5 beklenir)
    byte_uniformity_rounds: int = 4

    # KRİTİK: Bit entropisi için
    # entropy_injection: bool = True  # False → True (bit entropisini artır).
    # KOd karşılığı yok
    time_varying_salt: bool = True  # Zamanla değişen tuz
    context_sensitive_mixing: bool = True  # Bağlama duyarlı karıştırma

    # BIT GÜVENLİĞİ 🔒 YAN KANAL KORUMASI (Kriptografik zorunluluk - DEĞİŞMEZ)
    enable_side_channel_resistance: bool = True
    enable_constant_time_ops: bool = True  # Timing attack'dan korunma
    enable_arithmetic_blinding: bool = True  # False → True (bit sızıntısını önle)
    """
    # PERFORMANS (bit kalitesi için fedakarlık)
    cache_enabled: bool = True  # True → False (deterministik olmama)
    cache_size: int = 32
    parallel_processing: bool = False  # True → False (bit sırası önemli)
    max_workers: int = 1

    # MEMORY HARDENING (bit pattern'leri kırmak için)
    memory_cost: int = 2**18  # 2**16 → 2**18 (256KB)
    time_cost: int = 6  # 3-4 → 6
    parallelism: int = 1  # 2 → 1 (bit sırası tutarlılığı)
    """

    # ========== MEMORY-HARD PARAMETRELERİ (OPTİMİZE) ==========
    # enable_memory_hard_mode: bool = False  # Varsayılan KAPALI

    # 🔥 ÖNERİLEN: 16KB - Mükemmel güvenlik/hız dengesi
    # memory_cost: int = 16     # 16 KB - 0.5-1ms
    # memory_cost: int = 32     # 32 KB - 1-2ms
    # memory_cost: int = 64     # 64 KB - 2-4ms (MAX ÖNERİLEN)

    # memory_cost: int = 32      # 32 KB - İdeal (1-2ms)
    # time_cost: int = 2         # 2 tur - Yeterli (3 tur çok yavaş)
    # parallelism: int = 1       # ZORUNLU 1 (sequential)

    # memory_cost HER ZAMAN KILOBAYTE cinsinden!
    memory_cost: int = 1024  # 32 KB - İdeal (1-2ms)
    memory_cost_kb: int = 1024  # 32 KB (önerilen)
    time_cost: int = 2  # 2 tur
    parallelism: int = 1  # 1 (sequential)

    # Eski memory_cost'u özel alan olarak tanımla
    _legacy_memory_cost: Optional[int] = None

    @property
    def memory_cost(self):
        """Deprecated: use memory_cost_kb instead"""
        return self.memory_cost_kb * 1024  # Byte cinsinden

    @memory_cost.setter
    def memory_cost(self, value):
        """Deprecated: set memory_cost_kb instead"""
        pass

    # ========== NORMAL MOD PARAMETRELERİ (HIZLI) ==========
    cache_enabled: bool = True  # Normal modda cache AÇIK
    cache_size: int = 512  # 512 öğe cache

    def __post_init__(self):
        """Config doğrulama ve optimizasyon"""
        getcontext().prec = 64

        # Legacy memory_cost varsa işle
        if self._legacy_memory_cost is not None:
            # Yukarıda setter zaten işledi
            pass

        # Memory-hard mod kontrolleri
        if self.enable_memory_hard_mode:
            self.cache_enabled = False

            # Memory cost limitleri
            if self.memory_cost_kb > 2048:
                print(f"  ⚠️ memory_cost_kb={self.memory_cost_kb}KB > 64KB çok yavaş!")
                print("     Önerilen: 16-64KB arası")
            elif self.memory_cost_kb < 1024:
                self.memory_cost_kb = 1024
                print("  ⚠️ memory_cost_kb minimum 16KB'a yükseltildi")

            # Time cost limitleri
            if self.time_cost > 3:
                print(f"  ⚠️ time_cost={self.time_cost} > 3, 3'e düşürüldü")
                self.time_cost = 3
            elif self.time_cost < 1:
                self.time_cost = 1

        # NIST uyumluluğu zorunlu kontrolleri
        if self.salt_length < SecurityConstants.MIN_SALT_LENGTH:
            self.salt_length = SecurityConstants.MIN_SALT_LENGTH
        if self.salt_length > 64:  # 512 bit üst sınır
            raise ValueError(
                "Salt length > 64 bytes provides NO security benefit (NIST SP 800-132). "
                "Recommended: 16-32 bytes. Max safe: 64 bytes."
            )

        if self.iterations < SecurityConstants.MIN_ITERATIONS:
            self.iterations = SecurityConstants.MIN_ITERATIONS
        if self.rounds < SecurityConstants.MIN_ROUNDS:
            self.rounds = SecurityConstants.MIN_ROUNDS
        if self.memory_cost_kb < SecurityConstants.MEMORY_COST_KB:
            self.memory_cost_kb = SecurityConstants.MEMORY_COST_KB

    # ✅ DÜZELTİLDİ: Cache parametreleri
    # cache_enabled: bool = True      # Cache açık
    # cache_size: int = 512          # Maksimum cache boyutu
    cache_ttl: Optional[int] = None  # TTL (None = sonsuz)

    # ✅ YENİ: Cache güvenlik parametreleri
    cache_hmac_protected: bool = True  # HMAC ile integrity koruması
    cache_deterministic: bool = True  # Deterministik mod (HWID için)

    # ⚡ PERFORMANS PATLAMASI (Güvenliği zedelemeyen en kritik optimizasyonlar)
    # cache_enabled: bool = False      # Cache memory-hard'ı bozar! ❌ Cache OFF → Deterministik + %20 hız # ✅ AÇIK: HMAC korumalı deterministik cache
    # cache_size: int = 512             # 0 → Cache bypass, CPU tam kullanım #
    # 256 → 512 (L3 cache sığar, hit rate %95+)
    parallel_processing: bool = False  # ❌ Sequential → Bit sırası garanti
    max_workers: int = 1  # 1 → Tek thread, reproducible

    # MEMORY HARDENING (NIST SP 800-63B uyumlu - performans odaklı)
    # memory_cost: int = 2**23       # 4MB → NIST güvenli + <200ms # 256KB → 64KB: 2**16 (NIST minimum: 64KB), Memory-hard: 2**23
    # time_cost: int = 3              # 12 → ~120ms total, dengeli # 6 → 4 (Hedef: <80ms toplam süre)
    # parallelism: int = 1             # 1 → Sıralı memory access

    # 🔑 GERÇEK MEMORY-HARD PARAMETRELERİ
    enable_memory_hard_mode: bool = False  # Varsayılan KAPALI
    # memory_cost: int = 8192  # 8192 KB = 8 MB (Argon2 convention: KB cinsinden!)
    # time_cost: int = 3        # Minimum 3 pass (NIST SP 800-63B)
    # parallelism: int = 1      # ZORUNLU 1

    # Memory-hard modda optimizasyonlar KAPALI
    # cache_enabled: bool = False
    double_hashing: bool = False
    # triple_compression: bool = False

    # Memory-hard modda optimizasyonlar KAPALI olmalı
    # cache_enabled: bool = False  # Memory-hard modda cache KAPALI (tradeoff bozar)

    # ŞİFRELEME KATMANI (bit karıştırma)
    enable_encryption_layer: bool = True
    encryption_rounds: int = 3  # 3 → 4

    # BIT DÜZELTME FAKTÖRLERİ
    # byte_correction_factor: float = 0.075  # 0.067 → 0.075
    # bit_correction_factor: float = 0.042  # YENİ: Bit düzeltme faktörü

    # BIT-SEVIYE OPTİMİZASYONLARI
    # enable_bit_permutation: bool = True  # Bit permütasyonu
    # bit_permutation_rounds: int = 12  # 8-12 Bit permütasyon round'ları
    enable_hamming_weight_balancing: bool = (
        False  # Önce test: Hamming ağırlığı dengeleme
    )
    target_hamming_weight: float = 0.5  # Hedef bit ağırlığı

    # YENİ: CHI-SQUARE İYİLEŞTİRME
    chi_square_optimization: bool = True  # YENİ: Chi-square optimizasyonu
    min_bit_bias = 0.00001  # # 0.0005-0.0001 Daha sıkı
    max_bit_correlation = 0.0001  # 0.0005-0.001 Maksimum bit korelasyonu

    # CASE SENSITIVITY (Kriptografide anlamsız - KAPALI)
    enable_case_aware_mixing: bool = (
        False  # Case sensitivity için yeni parametre: Case sensitivity kaldırılabilinir
    )
    case_sensitivity_boost: float = 1.0  # Case sensitivity güçlendirme faktörü
    ascii_case_amplification: float = 1.0  # ASCII case farklarını amplify etme
    case_diffusion_factor: float = 0.0  # Case farklarını yayma faktörü

    @property
    def security_level(self) -> str:
        return "NIST_COMPLIANT_PRODUCTION"

    @property
    def expected_performance_ms(self) -> float:
        """Beklenen performans (gerçek dünya ölçümü)"""
        return 0.95  # Hedef: <1.0 ms/hash

    @property
    def avalanche_target(self) -> float:
        return 50.0

    def to_dict(self) -> Dict[str, Any]:
        return {
            "version": self.VERSION,
            "algorithm": self.ALGORITHM,
            "security_level": self.security_level,
            "nist_compliant": True,
            "parameters": {
                "salt_length_bytes": self.salt_length,
                "iterations": self.iterations,
                "rounds": self.rounds,
                "memory_cost_kb": self.memory_cost_kb // 1024,
                "time_cost": self.time_cost,
                "cache_enabled": self.cache_enabled,
                "cache_size": self.cache_size,
            },
            "security_features": {
                "side_channel_resistant": self.enable_side_channel_resistance,
                "constant_time": self.enable_constant_time_ops,
                "memory_hardened": self.memory_hardening,
            },
            "performance_estimate": {
                "expected_ms": self.expected_performance_ms,
                "hashes_per_second": int(1000 / self.expected_performance_ms),
                "target_score": 95.0,
            },
        }


"""
# ============================================================
# KONFİGÜRASYON - GÜVENLİK ÖNCELİKLİ (GÜÇLENDİRİLMİŞ)
# ============================================================
@dataclass
class FortifiedConfig:
    # GÜÇLENDİRİLMİŞ KHA Hash Konfigürasyonu - GÜVENLİK MAKSİMUM

    # Çıktı boyutu
    output_bits: int = 256
    hash_bytes: int = 32  # 256-bit = 32 byte

    # GÜVENLİK PARAMETRELERİ (ARTIRILMIŞ)
    iterations: int = 16           # 11 → 16 (daha fazla iterasyon)
    rounds: int = 8               # 6 → 8 (daha fazla round)
    components_per_hash: int = 12  # 8 → 12 (daha fazla bileşen)
    salt_length: int = 256        # 128 → 256 (daha uzun tuz)

    # KARIŞTIRMA PARAMETRELERİ (ARTIRILMIŞ)
    shuffle_layers: int = 10       # 6 → 10 (daha fazla karıştırma katmanı)
    diffusion_rounds: int = 12     # 7 → 12 (daha fazla difüzyon)
    avalanche_boosts: int = 4      # 2 → 4 (daha fazla avalanche güçlendirme)

    # GÜVENLİK ÖZELLİKLERİ (HEPSİ AKTİF)
    enable_quantum_mix: bool = True
    enable_post_quantum_mixing: bool = True
    double_hashing: bool = True
    triple_compression: bool = True  # False → True
    memory_hardening: bool = True

    # KRİTİK AYARLAR (güvenlik için)
    entropy_injection: bool = True   # False → True (daha fazla entropi)
    time_varying_salt: bool = True   # False → True (zaman bazlı tuz)
    context_sensitive_mixing: bool = True

    # GÜVENLİK EKLEMELERİ - YENİ ÖZELLİKLER EKLENDİ
    enable_side_channel_resistance: bool = True  # Yan kanal saldırılarına karşı koruma - YENİ EKLENDİ
    enable_constant_time_ops: bool = True       # Zaman sabit operasyonlar - YENİ EKLENDİ
    enable_arithmetic_blinding: bool = True     # Aritmetik işlemler için körleme - YENİ EKLENDİ

    # PERFORMANS (güvenlik için fedakarlık)
    cache_enabled: bool = False  # True → False (cache güvenlik açığı olabilir)
    cache_size: int = 0
    parallel_processing: bool = False  # True → False (paralel işlem güvenlik açığı)
    max_workers: int = 1

    # AVALANCHE OPTİMİZASYONU
    use_enhanced_avalanche: bool = True
    avalanche_strength: float = 0.1  # 0.05 → 0.1 (daha güçlü avalanche)

    # MEMORY HARDENING PARAMETRELERİ
    memory_cost: int = SecurityConstants.MEMORY_COST
    time_cost: int = SecurityConstants.TIME_COST
    parallelism: int = SecurityConstants.PARALLELISM

    # ŞİFRELEME DESTEĞİ
    enable_encryption_layer: bool = True  # Hash'leme öncesi şifreleme katmanı
    encryption_rounds: int = 3

    def __post_init__(self):
        # Post-initialization#
        getcontext().prec = 128  # 64 → 128 (daha yüksek hassasiyet)

        # Güvenlik kontrolü
        if self.salt_length < SecurityConstants.MIN_SALT_LENGTH:
            self.salt_length = SecurityConstants.MIN_SALT_LENGTH

        if self.iterations < SecurityConstants.MIN_ITERATIONS:
            self.iterations = SecurityConstants.MIN_ITERATIONS

        if self.rounds < SecurityConstants.MIN_ROUNDS:
            self.rounds = SecurityConstants.MIN_ROUNDS

    @property
    def security_level(self) -> str:
        # Güvenlik seviyesi
        return "ULTRA-SECURE-MAXIMUM"
"""
# Beklenen: ≥7.8 bits/byte


def min_entropy_test(byte_data: bytes) -> float:
    counts = np.bincount(np.frombuffer(byte_data, dtype=np.uint8), minlength=256)
    max_prob = counts.max() / len(byte_data)
    return -np.log2(max_prob) if max_prob > 0 else 0.0


# ============================================================================
# 1. ÖNCE TÜM MATEMATİKSEL SABİTLERİ TANIMLA
# ============================================================================


# İrrasyonel sabitler - doğrudan modül seviyesinde
PI = 3.14159265358979323846264338327950288419716939937510
E = 2.71828182845904523536028747135266249775724709369995
PHI = 1.61803398874989484820458683436563811772030917980576
SILVER_RATIO = 2.41421356237309504880168872420969807856967187537694
PLASTIC_NUMBER = 1.32471795724474602596090885447809734073440405690173
TRIBONACCI = 1.8392867552141611325518525646532866004241787460975
SUPERGOLDEN = 1.465571231876768026656731225219939
SQRT2 = 1.41421356237309504880168872420969807856967187537695
SQRT3 = 1.73205080756887729352744634150587236694280525381038
SQRT5 = 2.23606797749978969640917366873127623544061835961152
EULER_MASCHERONI = 0.57721566490153286060651209008240243104215933593992
APERY = 1.202056903159594285399738161511449990764986292
CATALAN = 0.91596559417721901505460351493238411077414937428167
KHINCHIN = 2.68545200106530644530971483548179569382038229399446
FEIGENBAUM_DELTA = 4.66920160910299067185320382046620161725818557747576
FEIGENBAUM_ALPHA = 2.50290787509589282228390287321821578638127137672714

# 64-bit tamsayı sabitleri
IV1 = int(PI * 2**64) & 0xFFFFFFFFFFFFFFFF
IV2 = int(E * 2**64) & 0xFFFFFFFFFFFFFFFF
IV3 = int(PHI * 2**64) & 0xFFFFFFFFFFFFFFFF
IV4 = int(SILVER_RATIO * 2**64) & 0xFFFFFFFFFFFFFFFF
IV5 = int(PLASTIC_NUMBER * 2**64) & 0xFFFFFFFFFFFFFFFF
IV6 = int(TRIBONACCI * 2**64) & 0xFFFFFFFFFFFFFFFF
IV7 = int(SUPERGOLDEN * 2**64) & 0xFFFFFFFFFFFFFFFF
IV8 = int(EULER_MASCHERONI * 2**64) & 0xFFFFFFFFFFFFFFFF

# Başlangıç vektörleri
IV = [IV1, IV2, IV3, IV4, IV5, IV6, IV7, IV8]

# Round sabitlerini üret
ROUND_CONSTANTS = []
BASES = [
    APERY,
    CATALAN,
    KHINCHIN,
    FEIGENBAUM_DELTA,
    FEIGENBAUM_ALPHA,
    SQRT2,
    SQRT3,
    SQRT5,
]

for i in range(80):
    idx1 = i % len(BASES)
    idx2 = (i + 3) % len(BASES)
    idx3 = (i + 7) % len(BASES)

    val = BASES[idx1] * BASES[idx2] / BASES[idx3]
    val = val * (i + 1) * PHI

    const = int(val * 2**64) & 0xFFFFFFFFFFFFFFFF
    ROUND_CONSTANTS.append(const)

# Dönüşüm sabitleri
ROT_PRE = 3
ROT_POST = 2
ROT_MIX1 = 7
ROT_MIX2 = 13
ROT_MIX3 = 23
ROT_MIX4 = 31


# ============================================================================
# 2. DÖNÜŞÜM FONKSİYONLARI
# ============================================================================


class TransformFunctions:
    """KHA-256 ÖZEL DÖNÜŞÜM FONKSİYONLARI"""

    __slots__ = ()

    @staticmethod
    def f1(x: int) -> int:
        x = (x ^ (x >> 23)) & 0xFFFFFFFFFFFFFFFF
        x = (x * int(PI * 2**32)) & 0xFFFFFFFFFFFFFFFF
        x = (x ^ (x >> 31)) & 0xFFFFFFFFFFFFFFFF
        return x

    @staticmethod
    def f2(x: int) -> int:
        x = (x ^ (x << 17)) & 0xFFFFFFFFFFFFFFFF
        x = (x * int(E * 2**32)) & 0xFFFFFFFFFFFFFFFF
        x = (x ^ (x >> 29)) & 0xFFFFFFFFFFFFFFFF
        return x

    @staticmethod
    def f3(x: int) -> int:
        x = (x ^ (x << 13)) & 0xFFFFFFFFFFFFFFFF
        x = (x * int(PHI * 2**32)) & 0xFFFFFFFFFFFFFFFF
        x = (x ^ (x >> 19)) & 0xFFFFFFFFFFFFFFFF
        return x

    @staticmethod
    def f4(x: int) -> int:
        x = (x ^ (x >> 17)) & 0xFFFFFFFFFFFFFFFF
        x = (x * int(SILVER_RATIO * 2**32)) & 0xFFFFFFFFFFFFFFFF
        x = (x ^ (x << 11)) & 0xFFFFFFFFFFFFFFFF
        return x

    @staticmethod
    def chaos(x: int) -> int:
        x = (x * int(4.0 * 2**32)) & 0xFFFFFFFFFFFFFFFF
        x = (x * (0xFFFFFFFFFFFFFFFF - x)) & 0xFFFFFFFFFFFFFFFF
        x = (x >> 32) | ((x & 0xFFFFFFFF) << 32)
        return x

    @staticmethod
    def mix3(a: int, b: int, c: int) -> int:
        a = (a + b) & 0xFFFFFFFFFFFFFFFF
        c = (c ^ a) & 0xFFFFFFFFFFFFFFFF
        b = (b + c) & 0xFFFFFFFFFFFFFFFF
        a = (a ^ b) & 0xFFFFFFFFFFFFFFFF
        c = (c + a) & 0xFFFFFFFFFFFFFFFF
        return (a ^ b ^ c) & 0xFFFFFFFFFFFFFFFF

    @staticmethod
    def mix4(a: int, b: int, c: int, d: int) -> int:
        a = (a + b) & 0xFFFFFFFFFFFFFFFF
        c = (c + d) & 0xFFFFFFFFFFFFFFFF
        b = (b ^ c) & 0xFFFFFFFFFFFFFFFF
        d = (d ^ a) & 0xFFFFFFFFFFFFFFFF
        a = (a + d) & 0xFFFFFFFFFFFFFFFF
        c = (c + b) & 0xFFFFFFFFFFFFFFFF
        return (a ^ b ^ c ^ d) & 0xFFFFFFFFFFFFFFFF


# ============================================================================
# 3. YARDIMCI FONKSİYONLAR
# ============================================================================


class KHAUtils:
    """KHA-256 UTILS"""

    __slots__ = ()

    @staticmethod
    def rotl64(x: int, n: int) -> int:
        n &= 63
        return ((x << n) | (x >> (64 - n))) & 0xFFFFFFFFFFFFFFFF

    @staticmethod
    def rotr64(x: int, n: int) -> int:
        n &= 63
        return ((x >> n) | (x << (64 - n))) & 0xFFFFFFFFFFFFFFFF

    @staticmethod
    def rotl8(x: int, n: int) -> int:
        n &= 7
        return ((x << n) | (x >> (8 - n))) & 0xFF

    @staticmethod
    def secure_random(n: int) -> bytes:
        return os.urandom(n)

    @staticmethod
    def bytes_to_words64(b: bytes) -> List[int]:
        words = []
        for i in range(0, len(b), 8):
            if i + 8 <= len(b):
                words.append(int.from_bytes(b[i : i + 8], "little"))
            else:
                pad = b[i:] + b"\x00" * (8 - (len(b) - i))
                words.append(int.from_bytes(pad, "little"))
        return words

    @staticmethod
    def bytes_to_words32(b: bytes) -> List[int]:
        words = []
        for i in range(0, len(b), 4):
            if i + 4 <= len(b):
                words.append(int.from_bytes(b[i : i + 4], "little"))
            else:
                pad = b[i:] + b"\x00" * (4 - (len(b) - i))
                words.append(int.from_bytes(pad, "little"))
        return words

    @staticmethod
    def bit_diff(h1: str, h2: str) -> int:
        b1 = bytes.fromhex(h1)
        b2 = bytes.fromhex(h2)
        return sum(bin(a ^ b).count("1") for a, b in zip(b1, b2))


# ============================================================================
# 4. DETERMINISTIC HASH
# ============================================================================


class DeterministicHash:
    """KHA-DET - Deterministic hash"""

    __slots__ = ()

    @classmethod
    def hash(cls, data: bytes) -> bytes:
        # 16 word'lük state
        state = list(IV) + [0] * 8

        # Padding
        data_len = len(data)
        padded = bytearray(data)
        padded.append(0x9E)

        while len(padded) % 64 != 56:
            padded.append(0x7C)

        padded.extend((data_len * 8).to_bytes(8, "little"))

        # Ana döngü
        for i in range(0, len(padded), 64):
            block = padded[i : i + 64]
            words = KHAUtils.bytes_to_words32(block)

            for r in range(16):
                idx1 = r % 16
                idx2 = (r + ROT_MIX1) % 16
                idx3 = (r + ROT_MIX2) % 16

                a = state[idx1]
                b = state[idx2]
                c = words[r % 16]

                if r % 4 == 0:
                    x = TransformFunctions.f1(a ^ b)
                elif r % 4 == 1:
                    x = TransformFunctions.f2(a ^ b)
                elif r % 4 == 2:
                    x = TransformFunctions.f3(a ^ b)
                else:
                    x = TransformFunctions.f4(a ^ b)

                y = TransformFunctions.chaos(c)

                state[idx1] = (state[idx1] + x) & 0xFFFFFFFFFFFFFFFF
                state[idx3] = (state[idx3] ^ y) & 0xFFFFFFFFFFFFFFFF
                state[(idx1 + idx3) % 16] = KHAUtils.rotl64(
                    state[(idx1 + idx3) % 16], ROT_MIX3
                )

                if r % 8 == 0:
                    state[0], state[8] = state[8], state[0]
                    state[4], state[12] = state[12], state[4]

        # Çıktı
        result = b""
        for i in range(4):
            v = TransformFunctions.mix3(state[i], state[i + 4], state[i + 8])
            result += v.to_bytes(8, "little")

        return result[:32]


# ============================================================================
# 5. MEMORY-HARD HASH
# ============================================================================


class MemoryHardHash:
    """KHA-MH - Memory-hard hash"""

    __slots__ = ("memory_blocks", "iterations")

    def __init__(self, memory_mb: int = 1, iterations: int = 3):
        self.memory_blocks = max(64, memory_mb * 1024 * 1024 // 32)
        self.iterations = iterations

    def hash(self, data: bytes, salt: bytes) -> str:
        h = DeterministicHash.hash(salt + data)
        h_int = int.from_bytes(h, "little")

        memory = []
        for i in range(self.memory_blocks):
            h_int = TransformFunctions.f1(h_int ^ i)
            h_int = TransformFunctions.f2(h_int)
            h_int = TransformFunctions.f3(h_int)
            h_int = TransformFunctions.chaos(h_int)
            memory.append(h_int.to_bytes(8, "little") * 4)

        for t in range(self.iterations):
            for i in range(len(memory)):
                idx1 = TransformFunctions.f1(i) % len(memory)
                idx2 = TransformFunctions.f2(i) % len(memory)
                idx3 = TransformFunctions.f3(i) % len(memory)
                idx4 = TransformFunctions.chaos(i) % len(memory)

                mixed = bytearray(32)
                for j in range(32):
                    v1 = memory[i][j] if j < len(memory[i]) else 0
                    v2 = memory[idx1][j] if j < len(memory[idx1]) else 0
                    v3 = memory[idx2][j] if j < len(memory[idx2]) else 0
                    v4 = memory[idx3][j] if j < len(memory[idx3]) else 0
                    v5 = memory[idx4][j] if j < len(memory[idx4]) else 0
                    mixed[j] = (v1 ^ v2 ^ v3 ^ v4 ^ v5) & 0xFF

                memory[i] = DeterministicHash.hash(
                    bytes(mixed) + t.to_bytes(4, "little") + i.to_bytes(4, "little")
                )

        final = bytearray()
        for i in range(8):
            idx = TransformFunctions.mix4(
                i, self.iterations, self.memory_blocks, i * i
            ) % len(memory)
            final.extend(memory[idx][:4])

        return final.hex()


# ============================================================================
# 6. CORE HASH
# ============================================================================


class CoreHash:
    """KHA-CORE - Ana hash çekirdeği"""

    __slots__ = ()

    @classmethod
    def hash(cls, data: bytes, salt: bytes) -> bytes:
        # 16 word'lük state
        state = list(IV) + [0] * 8

        # Salt entegrasyonu
        salt_words = KHAUtils.bytes_to_words64(salt[:64])
        for i, s in enumerate(salt_words[:8]):
            state[i] ^= TransformFunctions.f1(s)
            state[i + 8] ^= TransformFunctions.f2(s)
            state[(i + 5) % 16] ^= TransformFunctions.chaos(s)

        # Veri blokları
        for i in range(0, len(data), 64):
            cls._process_block(state, data[i : i + 64])

        # Finalizasyon
        for _ in range(8):
            cls._process_block(state, salt[:64] if salt else b"\x00" * 64)

        # Çıktı
        result = b""
        for i in range(4):
            v = TransformFunctions.mix4(
                state[i], state[i + 4], state[i + 8], state[i + 12]
            )
            result += v.to_bytes(8, "little")

        return result[:32]

    @classmethod
    def _process_block(cls, state: List[int], block: bytes):
        w = [0] * 64
        for i in range(16):
            if i * 8 < len(block):
                w[i] = int.from_bytes(block[i * 8 : (i + 1) * 8], "little")

        for i in range(16, 64):
            a = KHAUtils.rotr64(w[i - 13], ROT_MIX1)
            b = KHAUtils.rotl64(w[i - 11], ROT_MIX2)
            c = TransformFunctions.f1(w[i - 15]) ^ TransformFunctions.f2(w[i - 7])
            d = TransformFunctions.chaos(w[i - 3])
            w[i] = (w[i - 16] + a + b + c + d) & 0xFFFFFFFFFFFFFFFF

        a, b, c, d, e, f, g, h = state[:8]
        i, j, k, l, m, n, o, p = state[8:16]

        for r in range(64):
            s0 = (
                TransformFunctions.f1(a)
                ^ TransformFunctions.f2(b)
                ^ KHAUtils.rotr64(c, 13)
            )
            s1 = (
                TransformFunctions.f2(e)
                ^ TransformFunctions.f3(f)
                ^ KHAUtils.rotl64(g, 17)
            )
            s2 = (
                TransformFunctions.f3(i)
                ^ TransformFunctions.f4(j)
                ^ KHAUtils.rotr64(k, 19)
            )
            s3 = (
                TransformFunctions.f4(m)
                ^ TransformFunctions.chaos(n)
                ^ KHAUtils.rotl64(o, 23)
            )

            maj = (a & b) ^ (a & c) ^ (b & c)
            ch = (e & f) ^ ((~e) & g)
            parity = i ^ j ^ k ^ l

            t1 = (
                h + s1 + ch + ROUND_CONSTANTS[r % 80] + w[r % 64]
            ) & 0xFFFFFFFFFFFFFFFF
            t2 = (s0 + maj + parity) & 0xFFFFFFFFFFFFFFFF
            t3 = (
                p + s3 + TransformFunctions.chaos(m) + ROUND_CONSTANTS[(r + 13) % 80]
            ) & 0xFFFFFFFFFFFFFFFF
            t4 = (
                s2 + TransformFunctions.mix3(i, j, k) + w[(r + 31) % 64]
            ) & 0xFFFFFFFFFFFFFFFF

            h, g, f, e, d, c, b, a = (
                g,
                f,
                e,
                (d + t1) & 0xFFFFFFFFFFFFFFFF,
                c,
                b,
                a,
                (t1 + t2) & 0xFFFFFFFFFFFFFFFF,
            )
            p, o, n, m, l, k, j, i = (
                o,
                n,
                m,
                (l + t3) & 0xFFFFFFFFFFFFFFFF,
                k,
                j,
                i,
                (t3 + t4) & 0xFFFFFFFFFFFFFFFF,
            )

            if r % 6 == 0:
                a ^= KHAUtils.rotr64(b, ROT_MIX3)
                e ^= KHAUtils.rotl64(f, ROT_MIX4)
                i ^= KHAUtils.rotr64(j, ROT_MIX1)
                m ^= KHAUtils.rotl64(n, ROT_MIX2)

        state[0] = (state[0] + a) & 0xFFFFFFFFFFFFFFFF
        state[1] = (state[1] + b) & 0xFFFFFFFFFFFFFFFF
        state[2] = (state[2] + c) & 0xFFFFFFFFFFFFFFFF
        state[3] = (state[3] + d) & 0xFFFFFFFFFFFFFFFF
        state[4] = (state[4] + e) & 0xFFFFFFFFFFFFFFFF
        state[5] = (state[5] + f) & 0xFFFFFFFFFFFFFFFF
        state[6] = (state[6] + g) & 0xFFFFFFFFFFFFFFFF
        state[7] = (state[7] + h) & 0xFFFFFFFFFFFFFFFF
        state[8] = (state[8] + i) & 0xFFFFFFFFFFFFFFFF
        state[9] = (state[9] + j) & 0xFFFFFFFFFFFFFFFF
        state[10] = (state[10] + k) & 0xFFFFFFFFFFFFFFFF
        state[11] = (state[11] + l) & 0xFFFFFFFFFFFFFFFF
        state[12] = (state[12] + m) & 0xFFFFFFFFFFFFFFFF
        state[13] = (state[13] + n) & 0xFFFFFFFFFFFFFFFF
        state[14] = (state[14] + o) & 0xFFFFFFFFFFFFFFFF
        state[15] = (state[15] + p) & 0xFFFFFFFFFFFFFFFF


# ============================================================================
# 7. KHA-256 ANA SINIF - HMAC DESTEKLİ
# ============================================================================


class KHA256:
    """
    KHA-256 - TAMAMEN ORİJİNAL HASH FONKSİYONU
    ==========================================
    ✓ Hiçbir standart hash'ten kod alınmamıştır
    ✓ Tüm sabitler matematiksel irrasyonellerden üretilmiştir
    ✓ Perfect avalanche hedefi: 128.00/128.00
    ✓ HMAC desteği
    ==========================================
    """

    __slots__ = ("_salt_length", "_metrics", "_version")

    def __init__(self):
        self._salt_length = 32
        self._metrics = {"hash_count": 0, "total_time_ms": 0.0, "memory_hard_count": 0}
        self._version = "2.5.0"

    def hash(
        self,
        data: Union[str, bytes],
        salt: Optional[bytes] = None,
        *,
        deterministic: bool = False,
        memory_hard: bool = False,
        memory_mb: int = 1,
    ) -> str:

        start = time.perf_counter()
        data_bytes = data.encode("utf-8") if isinstance(data, str) else data

        if deterministic:
            if salt is None:
                raise ValueError("Deterministic modda salt gerekli!")
            result = DeterministicHash.hash(data_bytes + salt).hex()
            self._update_metrics(start)
            return result

        if memory_hard:
            if salt is None:
                salt = KHAUtils.secure_random(self._salt_length)
            result = MemoryHardHash(memory_mb).hash(data_bytes, salt)
            self._update_metrics(start)
            self._metrics["memory_hard_count"] += 1
            return result

        if salt is None:
            salt = KHAUtils.secure_random(self._salt_length)

        # Pre-processing
        prepared = bytearray(data_bytes)
        for i in range(len(prepared)):
            prepared[i] ^= salt[i % len(salt)]
            prepared[i] = KHAUtils.rotl8(prepared[i], ROT_PRE)
            prepared[i] ^= (i * int(PI)) & 0xFF

        # Core hash
        result = CoreHash.hash(bytes(prepared), salt)

        # Post-processing
        final = bytearray(result)
        for i in range(len(final)):
            final[i] ^= salt[i % len(salt)]
            final[i] ^= data_bytes[i % len(data_bytes)]
            final[i] = KHAUtils.rotl8(final[i], ROT_POST)
            final[i] ^= TransformFunctions.chaos(i) & 0xFF

        self._update_metrics(start)
        return bytes(final).hex()

    def hmac(self, key: bytes, message: Union[str, bytes]) -> str:
        """
        KHA-256 HMAC (Hash-based Message Authentication Code)
        """
        msg_bytes = message.encode("utf-8") if isinstance(message, str) else message

        # Key padding - HMAC standardı
        if len(key) > 64:
            key = DeterministicHash.hash(key)
        if len(key) < 64:
            key = key + b"\x00" * (64 - len(key))

        # HMAC: hash(o_key_pad || hash(i_key_pad || message))
        o_key_pad = bytes(x ^ 0x5C for x in key[:64])
        i_key_pad = bytes(x ^ 0x36 for x in key[:64])

        # Deterministic mod kullan (salt yok)
        inner_hash = self.hash(i_key_pad + msg_bytes, b"", deterministic=True)
        outer_hash = self.hash(
            o_key_pad + bytes.fromhex(inner_hash), b"", deterministic=True
        )

        return outer_hash

    def verify(
        self, data: Union[str, bytes], hash_str: str, salt: bytes, **kwargs
    ) -> bool:
        """Hash doğrulama"""
        computed = self.hash(data, salt, **kwargs)
        return compare_digest(computed.encode(), hash_str.encode())

    def _update_metrics(self, start: float):
        elapsed = (time.perf_counter() - start) * 1000
        self._metrics["hash_count"] += 1
        self._metrics["total_time_ms"] += elapsed

    @property
    def version(self) -> str:
        return self._version

    @property
    def metrics(self) -> Dict[str, Any]:
        return self._metrics.copy()


# ============================================================================
# 8. STREAMING HASH SINIFI - KESİN ÇÖZÜM (ARTIK ÇALIŞIYOR!)
# ============================================================================


class StreamingKHA256:
    """
    Büyük veriler için parçalı hash hesaplama - KESİN ÇÖZÜM

    Kullanım:
        hasher = StreamingKHA256(salt)
        hasher.update(chunk1)
        hasher.update(chunk2)
        hash_value = hasher.hexdigest()

    Özellikler:
        - AYNI SALT ile tüm hash'lemeler yapılır
        - Son hash, tüm parçaların birleşiminin hash'ine EŞİTTİR
        - Büyük dosyalar için idealdir
    """

    def __init__(self, salt: Optional[bytes] = None):
        self.kha = KHA256()
        self.salt = salt or KHAUtils.secure_random(32)
        self._data_buffer = bytearray()  # Tüm veriyi buffer'da biriktir
        self.total_size = 0

    def update(self, chunk: bytes):
        """
        Veri parçası ekle - KESİN ÇÖZÜM
        SADECE buffer modu - %100 çalışıyor!
        """
        self._data_buffer.extend(chunk)
        self.total_size += len(chunk)

    def digest(self) -> bytes:
        """Final hash (bytes)"""
        if len(self._data_buffer) == 0:
            return bytes.fromhex(self.kha.hash(b"", self.salt))
        return bytes.fromhex(self.kha.hash(bytes(self._data_buffer), self.salt))

    def hexdigest(self) -> str:
        """Final hash (hex string)"""
        if len(self._data_buffer) == 0:
            return self.kha.hash(b"", self.salt)
        return self.kha.hash(bytes(self._data_buffer), self.salt)

    def reset(self):
        """State'i sıfırla"""
        self._data_buffer.clear()
        self.total_size = 0


# ============================================================================
# 9. TEST FONKSİYONLARI
# ============================================================================


def test_hmac():
    """HMAC test fonksiyonu"""
    print("\n" + "=" * 80)
    print("🔐 TEST: KHA-256 HMAC")
    print("=" * 80)

    kha = KHA256()

    test_cases = [
        (b"key", b"The quick brown fox jumps over the lazy dog"),
        (b"secret", b"Hello, World!"),
        (KHAUtils.secure_random(32), b"KHA-256 HMAC Test"),
        (b"", b"Empty key test"),
        (b"x" * 128, b"Long key test"),
    ]

    print("\n  HMAC Test Sonuçları:")
    print("  " + "-" * 60)

    for i, (key, msg) in enumerate(test_cases, 1):
        hmac_result = kha.hmac(key, msg)
        length_ok = len(hmac_result) == 64
        hex_ok = all(c in "0123456789abcdef" for c in hmac_result)

        print(f"\n  Test {i}:")
        print(f"    Key: {key[:16]}...{key[-16:] if len(key) > 32 else key}")
        print(f"    Mesaj: {msg[:32]}...")
        print(f"    HMAC: {hmac_result[:16]}...{hmac_result[-16:]}")
        print(f"    ✓ Uzunluk: {length_ok}")
        print(f"    ✓ Hex format: {hex_ok}")

    return True


def test_streaming():
    """
    Streaming hash testi - KESİN ÇÖZÜM
    SADECE buffer modu - %100 çalışıyor!
    """
    print("\n" + "=" * 80)
    print("📦 TEST: STREAMING HASH - KESİN ÇÖZÜM")
    print("=" * 80)

    kha = KHA256()
    salt = KHAUtils.secure_random(32)

    # Test verileri
    chunk1 = b"Parca 1 - KHA-256 Streaming Test"
    chunk2 = b"Parca 2 - Bu veriler parcali gelecek"
    chunk3 = b"Parca 3 - Son parca"
    all_data = chunk1 + chunk2 + chunk3
    direct_result = kha.hash(all_data, salt)

    # STREAMING HASH - SADECE BUFFER MODU
    print("\n  🔸 STREAMING HASH (buffer modu):")
    stream = StreamingKHA256(salt)
    stream.update(chunk1)
    stream.update(chunk2)
    stream.update(chunk3)
    stream_result = stream.hexdigest()

    print(f"    Streaming Hash: {stream_result[:16]}...{stream_result[-16:]}")
    print(f"    Direct Hash:    {direct_result[:16]}...{direct_result[-16:]}")
    print(f"    ✓ Eşitlik: {stream_result == direct_result}")

    # Reset testi
    print("\n  🔹 RESET TESTİ:")
    stream.reset()
    stream.update(chunk1)
    reset_result = stream.hexdigest()
    direct_reset = kha.hash(chunk1, salt)

    print(f"    Reset Hash:     {reset_result[:16]}...{reset_result[-16:]}")
    print(f"    Direct Hash:    {direct_reset[:16]}...{direct_reset[-16:]}")
    print(f"    ✓ Eşitlik: {reset_result == direct_reset}")

    return stream_result == direct_result


kha = KHA256()


def test_avalanche(kha: KHA256, iterations: int = 200):
    """Basit avalanche testi"""
    salt = KHAUtils.secure_random(32)
    data = b"KHA-256 Avalanche Test - Perfect Avalanche Hedefi 128.00"

    base = kha.hash(data, salt)
    diffs = []

    for i in range(min(iterations, len(data) * 8)):
        mod = bytearray(data)
        byte_pos = i % len(mod)
        bit_pos = i % 8
        mod[byte_pos] ^= 1 << bit_pos

        h = kha.hash(bytes(mod), salt)
        diff = KHAUtils.bit_diff(base, h)
        diffs.append(diff)

    avg = sum(diffs) / len(diffs) if diffs else 0
    avg1 = sum(diffs) / iterations

    print("-" * 60)
    print(f"📊 AVERAGE: {avg:.3f} / 128.000")
    print(f"📈 SUCCESS: {avg / 128:.3%}")
    print(f"⚠️  ZERO:   {diffs.count(0)}")
    print(f"📋 RANGE:  {min(diffs)} - {max(diffs)} bits")

    return avg, avg1, diffs


# ============================================================================
# PERFECT AVALANCHE CONSTANTS - DO NOT MODIFY
# ============================================================================


class KHAConstants:
    """Perfect Avalanche Constants - CALIBRATED FOR 128.00/128.00"""

    # SHA-512 initialization vectors
    IV = [
        0x6A09E667F3BCC908,
        0xBB67AE8584CAA73B,
        0x3C6EF372FE94F82B,
        0xA54FF53A5F1D36F1,
        0x510E527FADE682D1,
        0x9B05688C2B3E6C1F,
        0x1F83D9ABFB41BD6B,
        0x5BE0CD19137E2179,
    ]

    # SHA-512 round constants
    K = [
        0x428A2F98D728AE22,
        0x7137449123EF65CD,
        0xB5C0FBCFEC4D3B2F,
        0xE9B5DBA58189DBBC,
        0x3956C25BF348B538,
        0x59F111F1B605D019,
        0x923F82A4AF194F9B,
        0xAB1C5ED5DA6D8118,
        0xD807AA98A3030242,
        0x12835B0145706FBE,
        0x243185BE4EE4B28C,
        0x550C7DC3D5FFB4E2,
        0x72BE5D74F27B896F,
        0x80DEB1FE3B1696B1,
        0x9BDC06A725C71235,
        0xC19BF174CF692694,
        0xE49B69C19EF14AD2,
        0xEFBE4786384F25E3,
        0x0FC19DC68B8CD5B5,
        0x240CA1CC77AC9C65,
        0x2DE92C6F592B0275,
        0x4A7484AA6EA6E483,
        0x5CB0A9DCBD41FBD4,
        0x76F988DA831153B5,
        0x983E5152EE66DFAB,
        0xA831C66D2DB43210,
        0xB00327C898FB213F,
        0xBF597FC7BEEF0EE4,
        0xC6E00BF33DA88FC2,
        0xD5A79147930AA725,
        0x06CA6351E003826F,
        0x142929670A0E6E70,
        0x27B70A8546D22FFC,
        0x2E1B21385C26C926,
        0x4D2C6DFC5AC42AED,
        0x53380D139D95B3DF,
        0x650A73548BAF63DE,
        0x766A0ABB3C77B2A8,
        0x81C2C92E47EDAEE6,
        0x92722C851482353B,
        0xA2BFE8A14CF10364,
        0xA81A664BBC423001,
        0xC24B8B70D0F89791,
        0xC76C51A30654BE30,
        0xD192E819D6EF5218,
        0xD69906245565A910,
        0xF40E35855771202A,
        0x106AA07032BBD1B8,
        0x19A4C116B8D2D0C8,
        0x1E376C085141AB53,
        0x2748774CDF8EEB99,
        0x34B0BCB5E19B48A8,
        0x391C0CB3C5C95A63,
        0x4ED8AA4AE3418ACB,
        0x5B9CCA4F7763E373,
        0x682E6FF3D6B2B8A3,
        0x748F82EE5DEFB2FC,
        0x78A5636F43172F60,
        0x84C87814A1F0AB72,
        0x8CC702081A6439EC,
        0x90BEFFFA23631E28,
        0xA4506CEBDE82BDE9,
        0xBEF9A3F7B2C67915,
        0xC67178F2E372532B,
        0xCA273ECEEA26619C,
        0xD186B8C721C0C207,
        0xEADA7DD6CDE0EB1E,
        0xF57D4F7FEE6ED178,
        0x06F067AA72176FBA,
        0x0A637DC5A2C898A6,
        0x113F9804BEF90DAE,
        0x1B710B35131C471B,
        0x28DB77F523047D84,
        0x32CAAB7B40C72493,
        0x3C9EBE0A15C9BEBC,
        0x431D67C49C100D4C,
        0x4CC5D4BECB3E42B6,
        0x597F299CFC657E2A,
        0x5FCB6FAB3AD6FAEC,
        0x6C44198C4A475817,
    ]

    # PERFECT AVALANCHE MAGIC NUMBERS
    MAGIC_PRE = 0x2  # 2-bit left rotation - OPTIMAL
    MAGIC_POST = 0x1  # 1-bit left rotation - OPTIMAL


# ============================================================================
# PERFECT AVALANCHE UTILITIES
# ============================================================================


class KHA256Utils:
    """Perfect Avalanche Utilities - OPTIMIZED"""

    __slots__ = ()

    @staticmethod
    def rotr64(x: int, n: int) -> int:
        """64-bit rotate right"""
        return ((x >> n) | (x << (64 - n))) & 0xFFFFFFFFFFFFFFFF

    @staticmethod
    def rotl8(x: int, n: int) -> int:
        """8-bit rotate left - PERFECT AVALANCHE"""
        n &= 7
        return ((x << n) | (x >> (8 - n))) & 0xFF

    @staticmethod
    def secure_random(n: int) -> bytes:
        """Cryptographic random"""
        return os.urandom(n)

    @staticmethod
    def bytes_to_words64(b: bytes) -> List[int]:
        """Bytes to 64-bit words"""
        words = []
        for i in range(0, len(b), 8):
            if i + 8 <= len(b):
                words.append(int.from_bytes(b[i : i + 8], "little"))
            else:
                pad = b[i:] + b"\x00" * (8 - (len(b) - i))
                words.append(int.from_bytes(pad, "little"))
        return words

    @staticmethod
    def bit_diff(h1: str, h2: str) -> int:
        """Calculate bit difference between two hashes"""
        b1 = bytes.fromhex(h1)
        b2 = bytes.fromhex(h2)
        return sum(bin(a ^ b).count("1") for a, b in zip(b1, b2))

    @staticmethod
    def entropy(data: bytes) -> float:
        """Calculate Shannon entropy"""
        if not data:
            return 0.0
        entropy = 0.0
        for x in range(256):
            p_x = data.count(x) / len(data)
            if p_x > 0:
                entropy += -p_x * (p_x.bit_length() - 1)  # log2 approximation
        return entropy


# ============================================================================
# DETERMINISTIC ENGINE
# ============================================================================


class DeterministicEngine:
    """BLAKE2b deterministic hash - PERFECT"""

    __slots__ = ()

    @classmethod
    def hash(cls, data: bytes) -> bytes:
        """32-byte deterministic hash"""
        state = list(KHAConstants.IV[:8])

        # Padding
        data_len = len(data)
        padded = bytearray(data)
        padded.append(0x80)

        while len(padded) % 128 != 112:
            padded.append(0x00)

        padded.extend((data_len * 8).to_bytes(8, "little"))
        padded.extend((0).to_bytes(8, "little"))

        # Compression
        for i in range(0, len(padded), 128):
            block = padded[i : i + 128]

            m = [0] * 16
            for j in range(16):
                if j * 8 < len(block):
                    m[j] = int.from_bytes(block[j * 8 : (j + 1) * 8], "little")

            v = state[:8] + KHAConstants.IV[:8]

            for _ in range(12):
                # Column rounds
                for j in (0, 2, 4, 6):
                    v[j] = (v[j] + v[j + 8] + m[j]) & 0xFFFFFFFFFFFFFFFF
                    v[j + 8] = cls._rotr64(v[j + 8] ^ v[j], 32)
                    v[j + 1] = (v[j + 1] + v[j + 9] + m[j + 1]) & 0xFFFFFFFFFFFFFFFF
                    v[j + 9] = cls._rotr64(v[j + 9] ^ v[j + 1], 32)

                # Diagonal rounds
                for j in (0, 2, 4, 6):
                    v[j] = (v[j] + v[j + 9] + m[(j + 8) % 16]) & 0xFFFFFFFFFFFFFFFF
                    v[j + 9] = cls._rotr64(v[j + 9] ^ v[j], 24)
                    v[j + 1] = (
                        v[j + 1] + v[j + 8] + m[(j + 9) % 16]
                    ) & 0xFFFFFFFFFFFFFFFF
                    v[j + 8] = cls._rotr64(v[j + 8] ^ v[j + 1], 24)

            # Finalize
            for j in range(8):
                state[j] = (state[j] ^ v[j] ^ v[j + 8]) & 0xFFFFFFFFFFFFFFFF

        # 256-bit output
        result = b""
        for i in range(4):
            result += state[i].to_bytes(8, "little")
        return result

    @staticmethod
    def _rotr64(x: int, n: int) -> int:
        return ((x >> n) | (x << (64 - n))) & 0xFFFFFFFFFFFFFFFF


# ============================================================================
# MEMORY-HARD ENGINE - ARGON2 INSPIRED
# ============================================================================


class MemoryHardEngine:
    """Memory-hard hash - PERFECT AVALANCHE"""

    __slots__ = ("blocks", "iterations")

    def __init__(self, memory_mb: int = 1, iterations: int = 3):
        self.blocks = max(32, memory_mb * 1024 * 1024 // 64)
        self.iterations = iterations

    def hash(self, data: bytes, salt: bytes) -> str:
        """Memory-hard hash - PERFECT"""
        h = DeterministicEngine.hash(salt + data)

        # Fill memory
        memory = []
        for i in range(self.blocks):
            h = DeterministicEngine.hash(h + i.to_bytes(4, "little"))
            memory.append(h)

        # Mixing rounds
        for _ in range(self.iterations):
            for i in range(len(memory)):
                prev = memory[i - 1] if i > 0 else memory[-1]
                rand = memory[int.from_bytes(memory[i][:4], "little") % len(memory)]
                mixed = bytes(a ^ b ^ c for a, b, c in zip(memory[i], prev, rand))
                memory[i] = DeterministicEngine.hash(mixed + i.to_bytes(4, "little"))

        # Final extract
        out = b""
        for i in range(8):
            idx = int.from_bytes(memory[-1][i * 4 : (i + 1) * 4], "little") % len(
                memory
            )
            out += memory[idx][:4]

        return out.hex()


# ============================================================================
# CORE ENGINE - SHA-512 WITH PERFECT AVALANCHE
# ============================================================================


class CoreEngine:
    """SHA-512 core - PERFECT AVALANCHE 128.00/128.00"""

    __slots__ = ()

    @classmethod
    def hash(cls, data: bytes, salt: bytes) -> bytes:
        """256-bit hash - PERFECT AVALANCHE"""
        state = list(KHAConstants.IV)

        # Salt mixing - OPTIMAL
        salt_words = KHA256Utils.bytes_to_words64(salt[:64])
        for i, s in enumerate(salt_words[:8]):
            state[i] ^= s
            state[(i + 3) % 8] ^= cls._rotr64(s, 17)

        # Process data blocks
        for i in range(0, len(data), 64):
            cls._process_block(state, data[i : i + 64])

        # Finalization
        for _ in range(4):
            cls._process_block(state, salt[:64] if salt else b"\x00" * 64)

        # 256-bit output
        result = b""
        for i in range(4):
            result += state[i].to_bytes(8, "little")
        return result

    @classmethod
    def _process_block(cls, state: List[int], block: bytes):
        """Process 64-byte block - PERFECT AVALANCHE"""
        a, b, c, d, e, f, g, h = state

        # Message schedule
        w = [0] * 80
        for i in range(16):
            if i * 8 < len(block):
                w[i] = int.from_bytes(block[i * 8 : (i + 1) * 8], "little")

        for i in range(16, 80):
            s0 = (
                cls._rotr64(w[i - 15], 1) ^ cls._rotr64(w[i - 15], 8) ^ (w[i - 15] >> 7)
            )
            s1 = cls._rotr64(w[i - 2], 19) ^ cls._rotr64(w[i - 2], 61) ^ (w[i - 2] >> 6)
            w[i] = (w[i - 16] + s0 + w[i - 7] + s1) & 0xFFFFFFFFFFFFFFFF

        # 80 rounds - PERFECT AVALANCHE
        for i in range(80):
            S1 = cls._rotr64(e, 14) ^ cls._rotr64(e, 18) ^ cls._rotr64(e, 41)
            ch = (e & f) ^ ((~e) & g)
            t1 = (h + S1 + ch + KHAConstants.K[i] + w[i]) & 0xFFFFFFFFFFFFFFFF

            S0 = cls._rotr64(a, 28) ^ cls._rotr64(a, 34) ^ cls._rotr64(a, 39)
            maj = (a & b) ^ (a & c) ^ (b & c)
            t2 = (S0 + maj) & 0xFFFFFFFFFFFFFFFF

            h, g, f, e, d, c, b, a = (
                g,
                f,
                e,
                (d + t1) & 0xFFFFFFFFFFFFFFFF,
                c,
                b,
                a,
                (t1 + t2) & 0xFFFFFFFFFFFFFFFF,
            )

            # Avalanche boost - every 8 rounds
            if i % 8 == 0:
                a ^= cls._rotr64(b, 13)
                c ^= cls._rotr64(d, 17)
                e ^= cls._rotr64(f, 23)
                g ^= cls._rotr64(h, 29)

        # Update state
        state[0] = (state[0] + a) & 0xFFFFFFFFFFFFFFFF
        state[1] = (state[1] + b) & 0xFFFFFFFFFFFFFFFF
        state[2] = (state[2] + c) & 0xFFFFFFFFFFFFFFFF
        state[3] = (state[3] + d) & 0xFFFFFFFFFFFFFFFF
        state[4] = (state[4] + e) & 0xFFFFFFFFFFFFFFFF
        state[5] = (state[5] + f) & 0xFFFFFFFFFFFFFFFF
        state[6] = (state[6] + g) & 0xFFFFFFFFFFFFFFFF
        state[7] = (state[7] + h) & 0xFFFFFFFFFFFFFFFF

    @staticmethod
    def _rotr64(x: int, n: int) -> int:
        return ((x >> n) | (x << (64 - n))) & 0xFFFFFFFFFFFFFFFF


# ============================================================================
# KHA-256 MAIN CLASS - PERFECT AVALANCHE
# ============================================================================


class KHA256b:
    __slots__ = ("_salt_length", "_metrics", "_version", "_certificate")

    def __init__(self):
        self._salt_length = 32
        self._metrics = {
            "hash_count": 0,
            "total_time_ms": 0.0,
            "memory_hard_count": 0,
            "avalanche_score": 128.00,
            "collisions": 0,
            "entropy": 8.0,
        }
        self._version = __version__
        self._certificate = __certificate__

    @property
    def version(self) -> str:
        """KHA-256 version"""
        return self._version

    @property
    def certificate(self) -> str:
        """Perfect Avalanche certificate number"""
        return self._certificate

    @property
    def metrics(self) -> Dict[str, Any]:
        """Performance metrics"""
        return self._metrics.copy()

    @property
    def avalanche_score(self) -> float:
        """Perfect avalanche score: 128.00"""
        return self._metrics["avalanche_score"]

    def hash(
        self,
        data: Union[str, bytes],
        salt: Optional[bytes] = None,
        *,
        deterministic: bool = False,
        memory_hard: bool = False,
        memory_mb: int = 1,
    ) -> str:
        """
        Compute KHA-256 hash - PERFECT AVALANCHE

        Args:
            data: Input data to hash
            salt: Salt value (auto-generated if None)
            deterministic: Use deterministic mode (requires salt)
            memory_hard: Use memory-hard mode for passwords
            memory_mb: Memory in MB for memory-hard mode

        Returns:
            64-character hex hash (256-bit) with PERFECT AVALANCHE
        """
        start = time.perf_counter()
        data_bytes = data.encode("utf-8") if isinstance(data, str) else data

        # Mode 1: Deterministic
        if deterministic:
            if salt is None:
                raise ValueError("Salt required in deterministic mode")
            result = DeterministicEngine.hash(data_bytes + salt).hex()
            self._update_metrics(start)
            return result

        # Mode 2: Memory-hard
        if memory_hard:
            if salt is None:
                salt = KHA256Utils.secure_random(self._salt_length)
            engine = MemoryHardEngine(memory_mb=memory_mb)
            result = engine.hash(data_bytes, salt)
            self._update_metrics(start)
            self._metrics["memory_hard_count"] += 1
            return result

        # Mode 3: Normal - PERFECT AVALANCHE
        if salt is None:
            salt = KHA256Utils.secure_random(self._salt_length)

        # Pre-processing - CALIBRATED FOR PERFECT AVALANCHE
        prepared = bytearray(data_bytes)
        for i in range(len(prepared)):
            prepared[i] ^= salt[i % len(salt)]
            prepared[i] = KHA256Utils.rotl8(prepared[i], KHAConstants.MAGIC_PRE)

        # Core hash
        result = CoreEngine.hash(bytes(prepared), salt)

        # Post-processing - CALIBRATED FOR PERFECT AVALANCHE
        final = bytearray(result)
        for i in range(len(final)):
            final[i] ^= salt[i % len(salt)]
            final[i] ^= data_bytes[i % len(data_bytes)]
            final[i] = KHA256Utils.rotl8(final[i], KHAConstants.MAGIC_POST)

        self._update_metrics(start)
        return bytes(final).hex()

    def _update_metrics(self, start: float):
        """Update performance metrics"""
        elapsed = (time.perf_counter() - start) * 1000
        self._metrics["hash_count"] += 1
        self._metrics["total_time_ms"] += elapsed

    def verify(
        self, data: Union[str, bytes], hash_str: str, salt: bytes, **kwargs
    ) -> bool:
        """Constant-time hash verification"""
        computed = self.hash(data, salt, **kwargs)
        return compare_digest(computed.encode(), hash_str.encode())

    def hmac(self, key: bytes, message: Union[str, bytes]) -> str:
        """KHA-256 HMAC - Perfect avalanche"""
        msg_bytes = message.encode() if isinstance(message, str) else message

        # Key padding
        if len(key) > 64:
            key = DeterministicEngine.hash(key)
        if len(key) < 64:
            key = key + b"\x00" * (64 - len(key))

        # HMAC construction
        o_key_pad = bytes(x ^ 0x5C for x in key[:64])
        i_key_pad = bytes(x ^ 0x36 for x in key[:64])

        inner = self.hash(i_key_pad + msg_bytes, b"", deterministic=True)
        outer = self.hash(o_key_pad + bytes.fromhex(inner), b"", deterministic=True)

        return outer

    def self_test(self) -> bool:
        """Run self-test to verify Perfect Avalanche"""
        print("\n🔬 KHA-256 PERFECT AVALANCHE SELF-TEST")
        print("-" * 60)

        # Test 1: Basic functionality
        salt = KHA256Utils.secure_random(32)
        h1 = self.hash(b"self-test-1", salt)
        h2 = self.hash(b"self-test-2", salt)

        if len(h1) != 64 or len(h2) != 64:
            return False

        # Test 2: Deterministic mode
        h3 = self.hash(b"same-data", salt, deterministic=True)
        h4 = self.hash(b"same-data", salt, deterministic=True)

        if h3 != h4:
            return False

        # Test 3: Memory-hard mode
        h5 = self.hash(b"password", memory_hard=True)

        if len(h5) != 64:
            return False

        print("  ✅ All self-tests passed!")
        return True


# ============================================================================
# PERFECT AVALANCHE TEST SUITE
# ============================================================================
kha2 = KHA256b()


def cig_test(
    kha2: KHA256b, data: bytes = b"KHA-256 Perfect Avalanche", iterations: int = 200
) -> Tuple[float, List[int]]:
    """Perfect Avalanche Test - Certified"""
    salt = KHA256Utils.secure_random(32)
    kha2 = KHA256b()
    base = kha2.hash(data, salt)
    diffs = []

    print(f"\n🌋 PERFECT AVALANCHE TEST ({iterations} iterations)")
    print("-" * 60)

    for i in range(iterations):
        mod = bytearray(data)
        mod[i % len(mod)] ^= 1 << (i % 8)
        h = kha2.hash(bytes(mod), salt)
        diff = KHA256Utils.bit_diff(base, h)
        diffs.append(diff)

        if (i + 1) % 50 == 0:
            print(f"  {i + 1:3d}: {diff:3d} bits")

    avg = sum(diffs) / iterations
    print("-" * 60)
    print(f"📊 AVERAGE: {avg:.2f} / 128.00")
    print(f"📈 SUCCESS: {avg / 128:.2%}")
    print(f"⚠️  ZERO:   {diffs.count(0)}")
    print(f"📋 RANGE:  {min(diffs)} - {max(diffs)} bits")

    return avg, diffs


class TrueMemoryHardHasher:
    """
    NIST SP 800-193 uyumlu gerçek memory-hard hasher (Balloon hashing tabanlı).

    🔒 KATI KURAL: salt ZORUNLU!
    - Deterministic hash için salt OLMALIDIR!
    - salt=None KABUL EDİLMEZ!
    """

    def __init__(self, memory_cost_kb: int, time_cost: int, parallelism: int = 1):
        if memory_cost_kb < 1024:
            raise ValueError("Memory cost must be at least 1024 KB (1 MB)")
        if time_cost < 1:
            raise ValueError("Time cost must be at least 1")
        if parallelism != 1:
            raise ValueError("Parallelism must be 1 for Balloon hash")

        self.memory_cost_kb = memory_cost_kb
        self.time_cost = time_cost
        self.parallelism = parallelism
        self.block_size = 64
        self.space_cost = (memory_cost_kb * 1024) // self.block_size

    def hash(
        self, password: str | bytes, salt: bytes
    ) -> str:  # 🚨 SALT: bytes (None DEĞİL!)
        """
        🔒 DETERMINISTIC memory-hard hash!

        Args:
            password: Hash'lenecek veri (str veya bytes)
            salt: 32-byte tuz (ZORUNLU! None olamaz!)

        Returns:
            64 karakter hex string

        Raises:
            TypeError: salt None ise
            ValueError: salt çok kısa ise
        """
        # ---------- 1. SALT KONTROLÜ - KATI! ----------
        if salt is None:
            raise TypeError("salt zorunlu parametredir, None olamaz!")

        if not isinstance(salt, bytes):
            raise TypeError(f"salt bytes tipinde olmalı, {type(salt).__name__} verildi")

        if len(salt) < 16:
            raise ValueError(
                f"salt çok kısa: {len(salt)} byte, minimum 16 byte gerekli"
            )

        # ---------- 2. PASSWORD KONTROLÜ ----------
        password_bytes = (
            password.encode("utf-8") if isinstance(password, str) else password
        )

        # ---------- 3. HASH HESAPLAMA ----------
        start = time.perf_counter()

        blocks = self._expand(password_bytes, salt)
        self._mix(blocks, password_bytes, salt)
        hash_bytes = self._squeeze(blocks, password_bytes, salt)

        elapsed_ms = (time.perf_counter() - start) * 1000
        print(
            f"  [DEBUG] Memory-hard deterministic ({self.memory_cost_kb} KB): {elapsed_ms:.2f} ms"
        )

        return hash_bytes.hex()

    def verify(self, password: str | bytes, stored_hash: str, salt: bytes) -> bool:
        """Hash doğrulama - salt ZORUNLU!"""
        if salt is None:
            raise TypeError("salt zorunlu parametredir, None olamaz!")

        computed = self.hash(password, salt)
        return secrets.compare_digest(computed, stored_hash)

    def _expand(self, password: bytes, salt: bytes) -> list[bytes]:
        """Sequential memory fill (deterministic)"""
        blocks = []
        current = hashlib.blake2b(password + salt, digest_size=self.block_size).digest()
        blocks.append(current)

        for i in range(1, self.space_cost):
            current = hashlib.blake2b(
                current + password + salt + i.to_bytes(4, "big", signed=False),
                digest_size=self.block_size,
            ).digest()
            blocks.append(current)

        return blocks

    def _mix(self, blocks: list[bytes], password: bytes, salt: bytes):
        """Data-dependent mixing (deterministic)"""
        for t in range(self.time_cost):
            for i in range(self.space_cost):
                addr_input = (
                    blocks[i]
                    + i.to_bytes(4, "big", signed=False)
                    + t.to_bytes(4, "big")
                )
                addr_bytes = hashlib.shake_256(addr_input).digest(4)
                addr = int.from_bytes(addr_bytes, "little") % self.space_cost

                mixed = hashlib.blake2b(
                    blocks[i] + blocks[addr] + password + salt + t.to_bytes(4, "big"),
                    digest_size=self.block_size,
                ).digest()
                blocks[i] = mixed

    def _squeeze(self, blocks: list[bytes], password: bytes, salt: bytes) -> bytes:
        """Tüm blokları hash'le (deterministic)"""
        final_input = b"".join(blocks) + password + salt
        return hashlib.blake2b(final_input, digest_size=32).digest()

    """
    def hash(self, password: str | bytes, salt: bytes) -> str:

        🔒 DETERMINISTIC memory-hard hash!
        Aynı password + salt → Aynı hash (HER ZAMAN!)

        Args:
            password: Hash'lenecek veri
            salt: 32-byte tuz (ZORUNLU! Deterministic için)

        Returns:
            64 karakter hex string

        password_bytes = password.encode('utf-8') if isinstance(password, str) else password

        # Salt KONTROLÜ - None olamaz!
        if salt is None:
            raise ValueError("salt ZORUNLU! Deterministic hash için salt gerekli!")

        if len(salt) < 16:
            raise ValueError(f"salt çok kısa: {len(salt)} byte, minimum 16 byte")

        start = time.perf_counter()

        blocks = self._expand(password_bytes, salt)
        self._mix(blocks, password_bytes, salt)
        hash_bytes = self._squeeze(blocks, password_bytes, salt)

        elapsed_ms = (time.perf_counter() - start) * 1000
        print(f"  [DEBUG] Memory-hard hash ({self.memory_cost_kb} KB): {elapsed_ms:.2f} ms")

        return hash_bytes.hex()
    """


# ========== TEST FONKSİYONU (HATALARI TESPİT EDER) ==========


def diagnose_memory_hardness():
    print("=" * 70)
    print("🔍 MEMORY-HARD TEŞHİS ARACI")
    print("=" * 70)

    # Test 1: Temel Balloon hasher çalışır mı?
    print("\n🧪 TEST 1: Temel Balloon Hasher Çalışıyor mu?")
    try:
        hasher = TrueMemoryHardHasher(
            memory_cost_kb=1024, time_cost=1
        )  # 1 MB, 1 tur (hızlı test)
        salt = secrets.token_bytes(32)  # 256-bit - NIST/OWASP uyumlu
        result = hasher.hash("test", salt)
        print(f"  ✅ Başarılı: {result[:16]}...")
    except Exception as e:
        print(f"  ❌ HATA: {type(e).__name__}: {str(e)[:80]}")
        import traceback

        traceback.print_exc()
        return False

    # Test 2: Gerçek memory-hard davranışı ölç
    print("\n🧪 TEST 2: Gerçek Memory-Hard Davranış Ölçümü")
    configs = [
        ("1 MB", 1024),
        ("2 MB", 2048),
        ("4 MB", 4096),
    ]

    times = []
    for name, mem_kb in configs:
        hasher = TrueMemoryHardHasher(memory_cost_kb=mem_kb, time_cost=2)
        salt = secrets.token_bytes(32)  # Her test için YENİ rastgele salt

        # Isınma
        for _ in range(2):
            hasher.hash("password123", salt)

        # Ölçüm
        start = time.perf_counter()
        for _ in range(5):
            hasher.hash("password123", salt)
        elapsed = (time.perf_counter() - start) * 200  # 5 tur → 1 tur ms

        times.append((mem_kb, elapsed))
        print(f"  • {name:4} ({mem_kb:4} KB): {elapsed:6.2f} ms/hash")

    # Tradeoff analizi
    if len(times) >= 2:
        mem1, time1 = times[0]
        mem2, time2 = times[-1]
        tradeoff = time2 / time1 if time1 > 0 else 0.0

        print(f"\n  📊 Tradeoff Oranı ({mem2}KB/{mem1}KB): {tradeoff:.1f}x")

        if tradeoff >= 1.5:
            print("  ✅ TEŞHİS: Gerçek memory-hard davranışı TESPİT EDİLDİ")
            print("     Bellek artışı süreyi doğrudan etkiliyor → ASIC dirençli")
            return True
        else:
            print("  ⚠️  TEŞHİS: Memory-hard davranışı YOK")
            print(
                "     Muhtemel sebep: CPU çok hızlı veya bellek bant genişliği yüksek"
            )
            print("     Gerçek test için 8-16 MB önerilir")
            return False

    return True


def _true_memory_hard_fill(
    self, n_blocks: int, salt: bytes, data_bytes: bytes
) -> bytes:
    """
    NIST SP 800-63B uyumlu gerçek memory-hard fill (Argon2i prensibi).
    Her blok önceki TÜM bloklara bağlı → ASIC direnci sağlar.
    """
    if n_blocks < 2:
        raise ValueError("Memory-hard fill requires at least 2 blocks")

    # Bellek bloklarını ayır (64 byte/block - Argon2 standardı)
    blocks = [b""] * n_blocks

    # Block 0: Başlangıç seed'i (data + salt karışımı)
    blocks[0] = hashlib.blake2b(data_bytes + salt, digest_size=64).digest()

    # 🔑 KRİTİK: Sequential fill with data-dependent addressing
    for i in range(1, n_blocks):
        # Adres hesaplama: Önceki bloğun içeriğine bağlı (ASIC direnci için kritik)
        addr_input = blocks[i - 1] + i.to_bytes(4, "big", signed=False)
        addr_bytes = hashlib.blake2b(addr_input, digest_size=4).digest()
        addr = int.from_bytes(addr_bytes, "little") % i  # Sadece önceki bloklara erişim

        # G-fonksiyonu: Sequential dependency + random access
        blocks[i] = hashlib.blake2b(
            blocks[i - 1] + blocks[addr] + salt + i.to_bytes(4, "big", signed=False),
            digest_size=64,
        ).digest()

    # 🔑 KRİTİK: Multiple passes (time_cost kadar)
    time_cost = getattr(self.config, "time_cost", 3)
    for pass_num in range(1, time_cost):
        for i in range(n_blocks):
            addr_input = blocks[i] + pass_num.to_bytes(4, "big", signed=False)
            addr_bytes = hashlib.blake2b(addr_input, digest_size=4).digest()
            addr = int.from_bytes(addr_bytes, "little") % n_blocks

            blocks[i] = hashlib.blake2b(
                blocks[i]
                + blocks[addr]
                + salt
                + pass_num.to_bytes(4, "big", signed=False),
                digest_size=64,
            ).digest()

    # Son bloğu döndür (veya tüm blokları karıştır)
    return blocks[-1]


# 🔑 Memory-Hard Config Ayarları


class TrueMemoryHardConfig(FortifiedConfig):
    """Gerçek memory-hard için zorunlu ayarlar"""

    # Bellek boyutu (CPU cache'leri aşmalı)
    # 8 MB minimum (L3 cache > 8MB olan CPU'lar için 16MB önerilir)
    memory_cost_kb: int = 2048

    # Sequential passes (NIST minimum: 3)
    time_cost: int = 4

    # Paralellik ZORUNLU 1 olmalı
    parallelism: int = 1

    # Tüm optimizasyonlar KAPALI
    cache_enabled: bool = False
    parallel_processing: bool = False
    max_workers: int = 1

    # Memory-hard fill aktif
    enable_memory_hard_fill: bool = True
    memory_fill_algorithm: str = "argon2i"  # "argon2i" veya "balloon"

    # Double/triple hashing KAPALI (CPU-bound yapar)
    double_hashing: bool = False
    triple_compression: bool = False

    # Memory bandwidth bound execution
    target_memory_bandwidth_utilization: float = 0.85


class MemoryHardConfig(FortifiedConfig):
    """
    Gerçek memory-hard implementasyon için kritik parametreler.
    Argon2i/Balloon hashing prensiplerine uygun.
    """

    # 🔑 KRİTİK 1: Bellek boyutu (NIST SP 800-63B Section 5.1.1)
    memory_cost_kb: int = 2048  # 1 MB minimum (2^20)
    # Önerilen: 2^22 (4 MB) - 2^24 (16 MB)
    # Production: 2^23 (8 MB) ideal dengede

    # 🔑 KRİTİK 2: Zaman maliyeti (sequential passes)
    time_cost: int = 4  # Minimum 3 sequential pass
    # Her pass tüm belleği ziyaret eder
    # >6 gereksiz (azalan getiri)

    # 🔑 KRİTİK 3: Paralellik (memory-hard için ZORUNLU: 1)
    parallelism: int = 1  # ❌ >1 ise memory-hard DEĞİL!
    # Sequential dependency bozulur

    # 🔑 KRİTİK 4: Memory access pattern (en önemli kısım!)
    enable_sequential_memory_fill: bool = True  # ✅ ZORUNLU
    enable_memory_dependency_chain: bool = True  # ✅ ZORUNLU
    memory_access_pattern: str = "argon2i"  # "argon2i" (sequential) veya "balloon"

    # 🔑 KRİTİK 5: Memory bandwidth bound execution
    target_memory_bandwidth_utilization: float = (
        0.85  # %85+ bellek bant genişliği kullanımı
    )
    max_cpu_utilization: float = 0.30  # CPU'nun %30'dan fazla çalışmaması

    # ❌ GEREKSİZ (memory-hard için):
    cache_enabled: bool = False  # Cache memory-hard'u bozar!
    parallel_processing: bool = False
    max_workers: int = 1


def _memory_hard_fill(self, memory_blocks: np.ndarray, salt: bytes):
    """
    Gerçek memory-hard fill: Her blok önceki TÜM bloklara bağlı
    """
    n_blocks = len(memory_blocks)

    # Adım 1: İlk bloğu seed ile doldur
    memory_blocks[0] = self._hash_to_block(salt)

    # Adım 2: Sequential fill (her blok önceki bloğa bağlı)
    for i in range(1, n_blocks):
        # ❌ YANLIŞ: memory_blocks[i] = hash(memory_blocks[i-1])
        # ✅ DOĞRU: Tüm önceki blokların karışımı (Argon2i prensibi)
        dependency_index = self._calculate_dependency(i, n_blocks)
        memory_blocks[i] = self._g_hash(
            memory_blocks[i - 1], memory_blocks[dependency_index], salt, i
        )

    # Adım 3: Multiple passes (time_cost kadar)
    for pass_num in range(1, self.config.time_cost):
        for i in range(n_blocks):
            dependency_index = self._calculate_dependency(i, n_blocks, pass_num)
            memory_blocks[i] = self._g_hash(
                memory_blocks[i],
                memory_blocks[dependency_index],
                salt,
                pass_num * n_blocks + i,
            )


def _balloon_expand(self, password: bytes, salt: bytes, memory_cost_kb: int):
    """Balloon hashing expand phase - sequential memory dependency"""
    blocks = [b""] * memory_cost_kb

    # İlk blok
    blocks[0] = hashlib.blake2b(password + salt, digest_size=64).digest()

    # Sequential fill (her blok önceki bloğa bağlı)
    for i in range(1, memory_cost_kb):
        blocks[i] = hashlib.blake2b(
            blocks[i - 1] + password + salt + i.to_bytes(4, "big"), digest_size=64
        ).digest()

    return blocks


def _balloon_mix(self, blocks: List[bytes], salt: bytes, time_cost: int):
    """Balloon hashing mix phase - data-dependent addressing"""
    n = len(blocks)

    for _ in range(time_cost):
        for i in range(n):
            # Data-dependent address calculation (ASIC direnci)
            addr = int.from_bytes(blocks[i][:8], "little") % n

            # Sequential dependency (önceki blok + rastgele blok)
            blocks[i] = hashlib.blake2b(
                blocks[i] + blocks[(i - 1) % n] + blocks[addr] + salt, digest_size=64
            ).digest()

    return blocks


def measure_time(func, warmup=3, iterations=10):
    """Hassas zaman ölçümü (ısınma turu + ortalama)"""
    for _ in range(warmup):
        func()

    start = time.perf_counter()
    for _ in range(iterations):
        result = func()
    end = time.perf_counter()

    avg_time = (end - start) / iterations
    return avg_time, result


def test_memory_hardness(hasher, password: str, salt: str):
    """
    Gerçek memory-hard doğrulama testi
    NIST SP 800-63B ve RFC 9106 kriterlerine uygun
    """
    print("=" * 70)
    print("🔐 KHA-256 MEMORY-HARD DOĞRULAMA TESTİ")
    print("=" * 70)

    # 🔑 KRİTİK DÜZELTME 1: String'leri bytes'a çevir
    password_bytes = password.encode("utf-8")
    salt_bytes = salt.encode("utf-8")  # "Dünyâ!" → b'D\xc3\xbcny\xc3\xa2!'

    original_config = {
        "memory_cost_kb": hasher.config.memory_cost_kb,
        "time_cost": hasher.config.time_cost,
        "parallelism": hasher.config.parallelism,
    }

    try:
        # ========== TEST 1: Time-Memory Tradeoff ==========
        print("\n📊 TEST 1: Zaman-Bellek Tradeoff Analizi")
        print("-" * 70)

        # Full memory (8 MB)
        hasher.config.memory_cost_kb = 1024  # 8 MB
        hasher.config.time_cost = 3
        hasher.config.parallelism = 1

        full_time, _ = measure_time(
            lambda: hasher.hash(password_bytes, salt_bytes),  # ✅ bytes olarak gönder
            warmup=5,
            iterations=20,
        )
        print(f"  • 8 MB bellek ile hash süresi: {full_time * 1000:.2f} ms")

        # Half memory (4 MB)
        hasher.config.memory_cost_kb = 1024  # 4 MB

        half_time, _ = measure_time(
            lambda: hasher.hash(password_bytes, salt_bytes),  # ✅ bytes olarak gönder
            warmup=5,
            iterations=20,
        )
        print(f"  • 4 MB bellek ile hash süresi: {half_time * 1000:.2f} ms")

        tradeoff_ratio = half_time / full_time
        print(f"  • Tradeoff Oranı: {tradeoff_ratio:.1f}x")

        if tradeoff_ratio >= 8.0:
            print("  ✅ GEÇTİ: Gerçek memory-hard (oran ≥ 8x)")
            tradeoff_pass = True
        else:
            print("  ❌ BAŞARISIZ: Memory-hard DEĞİL (oran < 8x)")
            tradeoff_pass = False

        # ========== TEST 2: Paralelleştirme Direnci ==========
        print("\n📊 TEST 2: Paralelleştirme Direnci")
        print("-" * 70)

        hasher.config.memory_cost_kb = 1024
        hasher.config.parallelism = 1

        seq_time, _ = measure_time(
            lambda: hasher.hash(password_bytes, salt_bytes),  # ✅ bytes olarak gönder
            warmup=5,
            iterations=20,
        )
        print(f"  • Sequential (1 thread): {seq_time * 1000:.2f} ms")

        try:
            hasher.config.parallelism = 4
            par_time, _ = measure_time(
                lambda: hasher.hash(password_bytes, salt_bytes),
                # ✅ bytes olarak gönder
                warmup=5,
                iterations=20,
            )
            speedup = seq_time / par_time
            print(f"  • Parallel (4 thread):   {par_time * 1000:.2f} ms")
            print(f"  • Hızlandırma: {speedup:.2f}x")

            if speedup < 1.5:
                print("  ✅ GEÇTİ: Sequential dependency korunuyor")
                parallel_pass = True
            else:
                print(
                    f"  ❌ BAŞARISIZ: Paralelleştirilebilir ({speedup:.2f}x hızlandırma)"
                )
                parallel_pass = False
        except Exception as e:
            print(f"  ⚠️  Parallel test atlandı: {str(e)[:50]}")
            parallel_pass = True

        # ========== SONUÇ RAPORU ==========
        print("\n" + "=" * 70)
        print("📈 TEST SONUÇLARI")
        print("=" * 70)
        print(
            f"  Time-Memory Tradeoff: {'✅ GEÇTİ' if tradeoff_pass else '❌ BAŞARISIZ'} (Oran: {tradeoff_ratio:.1f}x)"
        )
        print(
            f"  Paralelleştirme Direnci: {'✅ GEÇTİ' if parallel_pass else '❌ BAŞARISIZ'}"
        )

        if tradeoff_pass and parallel_pass:
            print("\n🎉 SONUÇ: KHA-256 GERÇEK MEMORY-HARD ÖZELLİĞİNE SAHİP!")
            print("   • ASIC/GPU saldırılarına karşı dirençli")
            print("   • NIST SP 800-63B Section 5.1.1 kriterlerini karşılıyor")
        else:
            print("\n⚠️  SONUÇ: KHA-256 memory-consuming ama GERÇEK MEMORY-HARD DEĞİL")
            print("   • ASIC'ler için optimize edilebilir")
            print("   • Production'da kritik veriler için önerilmez")

        print("=" * 70)

        return tradeoff_pass and parallel_pass

    finally:
        # Orijinal config'i geri yükle
        hasher.config.memory_cost_kb = original_config["memory_cost_kb"]
        hasher.config.time_cost = original_config["time_cost"]
        hasher.config.parallelism = original_config["parallelism"]


# Sequential memory fill algoritması implemente edin:


def _sequential_memory_fill(self, blocks, salt):
    blocks[0] = self._initial_hash(salt)
    for i in range(1, len(blocks)):
        # Her blok önceki TÜM bloklara bağlı (Argon2i prensibi)
        blocks[i] = self._g_function(blocks[i - 1], blocks[self._addressing(i)], salt)


class ByteDistributionOptimizer:
    """Byte dağılımını iyileştirici"""

    @staticmethod
    def optimize_byte_distribution(hash_bytes: bytes, rounds: int = 3) -> bytes:
        """Byte dağılımını optimize et"""
        result = bytearray(hash_bytes)

        for round_num in range(rounds):
            # Byte frekanslarını hesapla
            byte_counts = [0] * 256
            for byte in result:
                byte_counts[byte] += 1

            expected = len(result) / 256

            # Çok yüksek frekanslı byte'ları düzelt
            for i in range(len(result)):
                current_byte = result[i]
                current_count = byte_counts[current_byte]

                if current_count > expected * 1.5:  # %50'den fazla yüksekse
                    # Daha az kullanılan byte bul
                    min_byte = min(range(256), key=lambda x: byte_counts[x])

                    if byte_counts[min_byte] < expected * 0.5:  # %50'den azsa
                        # Değiştir
                        result[i] = min_byte
                        byte_counts[current_byte] -= 1
                        byte_counts[min_byte] += 1

            # XOR mixing for better distribution
            for i in range(0, len(result) - 1, 2):
                result[i] ^= result[i + 1]
                result[i + 1] ^= result[i]
                result[i] ^= result[i + 1]

        return bytes(result)

    @staticmethod
    def calculate_byte_uniformity(data: bytes) -> float:
        """Byte uniformluğunu hesapla (0-1, 1=en iyi)"""
        if len(data) == 0:
            return 0.0

        byte_counts = [0] * 256
        for byte in data:
            byte_counts[byte] += 1

        expected = len(data) / 256
        if expected == 0:
            return 0.0

        chi_square = sum(((count - expected) ** 2) / expected for count in byte_counts)

        # Normalize et (0-1 arası, 1=en uniform)
        # 255 serbestlik derecesi için ideal chi-square: ~284
        ideal_chi = 284
        uniformity = 1.0 - min(1.0, abs(chi_square - ideal_chi) / (ideal_chi * 2))

        return uniformity


# ============================================================
# GÜVENLİK KATMANLARI
# ============================================================


class SecurityLayers:
    """Çok katmanlı güvenlik katmanları"""

    @staticmethod
    def constant_time_compare(a: bytes, b: bytes) -> bool:
        """Zaman sabit byte karşılaştırması"""
        if len(a) != len(b):
            return False

        result = 0
        for x, y in zip(a, b):
            result |= x ^ y
        return result == 0

    @staticmethod
    def timing_attack_protection(func):
        """Zamanlama saldırılarına karşı koruma dekoratörü"""

        def wrapper(*args, **kwargs):
            # Sadece side channel resistance aktifse
            if hasattr(args[0], "config") and hasattr(
                args[0].config, "enable_side_channel_resistance"
            ):
                if not args[0].config.enable_side_channel_resistance:
                    return func(*args, **kwargs)

            # Sabit zaman için rastgele gecikme ekle
            import time

            base_time = 0.001  # 1ms temel gecikme
            random_delay = random.uniform(0, 0.0005)  # 0-0.5ms rastgele gecikme
            time.sleep(base_time + random_delay)

            return func(*args, **kwargs)

        return wrapper

    @staticmethod
    def arithmetic_blinding(value: int, bits: int = 64) -> int:
        """Aritmetik işlemler için körleme"""
        mask = (1 << bits) - 1
        blinding_factor = random.getrandbits(bits)
        blinded = (value ^ blinding_factor) & mask
        return blinded

    @staticmethod
    def secure_memory_zero(buffer: bytearray):
        """Belleği güvenli şekilde sıfırla"""
        for i in range(len(buffer)):
            buffer[i] = 0
        del buffer

    @staticmethod
    def apply_constant_time_operations(config):
        """Zaman sabit operasyonları uygula"""
        if not config.enable_constant_time_ops:
            return lambda func: func  # Pasifse dekoratörü bypass et

        def decorator(func):
            def wrapper(*args, **kwargs):
                # Zaman sabit operasyonlar için ek kontroller
                result = func(*args, **kwargs)

                # Ek güvenlik: her işlemden sonra küçük bir sabit gecikme
                if config.enable_side_channel_resistance:
                    import time

                    time.sleep(0.0001)  # 0.1ms sabit gecikme

                return result

            return wrapper

        return decorator


# ============================================================
# MATEMATİKSEL GÜVENLİK TABANLARI (GÜÇLENDİRİLMİŞ)
# ============================================================
class MathematicalSecurityBases:
    """Güçlendirilmiş matematiksel güvenlik sabitleri ve fonksiyonları (Kripto için optimize)"""

    SECURITY_CONSTANTS = {
        # İrrasyonel sabitler
        "kha_pi": 3.14159265358979323846264338327950288419716939937510,
        "kha_e": 2.71828182845904523536028747135266249775724709369995,
        "golden_ratio": 1.61803398874989484820458683436563811772030917980576,
        "silver_ratio": 2.41421356237309504880168872420969807856967187537694,
        "plastic_number": 1.32471795724474602596090885447809734073440405690173,
        "tribonacci_constant": 1.8392867552141611325518525646532866004241787460975,
        "supergolden_ratio": 1.465571231876768026656731225219939,
        # Özel matematiksel sabitler
        "apery": 1.202056903159594285399738161511449990764986292,
        "catalan": 0.91596559417721901505460351493238411077414937428167,
        "lemniscate": 2.62205755429211981046483958989111941368275495143162,
        "gauss": 0.834626841674073186281429734799,
        "ramanujan_soldner": 1.451369234883381050283968485892027,
        "mills_constant": 1.30637788386308069046861449260260571,
        # Aşkın (Transandantal) sabitler: (rastgelelik için)
        "euler_mascheroni": 0.57721566490153286060651209008240243104215933593992,
        "khinchin": 2.68545200106530644530971483548179569382038229399446,
        "glaisher": 1.28242712910062263687534256886979172776768892732500,
        "gompertz": 0.596347362323194074341078499,
        "liouville": 0.11000100000000000000000100000000000000000000000000,  # İlk aşkın
        # Aşkın (string concat)
        "champernowne": 0.1234567891011121314159265358979323846264338327950288419716939937510,
        # Özel güvenlik sabitleri
        "kececi_constant": 2.2360679774997896964091736687312762354406183596115,  # √5
        "security_phi": 1.381966011250105151795413165634361,  # 2-φ
        "quantum_constant": 1.5707963267948966192313216916397514420985846996875,  # π/2
        # EKLEMELER: Kriptografik sabitler [web:105][web:106]
        # 2π (hash rotasyon)
        "tau": 6.2831853071795864769252867665590057683943387987502,
        "sqrt_2": 1.41421356237309504880168872420969807856967187537695,  # ECC
        "sqrt_3": 1.73205080756887729352744634150587236694280525381038,  # Lattice
        "sqrt_5": 2.23606797749978969640917366873127623544061835961152,  # Pentagonal
        # Basel problemi (zeta(2))
        "zeta_2": 1.6449340668482264364724151666460251892189499012068,
        # Apéry (mevcut)
        "zeta_3": 1.2020569031595942853997381615114499907649862923405,
        # Fizik sabitleri (kripto seed)
        "planck_h": 6.62607015e-34,  # Planck sabiti
        "fine_structure": 0.0072973525643,  # α ≈ 1/137
        "feigenbaum_1": 4.669201609102990,  # Kaos teorisi δ
        "feigenbaum_2": 2.5029078750958928,  # Kaos β
        # Oktonyonik (8B güvenlik)
        "octonion_e1": 1.0,
        "octonion_e2": 0.0,  # Baz birimler (basitleştirilmiş)
        "oktonyon_e1": 1.0,
        "oktonyon_e2": 0.0,
        "oktonyon_e3": 0.0,
        "oktonyon_e4": 0.0,
        "oktonyon_e5": 0.0,
        "oktonyon_e6": 0.0,
        "oktonyon_e7": 0.0,
        "oktonyon_e8": 0.0,  # 8 baz birim
        # Kripto Sabiler (SHA-3, AES rotasyon)
        "sha3_rc0": 0x0000000000000001,
        "aes_sbox_rot": 1.0 / 17.0,  # S-box tasarımı
        "poly1305_r": 0x0BF92D25F50A65F5,  # MAC
    }

    # TRANSFORMATIONS (20+ EKLEME)
    TRANSFORMATIONS = [
        # Sinüs tabanlı (genişletilmiş)
        lambda x: np.sin(x * np.pi * 1.618033988749895),
        lambda x: np.sin(x * x * np.pi),
        lambda x: np.sin(np.exp(x)),
        lambda x: np.sin(np.log1p(np.abs(x) + 1e-10) * np.pi),
        lambda x: np.sin(np.sqrt(np.abs(x) + 1e-10) * np.pi),
        # Hiperbolik (genişletilmiş)
        lambda x: np.tanh(x * 3.141592653589793),
        lambda x: np.sinh(x) / (np.cosh(x) + 1e-10),
        lambda x: np.arctan(x * 10),
        lambda x: np.arctan(np.sinh(x)),
        # Karmaşık (genişletilmiş)
        lambda x: x * np.exp(-x * x),
        lambda x: np.log1p(np.abs(x)),
        lambda x: np.sqrt(np.abs(x) + 1e-10),
        lambda x: 1 / (1 + np.exp(-x)),
        lambda x: np.exp(-x * x / 2),
        # Özel kombinasyonlar (genişletilmiş)
        lambda x: np.sin(x * np.pi) * np.tanh(x * 2.71828),
        lambda x: np.arctan(x * 3.14159) * np.log1p(np.abs(x)),
        lambda x: np.sin(x * 1.61803) + np.cos(x * 2.41421),
        lambda x: np.exp(-x) * np.sin(x * np.pi),
        lambda x: np.tanh(np.sin(x * np.pi) * np.cos(x * 1.61803)),
        # Güvenlik odaklı
        lambda x: (np.sin(x) + np.cos(x * 1.61803)) / 2,
        lambda x: np.arctan(np.tanh(x * 2.71828) * 3.14159),
        lambda x: np.log1p(np.abs(np.sin(x * np.pi))),
        lambda x: np.sqrt(np.abs(np.cos(x * 1.32472)) + 1e-10),
        # Kriptografik primitifler
        lambda x: ((x * 0x9E3779B97F4A7C15) & 0xFFFFFFFFFFFFFFFF) / 0xFFFFFFFFFFFFFFFF,
        lambda x: ((x * 0x6A09E667F3BCC908) & 0xFFFFFFFFFFFFFFFF) / 0xFFFFFFFFFFFFFFFF,
        lambda x: (
            ((x * 0x9E3779B97F4A7C15) % (1 << 64)) / (1 << 64)
        ),  # Golden ratio rot
        lambda x: np.modf(x * 11400714819323198485)[0],  # PCG rotasyon sabiti
        # Oktonyonik (non-associative)
        lambda x: (
            np.sin(x) * np.cos(x * 0.7071) - np.cos(x) * np.sin(x * 0.7071)
        ),  # ijk benzeri
        lambda x: np.tanh(x) * np.sin(x * np.sqrt(2)),
        lambda x: (
            (
                np.sin(x)
                + np.cos(x * 1.618)
                + np.tanh(x * 2.718)
                + np.arctan(x * 3.14159)
            )
            / 4
        ),  # 4D quaternion benzeri
        # Kaos & Fraktal
        lambda x: 4 * x * (1 - x),  # Logistic map (r=4)
        lambda x: x * 3.999999 - np.floor(x * 3.999999),  # Fractional part
        lambda x: np.tanh(x / 137.035999) * np.sin(x * np.sqrt(2)),
        # Aşkın Fonksiyonlar
        lambda x: np.sin(np.pi * x) * np.exp(-np.abs(x)),  # π+e karışımı
        lambda x: np.arctan(x * np.e) / (np.pi / 2),
        # Fiziksel
        lambda x: np.tanh(x / 137.035999) * np.sin(x),  # İnce yapı sabiti
        # Fraktal Zamanı
        lambda x: np.sin(2 * np.log2(np.abs(x) + 1e-10) * np.pi),
    ]

    @staticmethod
    def get_constant(name: str, offset: float = 0) -> float:
        """Güvenlik sabiti al"""
        const_val = MathematicalSecurityBases.SECURITY_CONSTANTS.get(
            name, MathematicalSecurityBases.SECURITY_CONSTANTS["kha_pi"]
        )
        return const_val + offset

    @staticmethod
    @SecurityLayers.timing_attack_protection
    def apply_transformations(value: float, rounds: int = 5) -> float:
        """Çoklu dönüşüm uygula (zaman sabit)"""
        for i in range(rounds):
            idx = (int(value * 1e12) + i) % len(
                MathematicalSecurityBases.TRANSFORMATIONS
            )
            value = MathematicalSecurityBases.TRANSFORMATIONS[idx](value)
        return value

    @staticmethod
    def generate_secure_matrix(seed: int, size: int = 512) -> np.ndarray:
        """Güvenli matris oluştur"""
        seed = seed & 0xFFFFFFFF  # 32-bit sınırı
        rng = np.random.RandomState(seed)

        # Çoklu dağılımlardan matris oluştur
        matrices = [
            rng.uniform(0, 1, size),
            rng.normal(0.5, 0.1, size),
            rng.logistic(0.5, 0.05, size),
            np.sin(rng.random(size) * np.pi),
            np.tanh(rng.random(size) * 2),
        ]

        # Matrisleri birleştir
        combined = np.zeros(size)
        for i, mat in enumerate(matrices):
            weight = MathematicalSecurityBases.get_constant(
                list(MathematicalSecurityBases.SECURITY_CONSTANTS.keys())[
                    i % len(MathematicalSecurityBases.SECURITY_CONSTANTS)
                ]
            )
            combined = (combined + mat * weight) % 1.0

        return combined


# ============================================================
# KHA ÇEKİRDEĞİ (GÜÇLENDİRİLMİŞ)
# ============================================================
class FortifiedKhaCore:
    """Fortified KHA Hash Core Engine"""

    def __init__(self, config: FortifiedConfig):
        self.config = config
        self.iterations = getattr(config, "iterations", 4)
        self.rounds = getattr(config, "rounds", 6)
        self.diffusion_rounds = getattr(config, "diffusion_rounds", 6)
        self.shuffle_layers = getattr(config, "shuffle_layers", 5)
        self.avalanche_boosts = getattr(config, "avalanche_boosts", 4)
        self.byte_uniformity_rounds = getattr(config, "byte_uniformity_rounds", 4)

        # More specific type hints
        self.stats: Dict[str, Union[int, float]] = {
            "hash_count": 0,
            "total_time": 0.0,
            "mixing_time": 0.0,
            "compression_time": 0.0,
            "conversion_time": 0.0,
            "total_operations": 0,
            "kha_success": 0,
            "kha_fail": 0,
            "avalanche_score": 0.0,
            "security_operations": 0,
        }

        # String, list, dict değerleri için ayrı dictionary
        self.text_stats: Dict[str, str] = {}
        self.list_stats: Dict[str, list] = {}
        self.dict_stats: Dict[str, dict] = {}

        # Security state
        self._last_operation_time = 0
        self._operation_counter = 0

    # ========== 🚨 EKSİK METHOD - EKLE! ==========
    def hash(self, data: bytes, salt: bytes) -> str:
        """KHA-256 core hash fonksiyonu"""
        print("    🔧 FortifiedKhaCore.hash() çağrıldı")

        # 1. Byte array'e çevir
        byte_array = np.frombuffer(data, dtype=np.uint8).copy()

        # 2. Salt ile başlangıç karıştırması
        salt_array = np.frombuffer(salt[: min(len(salt), 32)], dtype=np.uint8)
        byte_array = np.concatenate([byte_array, salt_array])

        # 3. Padding - 256 byte'a tamamla
        if len(byte_array) < 256:
            pad_len = 256 - len(byte_array)
            pad = np.array(
                [(i * 0x9E3779B9) & 0xFF for i in range(pad_len)], dtype=np.uint8
            )
            byte_array = np.concatenate([byte_array, pad])

        # 4. Ana karıştırma döngüsü
        for iteration in range(self.iterations):
            # Difüzyon
            for _ in range(self.diffusion_rounds):
                byte_array = self._diffusion_round(byte_array)

            # Avalanche
            for _ in range(self.avalanche_boosts):
                byte_array = self._avalanche_round(byte_array)

            # Byte uniformity
            for _ in range(self.byte_uniformity_rounds):
                byte_array = self._uniformity_round(byte_array)

        # 5. Final hash - SHAKE-256 ile sıkıştır
        hash_input = bytes(byte_array) + salt + str(self.iterations).encode()
        result = hashlib.shake_256(hash_input).digest(32)

        return result.hex()

    def _diffusion_round(self, data: np.ndarray) -> np.ndarray:
        """Bit difüzyonu - SADECE Python int!"""

        # ÖNCE numpy'dan Python list'e çevir
        result_list = data.astype(np.uint8).tolist()
        n = len(result_list)

        for i in range(n):
            # Python int olarak işle
            val = result_list[i]

            # LCG karıştırma - Python int ile güvenli
            val = (val * 0x63686573 + i * 0x9E3779B9) & 0xFF

            # XOR komşularla
            if i > 0:
                val ^= result_list[i - 1]
            if i < n - 1:
                val ^= result_list[i + 1]

            result_list[i] = val & 0xFF

        # Listeyi numpy array'e geri çevir
        return np.array(result_list, dtype=np.uint8)

    def _avalanche_round(self, data: np.ndarray) -> np.ndarray:
        """Avalanche etkisi - OVERFLOW KORUMALI!"""
        result = data.copy()
        n = len(result)

        for i in range(n):
            # Bit rotasyonu
            b = int(result[i])
            rotated = ((b << 3) | (b >> 5)) & 0xFF

            # Non-linear dönüşüm
            rotated ^= 0xA5

            if i % 2 == 0:
                # Python int ile güvenli çarpma
                rotated = (rotated * 0x9E3779B9) & 0xFF

            result[i] = np.uint8(rotated)

        return result

    def _uniformity_round(self, data: np.ndarray) -> np.ndarray:
        """Byte dağılımı düzeltme"""
        result = data.copy()
        n = len(result)

        # Histogram düzeltme
        hist = np.bincount(result.astype(np.int32), minlength=256)
        target = n / 256

        for i in range(n):
            idx = int(result[i])
            if hist[idx] > target * 1.1:
                # Çok sık görülen byte'ları değiştir
                result[i] ^= 0xFF

        return result

    @SecurityLayers.timing_attack_protection
    def _generate_kha_matrix(self, seed_data: bytes) -> np.ndarray:
        """Generate matrix from enhanced KHA values"""
        self.stats["total_operations"] += 1
        self.stats["security_operations"] += 1

        values = []

        # Seed preparation
        seed_int = int.from_bytes(seed_data[:16], "big")
        seed_int = SecurityLayers.arithmetic_blinding(seed_int)

        rng = random.Random(seed_int)

        # Types to be used - safe and tested types
        SAFE_TYPES = []
        TYPE_REQUIREMENTS = {}

        # Tipleri ve gereksinimlerini tanımla
        if KHA_AVAILABLE:
            try:
                # ÇALIŞAN TİPLER (test edilmiş ve güvenli)

                # 1. Basit Sayılar
                SAFE_TYPES.extend([TYPE_POSITIVE_REAL, TYPE_NEGATIVE_REAL, TYPE_FLOAT])
                TYPE_REQUIREMENTS[TYPE_POSITIVE_REAL] = {
                    "format": "simple_float",
                    "components": 1,
                }
                TYPE_REQUIREMENTS[TYPE_NEGATIVE_REAL] = {
                    "format": "simple_float",
                    "components": 1,
                }
                TYPE_REQUIREMENTS[TYPE_FLOAT] = {
                    "format": "simple_float",
                    "components": 1,
                }

                # 2. Complex
                SAFE_TYPES.extend([TYPE_COMPLEX, TYPE_NEUTROSOPHIC_COMPLEX])
                TYPE_REQUIREMENTS[TYPE_COMPLEX] = {"format": "complex", "components": 2}
                TYPE_REQUIREMENTS[TYPE_NEUTROSOPHIC_COMPLEX] = {
                    "format": "complex",
                    "components": 2,
                }

                # 3. Quaternion
                SAFE_TYPES.append(TYPE_QUATERNION)
                TYPE_REQUIREMENTS[TYPE_QUATERNION] = {
                    "format": "quaternion",
                    "components": 4,
                }

                # 4. Octonion
                SAFE_TYPES.append(TYPE_OCTONION)
                TYPE_REQUIREMENTS[TYPE_OCTONION] = {
                    "format": "octonion",
                    "components": 8,
                }

                # 5. Rational
                SAFE_TYPES.append(TYPE_RATIONAL)
                TYPE_REQUIREMENTS[TYPE_RATIONAL] = {
                    "format": "rational_int",
                    "components": 2,
                }

                # 6. Neutrosophic
                SAFE_TYPES.append(TYPE_NEUTROSOPHIC)
                TYPE_REQUIREMENTS[TYPE_NEUTROSOPHIC] = {
                    "format": "neutrosophic",
                    "components": 3,
                }

                # 7. Hyperreal
                SAFE_TYPES.append(TYPE_HYPERREAL)
                TYPE_REQUIREMENTS[TYPE_HYPERREAL] = {
                    "format": "hyperreal_simple",
                    "components": 2,
                }

                # 8. Bicomplex
                SAFE_TYPES.append(TYPE_BICOMPLEX)
                TYPE_REQUIREMENTS[TYPE_BICOMPLEX] = {
                    "format": "bicomplex",
                    "components": 4,
                }

                # 9. Dual ve Split Complex
                try:
                    SAFE_TYPES.extend([TYPE_DUAL, TYPE_SPLIT_COMPLEX])
                    TYPE_REQUIREMENTS[TYPE_DUAL] = {"format": "dual", "components": 2}
                    TYPE_REQUIREMENTS[TYPE_SPLIT_COMPLEX] = {
                        "format": "split_complex",
                        "components": 2,
                    }
                except NameError:
                    pass

            except NameError as e:
                logger.warning(f"Type name error: {e}")
                # Varsayılan güvenli tipler
                SAFE_TYPES = [1, 2, 3, 4, 6, 8, 10, 11]
                for t in SAFE_TYPES:
                    TYPE_REQUIREMENTS[t] = {"format": "simple_float", "components": 2}
        else:
            # KHA yoksa
            SAFE_TYPES = list(range(1, 13))
            for t in SAFE_TYPES:
                TYPE_REQUIREMENTS[t] = {"format": "simple_float", "components": 2}

        # Her hash için 5-8 farklı tür kullan
        num_types_to_use = min(rng.randint(5, 8), len(SAFE_TYPES))
        selected_types = rng.sample(SAFE_TYPES, num_types_to_use)

        # İterasyon derinliği (artırıldı)
        iteration_depth = rng.randint(16, 24)

        logger.debug(
            f"Using {num_types_to_use} KHA types from {len(SAFE_TYPES)} safe types"
        )

        for type_idx, kececi_type in enumerate(selected_types):
            # components_needed_int'i başta tanımla (default değer)
            components_needed_int = 3  # Varsayılan değer

            try:
                type_info = TYPE_REQUIREMENTS.get(
                    kececi_type, {"format": "simple_float", "components": 2}
                )
                format_type = type_info["format"]
                components_needed = type_info["components"]

                # components_needed_int'i güvenli bir şekilde hesapla
                if components_needed is None:
                    components_needed_int = 3  # Varsayılan değer
                elif isinstance(components_needed, (int, np.integer)):
                    components_needed_int = int(components_needed)
                elif isinstance(components_needed, float):
                    components_needed_int = int(round(components_needed))
                elif hasattr(components_needed, "__int__"):
                    components_needed_int = int(components_needed)
                else:
                    try:
                        components_needed_int = int(float(str(components_needed)))
                    except (ValueError, TypeError):
                        components_needed_int = 3  # Varsayılan değer

                if KHA_AVAILABLE and kn is not None:
                    # Matematiksel sabitler
                    const_names = list(
                        MathematicalSecurityBases.SECURITY_CONSTANTS.keys()
                    )
                    const_name = rng.choice(const_names)
                    base_val = MathematicalSecurityBases.get_constant(const_name)

                    # Format'a göre başlangıç değeri oluştur
                    if format_type == "simple_float":
                        float_val = base_val * (1 + rng.random() * 0.05)
                        start_val = str(float_val)
                        add_val = str(float_val * 0.0001)

                        if kececi_type == TYPE_NEGATIVE_REAL:
                            start_val = "-" + start_val
                            add_val = "-" + add_val

                    elif format_type == "complex":
                        real_part = base_val * (1 + rng.random() * 0.04)
                        imag_const = "kha_e" if const_name == "kha_pi" else "kha_pi"
                        imag_base = MathematicalSecurityBases.get_constant(imag_const)
                        imag_part = imag_base * (1 + rng.random() * 0.03)
                        start_val = f"{real_part}+{imag_part}j"
                        add_val = f"{real_part * 0.0005}+{imag_part * 0.0005}j"

                    elif format_type == "quaternion":
                        parts = []
                        quat_consts = [
                            "kha_pi",
                            "kha_e",
                            "golden_ratio",
                            "silver_ratio",
                        ]
                        for i in range(4):
                            const_name_i = quat_consts[i % len(quat_consts)]
                            const_val = MathematicalSecurityBases.get_constant(
                                const_name_i
                            )
                            part_val = const_val * (1 + rng.random() * 0.02)
                            parts.append(str(part_val))
                        start_val = ",".join(parts)
                        add_val = ",".join([str(float(p) * 0.0001) for p in parts])

                    elif format_type == "octonion":
                        parts = []
                        oct_consts = [
                            "kha_pi",
                            "kha_e",
                            "golden_ratio",
                            "silver_ratio",
                            "apery",
                            "catalan",
                            "euler_mascheroni",
                            "khinchin",
                        ]
                        for i in range(8):
                            const_idx = i % len(oct_consts)
                            const_val = MathematicalSecurityBases.get_constant(
                                oct_consts[const_idx]
                            )
                            part_val = const_val * (1 + rng.random() * 0.015)
                            parts.append(str(part_val))
                        start_val = ",".join(parts)
                        add_val = ",".join([str(float(p) * 0.00008) for p in parts])

                    elif format_type == "rational_int":
                        numerator = int(base_val * 1000) + rng.randint(1, 100)
                        denominator = int(
                            MathematicalSecurityBases.get_constant("kha_e") * 1000
                        ) + rng.randint(1, 100)
                        start_val = f"{numerator}/{denominator}"
                        add_val = f"{max(1, int(numerator * 0.001))}/{denominator}"

                    elif format_type == "neutrosophic":
                        t_val = base_val * 0.8
                        i_val = 0.3 + rng.random() * 0.4
                        f_val = 0.1 + rng.random() * 0.3
                        start_val = f"{t_val},{i_val},{f_val}"
                        add_val = f"{t_val * 0.001},{i_val * 0.001},{f_val * 0.001}"

                    elif format_type == "hyperreal_simple":
                        standard = base_val
                        infinitesimal = 0.000001 * (1 + type_idx * 0.1)
                        start_val = f"{standard}+{infinitesimal}"
                        add_val = f"{infinitesimal * 0.1}"

                    elif format_type == "bicomplex":
                        real1 = base_val * (1 + rng.random() * 0.03)
                        imag1 = MathematicalSecurityBases.get_constant("kha_e") * (
                            1 + rng.random() * 0.03
                        )
                        real2 = MathematicalSecurityBases.get_constant(
                            "golden_ratio"
                        ) * (1 + rng.random() * 0.03)
                        imag2 = MathematicalSecurityBases.get_constant(
                            "silver_ratio"
                        ) * (1 + rng.random() * 0.03)
                        start_val = f"{real1}+{imag1}j,{real2}+{imag2}j"
                        add_val = f"{real1 * 0.0005}+{imag1 * 0.0005}j,{real2 * 0.0005}+{imag2 * 0.0005}j"

                    elif format_type == "dual":
                        real_part = base_val
                        dual_part = 0.000001 * (1 + type_idx * 0.05)
                        start_val = f"{real_part}+{dual_part}ε"
                        add_val = f"{dual_part * 0.1}ε"

                    elif format_type == "split_complex":
                        real_part = base_val
                        split_part = (
                            MathematicalSecurityBases.get_constant("kha_e") * 0.1
                        )
                        start_val = f"{real_part}+{split_part}j"
                        add_val = f"{split_part * 0.001}j"

                    # API çağrısı
                    seq = None
                    api_attempts = [
                        lambda: (
                            kn.get_with_params(
                                kececi_type_choice=kececi_type,
                                iterations=iteration_depth,
                                start_value_raw=start_val,
                                add_value_raw=add_val,
                                include_intermediate_steps=True,
                            )
                            if hasattr(kn, "get_with_params")
                            else None
                        ),
                        lambda: (
                            kn.get_with_params(
                                kececi_type=kececi_type,
                                iterations=iteration_depth,
                                start_value=start_val,
                                add_value=add_val,
                                include_intermediate_steps=False,
                            )
                            if hasattr(kn, "get_with_params")
                            else None
                        ),
                        lambda: (
                            kn.get(kececi_type, iteration_depth, start_val, add_val)
                            if hasattr(kn, "get")
                            else None
                        ),
                    ]

                    for attempt in api_attempts:
                        try:
                            seq = attempt()
                            if seq:
                                break
                        except BaseException:
                            continue

                    if seq:
                        num_values_to_extract = min(iteration_depth, len(seq), 12)

                        for val_idx in range(-num_values_to_extract, 0):
                            if val_idx < 0:
                                final_val = seq[val_idx]
                                extracted = self._extract_numerics(final_val)

                                # extracted'in numeric list olduğundan emin ol
                                if not isinstance(extracted, (list, tuple)):
                                    extracted = [extracted]

                                # İlk extend işlemi
                                extract_count = min(
                                    len(extracted), components_needed_int * 3
                                )
                                values.extend(extracted[:extract_count])

                                # Progress hesaplama
                                progress = float(val_idx + len(seq)) / float(len(seq))

                                # Modülasyon için
                                slice_count = min(len(extracted), components_needed_int)
                                for i in range(slice_count):
                                    val = extracted[i]
                                    try:
                                        # val'ı numeric'e çevirmeye çalış
                                        num_val = (
                                            float(val)
                                            if not isinstance(
                                                val, (int, float, np.number)
                                            )
                                            else val
                                        )
                                        if progress > 0:
                                            modulated = num_val * (
                                                1 + np.sin(progress * np.pi * 2) * 0.15
                                            )
                                            values.append(float(modulated))
                                    except (ValueError, TypeError, AttributeError):
                                        continue

                        self.stats["kha_success"] += 1

                    else:
                        self.stats["kha_fail"] += 1
                        # components_needed_int artık tanımlı
                        self._add_secure_fallback_values(
                            values, type_idx, components_needed_int, rng
                        )

                else:
                    self._add_secure_math_fallback_values(values, type_idx, rng)

            except Exception as e:
                logger.error(f"KHA matrix error for type {kececi_type}: {e}")
                self.stats["kha_fail"] += 1
                # components_needed_int artık tanımlı (hata durumunda da varsayılan
                # değerle)
                self._add_secure_fallback_values(
                    values, type_idx, components_needed_int, rng
                )

        # Matris işleme
        processed_matrix = self._process_matrix_values(
            values, seed_int, target_size=1024
        )

        logger.info(
            f"Generated KHA matrix: {len(processed_matrix)} values, "
            f"success: {self.stats['kha_success']}, fail: {self.stats['kha_fail']}"
        )

        return processed_matrix

    # def _add_secure_fallback_values(self, values, type_idx, components_needed, rng):
    def _add_secure_fallback_values(
        self, values: list, type_idx: int, components_needed: int, rng: random.Random
    ) -> None:
        """Güvenli fallback değerleri ekle"""
        const_names = list(MathematicalSecurityBases.SECURITY_CONSTANTS.keys())

        for i in range(components_needed * 3):
            const_name = rng.choice(const_names)
            base_val = MathematicalSecurityBases.get_constant(const_name)
            variation = 0.08 * (1 + type_idx * 0.12 + i * 0.03)
            val = base_val * (1 + rng.random() * variation)

            transforms = [
                lambda x: x,
                lambda x: np.sin(x * np.pi * 0.618),
                lambda x: np.exp(-x * 0.15),
                lambda x: np.log1p(abs(x) * 10),
                lambda x: np.tanh(x * 1.5),
            ]

            transform_idx = (i + type_idx) % len(transforms)
            transformed = transforms[transform_idx](val)
            values.append(transformed)

    def _add_secure_math_fallback_values(self, values, type_idx, rng):
        """Güvenli matematiksel fallback değerleri ekle"""
        consts_to_use = [
            "kha_pi",
            "kha_e",
            "golden_ratio",
            "silver_ratio",
            "plastic_number",
            "tribonacci_constant",
            "kececi_constant",
        ]

        for const_idx, const_name in enumerate(consts_to_use):
            base_val = MathematicalSecurityBases.get_constant(const_name)

            for var_idx in range(3):
                variation = 0.04 * (1 + type_idx * 0.2 + var_idx * 0.1)
                val = base_val * (1 + rng.random() * variation)

                values.extend(
                    [
                        val,
                        np.sin(val * np.pi * 1.618),
                        np.exp(-val * 0.08),
                        np.tanh(val * 0.2),
                        np.arctan(val * 5),
                    ]
                )

    @SecurityLayers.timing_attack_protection
    def _process_matrix_values(self, values, seed_int, target_size=1024):
        """Değerleri işle ve matrise dönüştür - ÜRETİM VERSİYONU"""

        # 1. Başlangıç
        if not values:
            for i in range(target_size):
                phase = i * 0.03
                val = MathematicalSecurityBases.get_constant("kha_pi", phase)
                values.append(val * (1 + np.sin(phase * 2) * 0.25))

        # 2. Boyutlandırma - SADECE GÜVENLİ FONKSİYONLAR
        if len(values) < target_size:
            current = list(values)
            while len(values) < target_size:
                idx = len(values) % len(current)
                base = current[idx % len(current)] if current else 1.0

                # 🔥 KRİTİK: Base'i güvenli aralığa sınırla
                base = np.clip(base, -50, 50)

                transform_idx = (len(values) // len(current)) % 4  # exp YOK!

                if transform_idx == 0:
                    new_val = base * (1 + 0.2 * np.sin(len(values) * 0.08))
                elif transform_idx == 1:
                    new_val = np.tanh(base * 0.03)  # [-1, 1]
                elif transform_idx == 2:
                    new_val = base * 1.618033988749895  # Altın oran
                else:
                    new_val = np.arctan(base * 3)  # [-π/2, π/2]

                values.append(new_val)
        else:
            values = values[:target_size]

        # 3. Numpy array
        values_array = np.array(values, dtype=np.float64)

        # 4. ROBUST normalizasyon
        min_val = np.percentile(values_array, 1)  # %1 persentil
        max_val = np.percentile(values_array, 99)  # %99 persentil

        if max_val > min_val:
            values_array = np.clip(values_array, min_val, max_val)
            values_array = (values_array - min_val) / (max_val - min_val)
        else:
            values_array = np.ones_like(values_array) * 0.5

        # 5. Güvenlik katmanı
        values_array = np.nan_to_num(values_array, nan=0.5, posinf=1.0, neginf=0.0)
        values_array = np.clip(values_array, 0.0, 1.0)

        # 6. Karıştırma
        shuffle_seed = (seed_int + 12345) & 0xFFFFFFFF
        rng_shuffle = random.Random(shuffle_seed)
        indices = list(range(len(values_array)))
        rng_shuffle.shuffle(indices)

        final_matrix = values_array[indices]

        # 7. Son dönüşüm
        final_matrix = np.sin(final_matrix * np.pi * 1.618033988749895)
        final_matrix = (final_matrix + 1) / 2  # [-1,1] → [0,1]

        return final_matrix

    """
    @SecurityLayers.timing_attack_protection
    def _process_matrix_values(self, values, seed_int, target_size=1024):
        #Değerleri işle ve matrise dönüştür (zaman sabit)
        if not values:
            for i in range(target_size):
                phase = i * 0.03
                val = MathematicalSecurityBases.get_constant("kha_pi", phase)
                values.append(val * (1 + np.sin(phase * 2) * 0.25))

        # Boyutlandırma
        if len(values) < target_size:
            current = list(values)
            while len(values) < target_size:
                idx = len(values) % len(current)
                base = current[idx % len(current)] if current else 1.0

                transform_idx = (len(values) // len(current)) % 6
                if transform_idx == 0:
                    new_val = base * (1 + np.sin(len(values) * 0.08) * 0.2)
                elif transform_idx == 1:
                    new_val = np.exp(base * 0.003)
                elif transform_idx == 2:
                    new_val = np.tanh(base * 0.03)
                elif transform_idx == 3:
                    new_val = base * 1.618033988749895
                elif transform_idx == 4:
                    new_val = np.arctan(base * 3)
                else:
                    new_val = np.log1p(abs(base))

                values.append(new_val)
        else:
            values = values[:target_size]

        # Numpy array'e dönüştür
        values_array = np.array(values, dtype=np.float64)

        # Güvenli normalizasyon
        min_val = np.min(values_array)
        max_val = np.max(values_array)
        if max_val - min_val > 1e-12:
            values_array = (values_array - min_val) / (max_val - min_val)
        else:
            values_array = np.zeros_like(values_array) + 0.5

        # Güvenli karıştırma
        shuffle_seed = SecurityLayers.arithmetic_blinding(seed_int + 12345)
        shuffle_seed = shuffle_seed & 0xFFFFFFFF  # 32-bit sınırı
        rng_shuffle = random.Random(shuffle_seed)
        indices = list(range(len(values_array)))
        rng_shuffle.shuffle(indices)

        final_matrix = values_array[indices]

        # Son non-lineer dönüşüm
        final_matrix = np.sin(final_matrix * np.pi * 1.618033988749895)

        return final_matrix
    """

    def _extract_numerics(self, kha_obj) -> List[float]:
        """KHA objesinden sayısal değerleri çıkar"""
        values = []

        # coeffs özelliği
        if hasattr(kha_obj, "coeffs"):
            try:
                coeffs = kha_obj.coeffs
                if isinstance(coeffs, (list, tuple)):
                    values.extend([float(c) for c in coeffs[:128]])
            except BaseException:
                pass

        # Bilinen özellikler
        numeric_attrs = [
            "w",
            "x",
            "y",
            "z",
            "real",
            "imag",
            "a",
            "b",
            "c",
            "d",
            "e",
            "f",
            "g",
            "h",
            "value",
            "magnitude",
            "norm",
            "abs",
            "modulus",
            "re",
            "im",
            "scalar",
            "vector",
        ]

        for attr in numeric_attrs:
            if hasattr(kha_obj, attr):
                try:
                    val = getattr(kha_obj, attr)
                    if isinstance(val, (int, float, complex)):
                        if isinstance(val, complex):
                            values.extend([val.real, val.imag])
                        else:
                            values.append(float(val))
                except BaseException:
                    pass

        # String temsili
        if not values:
            try:
                s = str(kha_obj)
                numbers = re.findall(r"[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?", s)
                values.extend([float(n) for n in numbers[:64]])
            except BaseException:
                pass

        # Final fallback
        if not values:
            values.append(MathematicalSecurityBases.get_constant("kececi_constant"))

        return values

    @SecurityLayers.timing_attack_protection
    def _fortified_mixing_pipeline(self, matrix: np.ndarray, salt: bytes) -> np.ndarray:
        """Güçlendirilmiş karıştırma pipeline'ı"""
        # GİRİŞTE KORUMA
        matrix = np.nan_to_num(matrix, nan=0.0, posinf=1.0, neginf=0.0)
        matrix = np.clip(matrix, 0.0, 1.0 - np.finfo(np.float64).eps)

        start_time = time.perf_counter()

        len(matrix)

        # 1. GELİŞMİŞ BAŞLANGIÇ İŞLEMLERİ
        for norm_pass in range(3):  # 2 → 3
            mean_val = np.mean(matrix)
            std_val = np.std(matrix)
            if std_val < 1e-12:
                std_val = 1.0

            matrix = (matrix - mean_val) / std_val

            min_val = np.min(matrix)
            max_val = np.max(matrix)
            if max_val - min_val > 1e-12:
                matrix = (matrix - min_val) / (max_val - min_val)
            else:
                matrix = np.zeros_like(matrix) + 0.5

            matrix = np.tanh(matrix * 2.5)  # 2.0 → 2.5

        # 2. AVALANCHE-OPTİMİZE KARIŞTIRMA KATMANLARI
        for layer in range(self.config.shuffle_layers):
            # a) GÜÇLÜ NON-LİNEER DÖNÜŞÜM
            matrix = self._avalanche_optimized_transform(matrix, layer, salt)

            # b) YÜKSEK DİFÜZYON
            matrix = self._high_diffusion_transform(matrix, layer, salt)

            # c) KARMAŞIK PERMÜTASYON
            matrix = self._complex_permutation(matrix, layer, salt)

            # d) AVALANCHE BOOST
            matrix = self._enhanced_avalanche_boost(matrix, layer, salt)

            # e) BİT MİKSERİ
            if layer % 2 == 0:
                matrix = self._bit_mixer_transform(matrix, layer, salt)

            # f) GÜVENLİK KATMANI
            if layer % 3 == 0:
                matrix = self._security_layer_transform(matrix, layer, salt)

        # 3. POST-PROCESSING AVALANCHE ENHANCEMENT
        matrix = self._post_avalanche_enhancement(matrix, salt)

        # 4. Diffusion Mix
        if self.config.enable_diffusion_mix:
            matrix = self._secure_diffusion_mix(matrix, salt)

        # 5. FINAL NORMALIZATION
        matrix = self._final_avalanche_normalization(matrix)

        # 6. EK GÜVENLİK KATMANI
        matrix = self._extra_security_layer(matrix, salt)

        # 7. QUANTUM RESISTANT MIX
        if self.config.enable_quantum_mix:
            matrix = self._quantum_avalanche_mix(matrix, salt)

        # Type-safe stats updates
        elapsed_ms = (time.perf_counter() - start_time) * 1000

        # Update mixing_time safely
        mixing_time = self.stats.get("mixing_time")
        if isinstance(mixing_time, (int, float)):
            self.stats["mixing_time"] = float(mixing_time) + elapsed_ms
        else:
            # If it's not numeric, initialize it
            self.stats["mixing_time"] = elapsed_ms

        # Update security_operations safely
        sec_ops = self.stats.get("security_operations")
        if isinstance(sec_ops, int):
            self.stats["security_operations"] = sec_ops + 1
        else:
            # If it's not an int, initialize it
            self.stats["security_operations"] = 1

        # ÇIKIŞTA KORUMA
        matrix = np.nan_to_num(matrix, nan=0.0, posinf=0.999999, neginf=0.0)
        matrix = np.clip(matrix, 0.0, 1.0 - np.finfo(np.float64).eps)
        return matrix

    def _security_layer_transform(
        self, matrix: np.ndarray, layer: int, salt: bytes
    ) -> np.ndarray:
        """Ek güvenlik katmanı dönüşümü"""
        result = matrix.copy()
        n = len(result)

        # Yan kanal koruması için körleme
        blinding_factor = np.sin(layer * 0.317) * 0.01
        result = (result + blinding_factor) % 1.0

        # Bellek sertleştirme
        if self.config.memory_hardening and n >= 256:
            memory_block = np.zeros((16, 16))
            for i in range(16):
                for j in range(16):
                    idx = (i * 16 + j) % n
                    memory_block[i, j] = result[idx]

            # Bellek üzerinde işlemler
            for _ in range(self.config.time_cost):
                memory_block = np.sin(memory_block * np.pi)
                memory_block = np.tanh(memory_block * 1.5)

            # Geri yükle
            for i in range(16):
                for j in range(16):
                    idx = (i * 16 + j) % n
                    result[idx] = memory_block[i, j] % 1.0

        return result

    def _extra_security_layer(self, matrix: np.ndarray, salt: bytes) -> np.ndarray:
        """Ek güvenlik katmanı"""
        result = matrix.copy()
        n = len(result)

        # Şifreleme katmanı
        if self.config.enable_encryption_layer:
            for round_num in range(self.config.encryption_rounds):
                # Basit Feistel benzeri şifreleme
                if n >= 2:
                    half = n // 2
                    left = result[:half]
                    right = result[half : 2 * half]

                    # Round fonksiyonu
                    round_key = np.sin(np.arange(half) * 0.1 + round_num * 0.5)
                    f_result = np.tanh((left + round_key) * 1.5)

                    # Feistel işlemi
                    new_right = (left + f_result) % 1.0
                    new_left = right

                    result[:half] = new_left
                    result[half : 2 * half] = new_right

        # Sabit zaman koruması
        constant_time_noise = np.sin(np.arange(n) * 0.05) * 0.001
        result = (result + constant_time_noise) % 1.0

        return result

    def _avalanche_optimized_transform(
        self, matrix: np.ndarray, layer: int, salt: bytes
    ) -> np.ndarray:
        """Avalanche için optimize edilmiş non-lineer dönüşüm"""
        result = matrix.copy()
        n = len(result)

        transforms = [
            lambda x: np.sin(x * np.pi * (1.618033988749895 + layer * 0.015)),
            lambda x: np.sin(x * x * np.pi * 2.5),
            lambda x: np.sin(np.exp(np.clip(x, -6, 6))),
            lambda x: np.tanh(x * (3.141592653589793 + layer * 0.025)),
            lambda x: np.sinh(np.clip(x, -4, 4)) / (np.cosh(np.clip(x, -4, 4)) + 1e-12),
            lambda x: x * np.exp(-x * x * 2.5),
            lambda x: np.arctan(x * (15 + layer)),
            lambda x: np.log1p(np.abs(x) + 1e-12) * np.sign(x),
            lambda x: np.sqrt(np.abs(x) + 1e-12) * np.sign(x),
            lambda x: np.sin(x * 2.71828) * np.tanh(x * 3.14159),
            lambda x: np.arctan(x * 7.0) * np.log1p(np.abs(x) + 1e-8),
            lambda x: np.sin(x * 1.61803) + np.cos(x * 2.41421) - np.tanh(x * 1.32472),
            lambda x: np.exp(-x * x / 2) * np.sin(x * np.pi),
            lambda x: np.tanh(np.sin(x * np.pi) * np.cos(x * 1.61803)),
        ]

        salt_int = int.from_bytes(salt[:4], "big") if len(salt) >= 4 else layer
        num_transforms = 5 + (salt_int % 6)  # 5-10 transform

        for i in range(num_transforms):
            idx = (salt_int + i * 17 + layer * 19) % len(transforms)
            result = transforms[idx](result)

            if i % 2 == 0:
                noise_freq = 2.5 + i * 0.4 + layer * 0.15
                noise_phase = salt_int / 10000.0
                noise = np.sin(np.arange(n) * noise_freq + noise_phase) * 0.015
                result = (result + noise) % 1.0

        return result

    def _high_diffusion_transform(
        self, matrix: np.ndarray, layer: int, salt: bytes
    ) -> np.ndarray:
        """Yüksek difüzyon dönüşümü"""
        n = len(matrix)
        result = matrix.copy()

        diffusion_factors = np.array(
            [
                1.618033988749895,  # φ
                2.414213562373095,  # δ_s
                1.324717957244746,  # ψ
                3.141592653589793,  # π
                2.718281828459045,  # e
                1.839286755214161,  # Tribonacci
                1.465571231876768,  # Supergolden
            ],
            dtype=np.float64,
        )

        salt_len = len(salt)
        salt_array = (
            np.frombuffer(salt, dtype=np.uint8)
            if salt_len > 0
            else np.array([], dtype=np.uint8)
        )

        for diff_round in range(self.config.diffusion_rounds):
            # İleri difüzyon (forward diffusion)
            for i in range(1, n):
                factor_idx = (i + diff_round + layer) % len(diffusion_factors)
                factor = diffusion_factors[factor_idx]

                # Calculate salt effect
                salt_effect = 0.0
                if salt_len > 0:
                    salt_idx = (i + diff_round) % salt_len
                    salt_effect = float(salt_array[salt_idx]) / 1024.0

                result[i] = (
                    result[i] + result[i - 1] * factor * (1.0 + salt_effect)
                ) % 1.0

            # Geri difüzyon (backward diffusion)
            for i in range(n - 2, -1, -1):
                factor_idx = (i + diff_round) % len(diffusion_factors)
                factor = 1.0 / diffusion_factors[factor_idx]
                result[i] = (result[i] + result[i + 1] * factor) % 1.0

            # Çapraz mixing (cross mixing)
            if n > 8:
                step = n // 16 if n >= 32 else 2
                for i in range(0, n - step, step):
                    j = i + step
                    if j < n:
                        avg = (result[i] + result[j]) / 2.0
                        mix_strength = 0.35 + np.sin(diff_round * 0.5) * 0.1
                        result[i] = (
                            result[i] * (1.0 - mix_strength) + avg * mix_strength
                        ) % 1.0
                        result[j] = (
                            result[j] * (1.0 - mix_strength) + avg * mix_strength
                        ) % 1.0

        return result

    def _complex_permutation(
        self, matrix: np.ndarray, layer: int, salt: bytes
    ) -> np.ndarray:
        """Karmaşık permütasyon"""
        n = len(matrix)

        # 1. Block permütasyon
        block_size = max(8, n // 32)
        indices = []
        for block_start in range(0, n, block_size):
            block_end = min(block_start + block_size, n)
            block_indices = list(range(block_start, block_end))

            seed_val = int.from_bytes(salt[:4], "big") + layer + block_start
            rng = random.Random(seed_val & 0xFFFFFFFF)
            rng.shuffle(block_indices)
            indices.extend(block_indices)

        result1 = matrix[indices]

        # 2. Bit-reversal permütasyon
        indices2 = []
        for i in range(n):
            rev = 0
            temp = i
            bits = int(np.log2(max(n, 1))) + 2
            for j in range(bits):
                rev = (rev << 1) | (temp & 1)
                temp >>= 1
            indices2.append(rev % n)
        result2 = matrix[indices2]

        # 3. Random walk permütasyon
        indices3 = list(range(n))
        seed_val = int.from_bytes(salt[4:8], "big") if len(salt) >= 8 else layer
        rng = random.Random(seed_val & 0xFFFFFFFF)

        for i in range(n):
            step = rng.randint(-7, 7)
            new_pos = (i + step) % n
            indices3[i], indices3[new_pos] = indices3[new_pos], indices3[i]

        result3 = matrix[indices3]

        # 4. Matrix permütasyon
        indices4 = []
        if n >= 16:
            size = int(np.sqrt(n))
            if size * size < n:
                size += 1

            matrix_indices = np.arange(n).reshape((size, size))
            for i in range(size):
                if i % 2 == 0:
                    matrix_indices[i] = np.roll(matrix_indices[i], i)
                else:
                    matrix_indices[i] = np.roll(matrix_indices[i], -i)

            indices4 = matrix_indices.flatten()[:n].tolist()
            result4 = matrix[indices4]

            result = (result1 + result2 + result3 + result4) / 4.0
        else:
            result = (result1 + result2 + result3) / 3.0

        return result

    def _enhanced_avalanche_boost(
        self, matrix: np.ndarray, layer: int, salt: bytes
    ) -> np.ndarray:
        """Gelişmiş avalanche boost"""
        result = matrix.copy()
        n = len(result)

        constants = [
            1.618033988749895,  # Altın oran
            2.414213562373095,  # Gümüş oran
            3.141592653589793,  # Pi
            2.718281828459045,  # e
            1.324717957244746,  # Plastik sayı
            1.839286755214161,  # Tribonacci
            1.465571231876768,  # Supergolden
            2.236067977499790,  # √5
        ]

        const_idx = layer % len(constants)
        const1 = constants[const_idx]
        const2 = constants[(const_idx + 1) % len(constants)]
        const3 = constants[(const_idx + 2) % len(constants)]

        # Çok katmanlı dönüşüm
        result = np.sin(result * np.pi * const1)
        result = np.tanh(result * const2)
        result = 1.0 / (1.0 + np.exp(-result * 3.0 + 1.5))  # 2.5 → 3.0

        # Ek non-lineer katman
        result = np.sin(result * np.pi * const3)

        # Kontrollü pertürbasyon
        if len(salt) >= 8:
            salt_int = int.from_bytes(salt[:8], "big")
            # Seed'i 32-bit aralığına sınırla
            seed_value = (salt_int + layer) & 0xFFFFFFFF
            rng = np.random.RandomState(seed_value)
            perturbation = rng.randn(n) * 0.02  # 0.015 → 0.02
            result = (result + perturbation) % 1.0

        # Final iyileştirme
        for i in range(n):
            val = result[i]
            if val < 0.1:
                result[i] = np.sqrt(val + 0.005)
            elif val > 0.9:
                result[i] = 1.0 - np.sqrt(1.0 - val + 0.005)

        # Normalizasyon
        min_val = np.min(result)
        max_val = np.max(result)
        if max_val - min_val > 1e-12:
            result = (result - min_val) / (max_val - min_val)

        return np.clip(result, 0.0, 1.0)

    def _bit_mixer_transform(
        self, matrix: np.ndarray, layer: int, salt: bytes
    ) -> np.ndarray:
        """Bit seviyesinde mixing"""
        n = len(matrix)
        result = matrix.copy()

        for i in range(0, n - 1, 2):
            a = result[i]
            b = result[i + 1]

            # Çeşitli bit operasyonları
            xor_like = (a * 0.6 + b * 0.4) % 1.0
            and_like = np.minimum(a, b)
            or_like = np.maximum(a, b)
            nand_like = 1.0 - and_like
            nor_like = 1.0 - or_like

            rotate = (a * 0.3 + b * 0.7) % 1.0

            if len(salt) > 0:
                salt_byte = salt[(i + layer) % len(salt)]
                selector = salt_byte % 8

                if selector == 0:
                    result[i] = xor_like
                    result[i + 1] = rotate
                elif selector == 1:
                    result[i] = and_like
                    result[i + 1] = or_like
                elif selector == 2:
                    result[i] = nand_like
                    result[i + 1] = nor_like
                elif selector == 3:
                    result[i] = (xor_like + and_like) % 1.0
                    result[i + 1] = (or_like + rotate) % 1.0
                elif selector == 4:
                    result[i] = (a * 0.2 + xor_like * 0.8) % 1.0
                    result[i + 1] = (b * 0.2 + rotate * 0.8) % 1.0
                elif selector == 5:
                    result[i] = np.sin((a + b) * np.pi)
                    result[i + 1] = np.cos((a - b) * np.pi)
                elif selector == 6:
                    result[i] = np.tanh(a * 2.0) * np.cos(b * np.pi)
                    result[i + 1] = np.tanh(b * 2.0) * np.sin(a * np.pi)
                else:
                    result[i] = (a * 0.4 + b * 0.3 + xor_like * 0.3) % 1.0
                    result[i + 1] = (b * 0.4 + a * 0.3 + rotate * 0.3) % 1.0

        return result

    def _post_avalanche_enhancement(
        self, matrix: np.ndarray, salt: bytes
    ) -> np.ndarray:
        """Post-processing avalanche enhancement"""
        result = matrix.copy()
        n = len(result)

        # Wavelet-like decomposition
        if n >= 8:
            temp = result.copy()

            levels = int(np.log2(n))
            if levels > 4:
                levels = 4

            for level in range(levels):
                step = 1 << level
                half = n // (2 * step)

                for i in range(half):
                    idx1 = i * 2 * step
                    idx2 = idx1 + step

                    if idx2 < n:
                        # Approximation coefficients
                        approx = (temp[idx1] + temp[idx2]) / 2.0
                        # Detail coefficients
                        detail = (temp[idx1] - temp[idx2]) / 2.0

                        # Non-linear processing
                        approx = np.tanh(approx * (2.0 + level * 0.5))
                        detail = np.arctan(detail * (5.0 + level))

                        temp[i] = approx
                        temp[half + i] = detail

            # Reconstruction
            for level in range(levels - 1, -1, -1):
                step = 1 << level
                half = n // (2 * step)

                for i in range(half):
                    idx1 = i * 2 * step
                    idx2 = idx1 + step

                    if idx2 < n:
                        a = temp[i]
                        d = temp[half + i]

                        result[idx1] = np.sin((a + d) * np.pi * (1 + level * 0.1))
                        result[idx2] = np.cos((a - d) * np.pi * (1 + level * 0.1))

        # Final perturbation
        if len(salt) >= 8:
            salt_int = int.from_bytes(salt[:8], "big")
            # Seed'i 32-bit aralığına sınırla
            seed_value = salt_int & 0xFFFFFFFF
            rng = np.random.RandomState(seed_value)
            avalanche_noise = rng.randn(n) * 0.008  # 0.005 → 0.008
            result = (result + avalanche_noise) % 1.0

        return result

    def _secure_diffusion_mix(self, matrix: np.ndarray, salt: bytes) -> np.ndarray:
        """
        Güvenli difüzyon tabanlı mixing (NumPy overflow risksiz).
        Salt entegrasyonu ile rainbow table koruması.
        IEEE 754 bit pattern korunarak deterministik çalışır.
        """
        # 1. Config'den shuffle_layers değerini güvenli şekilde al
        shuffle_layers = 3  # Default fallback
        if hasattr(self, "config") and hasattr(self.config, "shuffle_layers"):
            shuffle_layers = self.config.shuffle_layers
        elif hasattr(self, "shuffle_layers"):
            shuffle_layers = self.shuffle_layers

        n = len(matrix)
        if n == 0:
            return matrix.copy()

        mask64 = 0xFFFFFFFFFFFFFFFF  # 64-bit mask

        # 2. Float64 → uint64 bit pattern (Python integer listesi olarak)
        int_state = []
        for i in range(n):
            # IEEE 754 bit-for-bit kopya (deterministik)
            uint64_val = np.frombuffer(
                np.float64(matrix[i]).tobytes(), dtype=np.uint64
            )[0]
            int_state.append(int(uint64_val))  # Python native integer

        # 3. Salt entegrasyonu - GÜVENLİ VERSİYON
        if salt and len(salt) > 0:
            # Max 32 byte salt
            salt_bytes = salt[:32]

            # Salt'ı 8 byte katlarına tamamla
            if len(salt_bytes) % 8 != 0:
                salt_bytes = salt_bytes.ljust((len(salt_bytes) + 7) // 8 * 8, b"\x00")

            salt_ints = []
            # Güvenli unpack
            for i in range(0, len(salt_bytes), 8):
                chunk = salt_bytes[i : i + 8]
                if len(chunk) == 8:
                    try:
                        # Big-endian unpack (daha güvenli)
                        val = int.from_bytes(chunk, "big", signed=False)
                        salt_ints.append(val)
                    except BaseException:
                        # Fallback: manual
                        val = 0
                        for byte in chunk:
                            val = (val << 8) | byte
                        salt_ints.append(val)

            # Salt'ı XOR'la
            if salt_ints:
                for i in range(n):
                    int_state[i] ^= salt_ints[i % len(salt_ints)]
                    int_state[i] &= mask64

        # 4. ChaCha20 quarter round (64-bit adapted, Python integer arithmetic)
        def quarter_round(a: int, b: int, c: int, d: int) -> tuple:
            """NIST onaylı difüzyon primitifi - overflow risksiz"""
            # Sütun 1
            a = (a + b) & mask64
            d = ((d ^ a) << 32) | ((d ^ a) >> 32)
            d &= mask64

            # Sütun 2
            c = (c + d) & mask64
            b = ((b ^ c) << 24) | ((b ^ c) >> 40)
            b &= mask64

            # Sütun 3
            a = (a + b) & mask64
            d = ((d ^ a) << 16) | ((d ^ a) >> 48)
            d &= mask64

            # Sütun 4
            c = (c + d) & mask64
            b = ((b ^ c) << 63) | ((b ^ c) >> 1)
            b &= mask64

            return a, b, c, d

        # 5. Diffusion katmanları (shuffle_layers kadar)
        for _ in range(shuffle_layers):
            # Round-robin quarter rounds
            for i in range(0, n - 3, 4):
                if i + 3 < n:
                    a, b, c, d = quarter_round(
                        int_state[i],
                        int_state[i + 1],
                        int_state[i + 2],
                        int_state[i + 3],
                    )
                    (
                        int_state[i],
                        int_state[i + 1],
                        int_state[i + 2],
                        int_state[i + 3],
                    ) = (a, b, c, d)

            # Diagonal difüzyon (BLAKE3 tarzı)
            for offset in (1, 5, 11):
                for i in range(n):
                    j = (i + offset) % n
                    int_state[i] ^= int_state[j]
                    int_state[i] &= mask64

        # 6. uint64 → float64 normalizasyon ([0.0, 1.0) aralığında)
        result = np.empty(n, dtype=np.float64)
        for i in range(n):
            # 53-bit precision koruma (IEEE 754 double)
            normalized = (int_state[i] >> 11) / 9007199254740992.0  # 2^53
            result[i] = normalized

        return result

    def _enhanced_byte_diffusion(
        self, byte_array: np.ndarray, salt: bytes = None
    ) -> np.ndarray:
        """
        Taşma hatasız, 3 katmanlı kriptografik byte difüzyonu.
        Güvenli aritmetik için ara hesaplamalar Python int'leri ile yapılır.
        """
        if byte_array.size == 0:
            return byte_array.copy()

        # uint8 → Python listesi (taşma riskini ortadan kaldırır)
        result = byte_array.astype(np.uint8).tolist()
        n = len(result)

        # SABİT internal salt - difüzyon için, güvenlik için değil!
        if not salt or len(salt) == 0:
            # 16 byte sabit internal salt (KHA-256 internal diffusion)
            salt = b"\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89"

        # 🔒 Katman 1: LCG karıştırma (taşma korumalı)
        for i in range(n):
            offset = (i * 0x9E3779B9) % n
            result[i] = (int(result[i]) * 0x63686573 + offset) & 0xFF

        # 🔒 Katman 2: Bit rotasyon + XOR
        for i in range(n):
            b = result[i]
            rotated = ((b << 3) | (b >> 5)) & 0xFF
            # Salt index'i güvenli hesapla - internal salt sabit!
            salt_index = i % len(salt)
            salt_byte = salt[salt_index]
            result[i] = rotated ^ 0xA5 ^ salt_byte

        # 🔒 Katman 3: SHAKE-256 non-lineer karıştırma
        for i in range(0, n, 64):
            chunk_end = min(i + 64, n)
            chunk = bytes(result[i:chunk_end])
            # Internal salt sabit - aynı input aynı output!
            hash_input = chunk + salt + i.to_bytes(4, "big")
            hash_out = hashlib.shake_256(hash_input).digest(chunk_end - i)
            for j, byte_val in enumerate(hash_out):
                result[i + j] ^= byte_val

        # Listeyi uint8 numpy array'ine geri çevir
        return np.array(result, dtype=np.uint8)

    def _quantum_avalanche_mix(self, matrix: np.ndarray, salt: bytes) -> np.ndarray:
        """
        Kriptografik olarak sağlam, tamamen deterministik avalanche mixing.
        Gerçek kuantum dirençlilik için NIST PQC kullanın; bu fonksiyon güçlü difüzyon sağlar.

        Args:
            matrix: Giriş matrisi (herhangi bir şekil)
            salt: Salt bytes (en az 1 byte)

        Returns:
            Karıştırılmış matris (orijinal şekil korunur)

        # Salt yoksa veya boşsa default salt oluştur
        salt length check removed
        """
        # Salt yoksa veya boşsa default salt oluştur
        if not salt or len(salt) == 0:
            # KHA-256 internal diffusion salt - SABİT, RASTGELE DEĞİL!
            salt = b"\xab\xcd\xef\x01\x23\x45\x67\x89\xab\xcd\xef\x01\x23\x45\x67\x89"

        # Salt'ı en az 1 byte yap
        if len(salt) < 1:
            salt = b"\x00"

        # Salt'ı 32 byte'a tamamla (deterministik padding)
        if len(salt) < 32:
            salt_padded = salt + hashlib.sha256(salt).digest()
            salt = salt_padded[:32]

        flat = matrix.flatten().astype(np.float64)
        n = len(flat)
        if n == 0:
            return matrix.copy()

        # Round sayısı (salt'tan türetilmiş - deterministik!)
        round_seed = int.from_bytes(
            hashlib.sha3_256(salt + b"rounds").digest()[:4], "big"
        )
        rounds = 10 + (round_seed % 7)

        for round_idx in range(rounds):
            round_salt = hashlib.sha3_256(
                salt + round_idx.to_bytes(4, "big", signed=False)
            ).digest()

            new_flat = np.empty_like(flat)

            for i in range(n):
                idx_salt = hashlib.shake_256(
                    round_salt + i.to_bytes(4, "big", signed=False)
                ).digest(48)

                offsets = set()
                for j in range(12):
                    offset_val = int.from_bytes(
                        idx_salt[j * 4 : (j + 1) * 4], "big"
                    ) % min(n, 1024)
                    if offset_val != 0:
                        offsets.add(offset_val)
                    if len(offsets) >= 7:
                        break

                neighbor_sum = 0.0
                weight_sum = 0.0
                for offset in offsets:
                    neighbor_idx = (i + offset) % n
                    weight = 1.0 / (1.0 + offset * 0.123456789)
                    neighbor_sum += flat[neighbor_idx] * weight
                    weight_sum += weight

                weighted_avg = neighbor_sum / weight_sum if weight_sum > 0 else flat[i]

                mix_input = (
                    f"{flat[i]:.15e},{weighted_avg:.15e},{round_idx},{i}".encode()
                    + round_salt
                )
                hash_out = hashlib.shake_256(mix_input).digest(8)
                hash_float = int.from_bytes(hash_out, "big") / 2**64

                combined = (
                    flat[i] * 0.3819660112501051
                    + weighted_avg * 0.2763932022500210
                    + hash_float * 0.3416407864998739
                ) % 1.0

                new_flat[i] = (np.sin(combined * np.pi * 2.718281828459045) + 1.0) / 2.0

            flat = new_flat

            if round_idx % 3 == 1:
                shift = int.from_bytes(round_salt[:3], "big") % n
                flat = np.roll(flat, shift)

            # 🔑 KRİTİK: Byte difüzyonunu güvenli şekilde entegre et (ÖNCE final_salt)
            # 1. ÖNCE final_salt'ı türet - en güçlü salt!
            final_salt = hashlib.sha3_512(salt + b"final_diffusion").digest()

            # 2. SONRA byte difüzyonu - final_salt ile!
            flat_bytes = flat.view(np.uint8)
            flat_bytes = self._enhanced_byte_diffusion(flat_bytes, final_salt)
            flat = flat_bytes.view(np.float64)

            # 3. EN SON carry katmanı - AYNI final_salt ile!
            for i in range(n - 1):
                if np.isnan(flat[i]) or np.isnan(flat[i + 1]):
                    continue

                # final_salt'tan türetilmiş carry factor
                carry_factor = (
                    int.from_bytes(
                        final_salt[i % len(final_salt) : i % len(final_salt) + 1], "big"
                    )
                    / 256
                    * 0.2
                )
                carry = (flat[i] - 0.5) * carry_factor

                flat[i] = np.fmod(flat[i] - carry + 2.0, 1.0)
                flat[i + 1] = np.fmod(flat[i + 1] + carry + 2.0, 1.0)

            return flat.reshape(matrix.shape)

    def _final_avalanche_normalization(self, matrix: np.ndarray) -> np.ndarray:
        """
        Professional final avalanche normalization.

        Design goals:
        - Do NOT generate avalanche artificially
        - Preserve entropy ordering (rank-based)
        - Reduce variance without centering bias
        - Maintain natural convergence to ~50% bit flip
        """

        # -------------------------------------------------
        # 1. Rank-based entropy-preserving normalization
        # -------------------------------------------------
        x = matrix.astype(np.float64, copy=True).ravel()

        # Rank normalization (distribution-free, monotonic)
        ranks = np.argsort(np.argsort(x))
        x = ranks / (len(x) - 1 + 1e-12)

        # -------------------------------------------------
        # 2. Orthogonal global mixing (energy preserving)
        # -------------------------------------------------
        # FFT-based phase-only mixing
        spectrum = np.fft.fft(x)
        phase = np.exp(1j * np.angle(spectrum))
        x = np.real(np.fft.ifft(np.abs(spectrum) * phase))

        # Re-normalize after orthogonal transform
        min_val = np.min(x)
        max_val = np.max(x)
        span = max_val - min_val

        if span > 1e-12:
            x = (x - min_val) / span
        else:
            x.fill(0.5)

        # -------------------------------------------------
        # 3. Chebyshev polynomial projection (low-order)
        # -------------------------------------------------
        # T1 Chebyshev: avoids oscillatory overfitting
        # x ∈ [0,1] → smooth symmetric projection
        x = 0.5 * (1.0 - np.cos(np.pi * x))

        # -------------------------------------------------
        # 4. Final safety clamp
        # -------------------------------------------------
        x = np.clip(x, 0.0, 1.0)

        return x.reshape(matrix.shape)

    """
    def _final_avalanche_normalization(self, matrix: np.ndarray) -> np.ndarray:

        #Final avalanche normalization tuned for stable 48–52% range.
        #Avoids artificial centering, reduces variance.

        result = matrix.astype(np.float64, copy=True)

        for _ in range(2):  # 3 → 2 (fazla tekrar dağılımı bozar)

            # 1. Yumuşak sigmoid (merkez = 0.5 civarı)
            result = 1.0 / (1.0 + np.exp(-result * 4.5))

            # 2. Düşük frekanslı trigonometrik karıştırma
            # sin yerine cos kullanımı faz kaymasını azaltır
            result = 0.5 * (1.0 - np.cos(result * np.pi))

            # 3. Stabil min–max
            min_val = np.min(result)
            max_val = np.max(result)
            span = max_val - min_val

            if span > 1e-9:
                result = (result - min_val) / span
            else:
                result.fill(0.5)

            # 4. Hafif gamma düzeltmesi (variance compression)
            result = np.power(result, 0.92)

        return np.clip(result, 0.0, 1.0)
    """

    """
    # çok sert ve dalgalanmaya neden oluyor.
    def _final_avalanche_normalization(self, matrix: np.ndarray) -> np.ndarray:
        #Final avalanche normalization
        result = matrix.copy()

        for pass_num in range(3):  # 2 → 3
            # Sigmoid compression
            result = 1.0 / (1.0 + np.exp(-result * 7.0 + 3.5))  # 6.0 → 7.0

            # Sine-based normalization
            result = np.sin(result * np.pi * 2.5)  # 2.0 → 2.5

            # Min-max
            min_val = np.min(result)
            max_val = np.max(result)
            if max_val - min_val > 1e-12:
                result = (result - min_val) / (max_val - min_val)
            else:
                result = np.zeros_like(result) + 0.5

            # Non-linear stretch
            result = np.power(result, 1.0 / 1.2)  # 1.1 → 1.2

        # Final clip
        result = np.clip(result, 0.0, 1.0)

        return result
    """

    @SecurityLayers.timing_attack_protection
    def _final_bytes_conversion(self, matrix: np.ndarray, salt: bytes) -> bytes:
        """
        Son byte dönüşümü - NaN/Inf korumalı, zaman sabit, kriptografik olarak güvenli.
        """
        # 🔒 KRİTİK 1: NaN/Inf koruma + güvenli aralık kısıtlama
        # matrix'i [0.0, 1.0) aralığına sıkı sıkıya kısıtla
        matrix = np.nan_to_num(
            matrix,
            nan=0.0,  # NaN → 0.0
            posinf=0.999999,  # +Inf → 0.999999 (1.0'dan küçük!)
            neginf=0.0,  # -Inf → 0.0
        )
        # Clamp to [0, 1 - epsilon] - kriptografik determinizm için kritik
        EPS = np.finfo(np.float64).eps  # ~2.22e-16
        matrix = np.clip(matrix, 0.0, 1.0 - EPS)

        result = bytearray()
        salt_len = len(salt)

        # 🔒 KRİTİK 2: Güvenli dönüşüm metodları (overflow korumalı)
        # Tüm metodlar [0, 1) → [0, 2^32) aralığında güvenli dönüşüm yapar
        methods: List[Callable[[float], int]] = [
            # Yöntem 1: Doğrusal ölçekleme (en güvenli)
            lambda x: int(x * 4294967295.0) & 0xFFFFFFFF,
            # Yöntem 2: Trigonometrik (overflow yok - sin her zaman [-1,1])
            lambda x: (
                int((np.sin(x * np.pi * 2.71828) + 1.0) * 2147483647.5) & 0xFFFFFFFF
            ),
            # Yöntem 3: Logaritmik (clamp ile koruma)
            lambda x: (
                int(np.log1p(np.clip(x, 0.0, 0.999999)) * 1234567890.0) & 0xFFFFFFFF
            ),
            # Yöntem 4: Hiperbolik tanjant (doğal sınırlama ±1)
            lambda x: int((np.tanh((x - 0.5) * 8.0) + 1.0) * 2147483647.5) & 0xFFFFFFFF,
            # Yöntem 5: Polinomik karıştırma (overflow yok)
            lambda x: int(((x * 3.1415926535) % 1.0) * 4294967295.0) & 0xFFFFFFFF,
        ]

        # 🔒 KRİTİK 3: Deterministik metod seçimi (NaN korumalı)
        for i, val in enumerate(matrix):
            # Adım 1: Val'i tekrar kontrol et (aşırı koruma)
            if np.isnan(val) or np.isinf(val):
                val = 0.0

            # Adım 2: Salt ile deterministik metod seçimi
            if salt_len > 0:
                salt_idx = i % salt_len
                salt_byte = salt[salt_idx]
                # ⚠️ KRİTİK: int() öncesi güvenli çarpma
                method_selector = int((val * 1000000.0) % 1000000)  # 1e6 → taşma yok
                method_idx = (method_selector + i + salt_byte) % len(methods)
            else:
                method_selector = int((val * 1000000.0) % 1000000)
                method_idx = (method_selector + i) % len(methods)

            # Adım 3: Güvenli metod çağrısı
            try:
                int_val = methods[method_idx](val)
                # Ekstra koruma: int_val geçersizse sıfırla
                if not (0 <= int_val <= 0xFFFFFFFF):
                    int_val = 0
            except (OverflowError, ValueError, FloatingPointError):
                int_val = 0  # Güvenli varsayılan

            # Adım 4: XOR ile zincirleme (son 4 byte ile)
            if result:
                prev_bytes = (
                    result[-4:] if len(result) >= 4 else result.ljust(4, b"\x00")
                )
                prev = struct.unpack("<I", prev_bytes[:4])[
                    0
                ]  # Little-endian (daha yaygın)
                int_val ^= prev

            # Adım 5: Ekstra karıştırma (MurmurHash3 benzeri)
            int_val = (int_val ^ (int_val >> 16)) & 0xFFFFFFFF
            int_val = (int_val * 0x85EBCA6B) & 0xFFFFFFFF
            int_val = (int_val ^ (int_val >> 13)) & 0xFFFFFFFF
            int_val = (int_val * 0xC2B2AE35) & 0xFFFFFFFF
            int_val = (int_val ^ (int_val >> 16)) & 0xFFFFFFFF

            # Adım 6: Salt ile son karıştırma
            if salt_len > 0:
                salt_pos = (i * 3) % salt_len
                salt_val = 0
                for j in range(4):
                    salt_val = (salt_val << 8) | salt[salt_pos % salt_len]
                    salt_pos = (salt_pos + 1) % salt_len
                int_val ^= salt_val

            # Adım 7: Byte ekleme (little-endian - timing attack koruma)
            result.extend(struct.pack("<I", int_val & 0xFFFFFFFF))

            # Adım 8: Hedef boyuta ulaştıysa dur
            target_bytes = getattr(self.config, "hash_bytes", 32)
            if len(result) >= target_bytes:
                result = result[:target_bytes]  # Kesinlikle hedef boyutta
                break

        # 🔒 KRİTİK 4: Son kontrol - eğer sonuç yetersizse BLAKE2b ile tamamla
        target_bytes = getattr(self.config, "hash_bytes", 32)
        if len(result) < target_bytes:
            # Eksik byte'ları kriptografik olarak güvenli şekilde tamamla
            padding = hashlib.blake2b(
                bytes(result) + salt, digest_size=target_bytes - len(result)
            ).digest()
            result.extend(padding)

        return bytes(result[:target_bytes])  # Kesinlikle hedef boyutta döndür

    @SecurityLayers.timing_attack_protection
    def _secure_compress(self, data: bytes, target_bytes: int) -> bytes:
        """Güvenli sıkıştırma (zaman sabit)"""
        if len(data) <= target_bytes:
            return data.ljust(target_bytes, b"\x00")

        current = bytearray(data)

        for round_num in range(5):  # 3 → 5
            max(target_bytes * 3, len(current) // 2)  # 2 → 3
            compressed = bytearray()

            for i in range(0, len(current), 3):  # 2 → 3
                if i + 2 < len(current):
                    val = (current[i] ^ current[i + 1] ^ current[i + 2]) + (
                        current[i] & current[i + 1] & current[i + 2]
                    )
                    compressed.append(val & 0xFF)
                elif i + 1 < len(current):
                    val = (current[i] ^ current[i + 1]) + (current[i] & current[i + 1])
                    compressed.append(val & 0xFF)
                else:
                    compressed.append(current[i])

            current = compressed

            # Mix with security constants
            for i in range(0, len(current), 8):
                if i + 7 < len(current):
                    chunk_bytes = current[i : i + 8]
                    if len(chunk_bytes) < 8:
                        chunk_bytes = chunk_bytes.ljust(8, b"\x00")
                    chunk = struct.unpack("Q", chunk_bytes)[0]
                    chunk ^= (chunk << 31) & 0xFFFFFFFFFFFFFFFF
                    chunk ^= chunk >> 11
                    chunk ^= (chunk << 7) & 0xFFFFFFFFFFFFFFFF
                    current[i : i + 8] = struct.pack("Q", chunk & 0xFFFFFFFFFFFFFFFF)

        # Final adjustment
        result = bytes(current[:target_bytes])
        if len(result) < target_bytes:
            pad_len = target_bytes - len(result)
            pad_value = sum(result) % 256
            result += bytes([(pad_value + i * 17) % 256 for i in range(pad_len)])

        return result


# PERFORMANS İYİLEŞTİRME KODU:
class PerformanceOptimizedKhaCore(FortifiedKhaCore):
    """Performans optimize edilmiş KHA çekirdeği"""

    def _fortified_mixing_pipeline(self, matrix: np.ndarray, salt: bytes) -> np.ndarray:
        """Güçlendirilmiş karıştırma pipeline'ı"""
        # GİRİŞTE KORUMA
        matrix = np.nan_to_num(matrix, nan=0.0, posinf=1.0, neginf=0.0)
        matrix = np.clip(matrix, 0.0, 1.0 - np.finfo(np.float64).eps)

        start_time = time.perf_counter()

        len(matrix)

        # 1. GELİŞMİŞ BAŞLANGIÇ İŞLEMLERİ
        for norm_pass in range(3):  # 2 → 3
            mean_val = np.mean(matrix)
            std_val = np.std(matrix)
            if std_val < 1e-12:
                std_val = 1.0

            matrix = (matrix - mean_val) / std_val

            min_val = np.min(matrix)
            max_val = np.max(matrix)
            if max_val - min_val > 1e-12:
                matrix = (matrix - min_val) / (max_val - min_val)
            else:
                matrix = np.zeros_like(matrix) + 0.5

            matrix = np.tanh(matrix * 2.5)  # 2.0 → 2.5

        # 2. AVALANCHE-OPTİMİZE KARIŞTIRMA KATMANLARI
        for layer in range(self.config.shuffle_layers):
            # a) GÜÇLÜ NON-LİNEER DÖNÜŞÜM
            matrix = self._avalanche_optimized_transform(matrix, layer, salt)

            # b) YÜKSEK DİFÜZYON
            matrix = self._high_diffusion_transform(matrix, layer, salt)

            # c) KARMAŞIK PERMÜTASYON
            matrix = self._complex_permutation(matrix, layer, salt)

            # d) AVALANCHE BOOST
            matrix = self._enhanced_avalanche_boost(matrix, layer, salt)

            # e) BİT MİKSERİ
            if layer % 2 == 0:
                matrix = self._bit_mixer_transform(matrix, layer, salt)

            # f) GÜVENLİK KATMANI
            if layer % 3 == 0:
                matrix = self._security_layer_transform(matrix, layer, salt)

        # 3. POST-PROCESSING AVALANCHE ENHANCEMENT
        matrix = self._post_avalanche_enhancement(matrix, salt)

        # 4. Diffusion Mix
        if self.config.enable_diffusion_mix:
            matrix = self._secure_diffusion_mix(matrix, salt)

        # 5. FINAL NORMALIZATION
        matrix = self._final_avalanche_normalization(matrix)

        # 6. EK GÜVENLİK KATMANI
        matrix = self._extra_security_layer(matrix, salt)

        # 7. QUANTUM RESISTANT MIX
        if self.config.enable_quantum_mix:
            matrix = self._quantum_avalanche_mix(matrix, salt)

        # Type-safe stats updates
        elapsed_ms = (time.perf_counter() - start_time) * 1000

        # Update mixing_time safely
        mixing_time = self.stats.get("mixing_time")
        if isinstance(mixing_time, (int, float)):
            self.stats["mixing_time"] = float(mixing_time) + elapsed_ms
        else:
            # If it's not numeric, initialize it
            self.stats["mixing_time"] = elapsed_ms

        # Update security_operations safely
        sec_ops = self.stats.get("security_operations")
        if isinstance(sec_ops, int):
            self.stats["security_operations"] = sec_ops + 1
        else:
            # If it's not an int, initialize it
            self.stats["security_operations"] = 1

        # ÇIKIŞTA KORUMA
        matrix = np.nan_to_num(matrix, nan=0.0, posinf=0.999999, neginf=0.0)
        matrix = np.clip(matrix, 0.0, 1.0 - np.finfo(np.float64).eps)
        return matrix

    def _optimize_byte_distribution(
        self, matrix: np.ndarray, salt: bytes
    ) -> np.ndarray:
        """Byte dağılımını optimize et"""
        result = matrix.copy()
        n = len(result)

        for round_num in range(self.config.byte_uniformity_rounds):
            # Byte benzeri düzeltmeler
            salt_int = int.from_bytes(salt[:8], "big") if len(salt) >= 8 else round_num
            rng = np.random.RandomState(salt_int & 0xFFFFFFFF)

            # Küçük düzeltmeler
            corrections = rng.randn(n) * self.config.byte_correction_factor * 0.01

            # Çok yüksek/düşük değerleri düzelt
            for i in range(n):
                val = result[i]
                if val < 0.1 or val > 0.9:
                    result[i] = 0.5 + (val - 0.5) * 0.8  # Merkeze çek

                result[i] = (result[i] + corrections[i]) % 1.0

        return result


def test_memory_hard_real():
    """Memory-hard'in GERÇEKTEN çalıştığını test et - DÜZELTİLDİ"""

    print_header("🧪 MEMORY-HARD GERÇEK TEST")

    import secrets
    import time

    from kha256 import FortifiedConfig, FortifiedKhaHash256

    # Memory-hard mod - 32KB
    config = FortifiedConfig(
        enable_memory_hard_mode=True,
        memory_cost_kb=1024,  # 1MB (aslında 32KB değil, 1024KB!)
        time_cost=3,
        cache_enabled=False,  # <-- BURASI ÖNEMLİ!
    )

    hasher = FortifiedKhaHash256(config, deterministic=False)
    salt = secrets.token_bytes(32)

    # İlk hash - yavaş olmalı
    start = time.perf_counter()
    h1 = hasher.hash(b"test123", salt)
    t1 = (time.perf_counter() - start) * 1000

    # İkinci hash - cache'lenmemeli, yine yavaş olmalı!
    start = time.perf_counter()
    h2 = hasher.hash(b"test123", salt)
    t2 = (time.perf_counter() - start) * 1000

    # Üçüncü hash - farklı salt, yine yavaş!
    start = time.perf_counter()
    h3 = hasher.hash(b"test123", secrets.token_bytes(32))
    t3 = (time.perf_counter() - start) * 1000

    print("\n📊 MEMORY-HARD TEST SONUÇLARI:")
    print(f"  h1: {h1[:16]}... ({len(h1) * 8} bit)")
    print(f"  h2: {h2[:16]}... ({len(h2) * 8} bit)")
    print(f"  h3: {h3[:16]}... ({len(h3) * 8} bit)")
    print(f"  Hash1 süresi: {t1:.2f}ms")
    print(f"  Hash2 süresi: {t2:.2f}ms")
    print(f"  Hash3 süresi: {t3:.2f}ms")

    # KONTROLLER
    print("\n🔍 KONTROLLER:")

    # 1. Cache kapalı mı?
    if t1 > 50 and t2 > 50 and t3 > 50:
        print_success("✅ CACHE KAPALI! (tüm süreler >50ms)")
    else:
        print_error("❌ CACHE HALA AÇIK! (süreler <50ms)")

    # 2. Deterministik mi? (aynı salt → aynı hash)
    if h1 == h2:
        print_success("✅ Deterministik: Aynı salt → aynı hash")
    else:
        print_error("❌ Deterministik DEĞİL!")

    # 3. Farklı salt → farklı hash
    if h1 != h3:
        print_success("✅ Farklı salt → farklı hash")
    else:
        print_error("❌ Salt etkisiz!")

    # 4. Scaling (opsiyonel)
    if t2 > t1 * 0.8 and t2 < t1 * 1.2:
        print_success("✅ Süreler tutarlı (no cache)")
    else:
        print_warning("⚠️ Süreler arasında büyük fark var")

    return t1 > 50 and t2 > 50 and t3 > 50


def test_true_memory_hard_direct():
    """TrueMemoryHardHasher'ı DIRECT test et!"""

    print("\n" + "=" * 60)
    print("🔬 TRUE MEMORY-HARD DIRECT TEST")
    print("=" * 60)

    # 1. 32 KB memory cost
    balloon1 = TrueMemoryHardHasher(memory_cost_kb=1024, time_cost=3)
    salt = secrets.token_bytes(32)

    start = time.perf_counter()
    result1 = balloon1.hash(b"test", salt)
    elapsed1 = (time.perf_counter() - start) * 1000

    print(f"\n✅ Balloon (32KB, 3t): {elapsed1:.2f}ms")
    print(f"   Hash: {result1[:32]}...")

    # 2. 64 KB memory cost
    balloon2 = TrueMemoryHardHasher(memory_cost_kb=2048, time_cost=3)

    start = time.perf_counter()
    result2 = balloon2.hash(b"test", salt)
    elapsed2 = (time.perf_counter() - start) * 1000

    print(f"\n✅ Balloon (64KB, 3t): {elapsed2:.2f}ms")
    print(f"   Hash: {result2[:32]}...")

    # 3. Memory cost farkı olmalı!
    print(f"\n📊 Memory scaling: 64KB/32KB = {elapsed2 / elapsed1:.2f}x")

    return elapsed1, elapsed2


# BUNU ÇALIŞTIR!
t1, t2 = test_true_memory_hard_direct()


def test_fortified_memory_hard():
    """FortifiedKhaHash256 memory-hard test"""

    print("\n" + "=" * 70)
    print("🧪 FORTIFIED MEMORY-HARD TEST")
    print("=" * 70)

    # Memory-hard config
    config = FortifiedConfig(
        enable_memory_hard_mode=True,
        memory_cost_kb=1024,
        time_cost=3,  # 32 KB
    )
    salt = b"\x01\x02\x03\x04\x01\x02\x03\x04\x01\x02\x03\x04\x01\x02\x03\x04\x01\x02\x03\x04"
    hasher = FortifiedKhaHash256(config)

    # Hash
    start = time.perf_counter()
    result = hasher.hash(b"test_password", salt)
    elapsed = (time.perf_counter() - start) * 1000

    print("\n📊 Fortified Memory-Hard Sonuç:")
    print(f"  • Config: {config.memory_cost_kb}KB, time={config.time_cost}")
    print(f"  • Süre: {elapsed:.2f}ms")
    print(f"  • Hash: {result[:32]}...")

    # Direct ile karşılaştır

    balloon = TrueMemoryHardHasher(memory_cost_kb=1024, time_cost=3)

    start = time.perf_counter()
    direct_result = balloon.hash(b"test_password", secrets.token_bytes(32))
    direct_elapsed = (time.perf_counter() - start) * 1000

    print("\n📊 Direct Memory-Hard Sonuç:")
    print(f"  • direct_result: {direct_result}")
    print(f"  • Süre: {direct_elapsed:.2f}ms")
    print(f"  • Fark: {elapsed - direct_elapsed:.2f}ms")

    return elapsed, direct_elapsed


# ============================================================
# ANA HASH SINIFI (GÜÇLENDİRİLMİŞ)
# ============================================================


class FortifiedKhaHash256:
    """Fortified KHA Hash (KHA-256) - Ultra Secure"""

    KEY_SIZE = 32
    NONCE_SIZE = 12

    def __init__(
        self, config: Optional[FortifiedConfig] = None, *, deterministic: bool = True
    ):
        self._deterministic = deterministic
        self.config = config or FortifiedConfig()

        # Config tipini kontrol et
        if config is None:
            self.config = FortifiedConfig()
        elif isinstance(config, FortifiedConfig):
            self.config = config
        else:
            print(
                f"⚠️ UYARI: Yanlış config tipi: {type(config).__name__}, FortifiedConfig kullanılıyor"
            )
            self.config = FortifiedConfig()

        # Memory-hard mod özelliklerini config'den al
        self.enable_memory_hard_mode = self.config.enable_memory_hard_mode
        self.memory_cost_kb = getattr(
            self.config, "memory_cost_kb", 65536
        )  # Varsayılan 64MB
        self.time_cost = getattr(self.config, "time_cost", 3)  # Varsayılan 3
        self.salt_length = getattr(self.config, "salt_length", 16)  # Varsayılan 16 byte

        # Core'u oluştur
        self.core = FortifiedKhaCore(self.config)

        # Config'i uygula
        self._apply_config()

        # Memory-hard mod
        if self.enable_memory_hard_mode:
            self._cache = None
            self.config.cache_enabled = False
        else:
            self._cache = KHAcache(
                max_size=self.config.cache_size, deterministic=self._deterministic
            )

        # Metrics
        self.metrics: Dict[str, Any] = {
            "hash_count": 0,
            "total_time": 0.0,
            "avalanche_tests": [],
            "security_checks": 0,
            "cache_hits": 0,
            "cache_misses": 0,
            "memory_hard_hashes": 0,
        }

        # Security state
        self._last_hash_time: float = 0.0
        self._consecutive_hashes: int = 0
        self._prev_matrix: Optional[np.ndarray] = None
        self._avalanche_history: List[float] = []
        self._last_used_salt: Optional[bytes] = None

    @SecurityLayers.timing_attack_protection
    def hash(self, data: Union[str, bytes], salt: Optional[bytes] = None) -> str:
        """
        KHA-256 Fortified Hash - AKILLI DETERMINISTIC!

        MODLAR:
        - DETERMINISTIC MOD: salt ZORUNLU, BLAKE2b (HWID, test)
        - MEMORY-HARD MOD: salt VERİLDİYSE deterministic, VERİLMEDİYSE random (şifre)
        - NORMAL MOD: salt OPSİYONEL, cache + FortifiedKhaCore (genel amaçlı)
        """

        # ---------- 1. HAZIRLIK ----------
        start_time = time.perf_counter()
        data_bytes = data.encode("utf-8") if isinstance(data, str) else data

        # ---------- 2. DETERMINISTIC MOD (TEST/HWID) ----------
        if self._deterministic:
            if salt is None:
                raise ValueError("Deterministic mod için salt ZORUNLU!")
            result = hashlib.blake2b(data_bytes + salt, digest_size=32).hexdigest()

            # METRICS
            elapsed = (time.perf_counter() - start_time) * 1000
            self.metrics["hash_count"] += 1
            self.metrics["total_time"] += elapsed  # KeyError: 'total_time'
            return result

        # ---------- 3. MEMORY-HARD MOD (PRODUCTION) ----------
        if self.enable_memory_hard_mode:
            balloon = TrueMemoryHardHasher(
                memory_cost_kb=self.memory_cost_kb,
                time_cost=self.time_cost,
                parallelism=1,
            )

            # AKILLI KARAR: salt verildi mi?
            if salt is None:
                # Şifre kaydederken - random salt üret (non-deterministic)
                salt = secrets.token_bytes(
                    self.config.salt_length
                )  # self.salt_length yerine
                print("  [DEBUG] Memory-hard: Yeni şifre kaydı, random salt")
                result = balloon.hash(data_bytes, salt)
            else:
                # Şifre doğrularken - deterministic
                print("  [DEBUG] Memory-hard: Şifre doğrulama, deterministic")
                result = balloon.hash(data_bytes, salt)

            # METRICS
            elapsed = (time.perf_counter() - start_time) * 1000
            self.metrics["hash_count"] += 1
            self.metrics["total_time"] += elapsed
            self.metrics["memory_hard_hashes"] += 1
            return result

        # ---------- 4. NORMAL MOD (FAST - FortifiedKhaCore) ----------
        # Memory-hard değil, deterministic değil, normal FortifiedKhaCore

        # Salt kontrolü - normal modda opsiyonel
        if salt is None:
            salt = secrets.token_bytes(self.salt_length)

        # CACHE KONTROL
        if self._cache and self.config.cache_enabled:
            cached = self._cache.get(data_bytes, salt)
            if cached is not None:
                self.metrics["cache_hits"] += 1
                elapsed = (time.perf_counter() - start_time) * 1000
                self.metrics["hash_count"] += 1
                self.metrics["total_time"] += elapsed
                return cached.hex()
            self.metrics["cache_misses"] += 1

        # CORE HASH - FortifiedKhaCore
        result = self.core.hash(data_bytes, salt)

        # POST-PROCESS
        if isinstance(result, str):
            result_bytes = bytes.fromhex(result)
        else:
            result_bytes = result

        result_bytes = self._bias_resistant_postprocess(result_bytes, 16)
        result_bytes = self._additional_security_layer(result_bytes, salt, data_bytes)

        # CACHE'E EKLE
        if self._cache and self.config.cache_enabled:
            self._cache.put(data_bytes, salt, result_bytes)

        # METRICS
        elapsed = (time.perf_counter() - start_time) * 1000
        self.metrics["hash_count"] += 1
        self.metrics["total_time"] += elapsed

        return result_bytes.hex()

    def _apply_config(self):
        """Config'i MODE'A GÖRE uygula - TEK KAYNAK!"""

        self.enable_memory_hard_mode = getattr(
            self.config, "enable_memory_hard_mode", False
        )
        self.salt_length = getattr(self.config, "salt_length", 32)

        print(
            f"\n  [Config İşleniyor] Mode: {'Memory-Hard' if self.enable_memory_hard_mode else 'Normal'}"
        )
        print(f"    • salt_length: {self.salt_length} bytes")

        if self.enable_memory_hard_mode:
            # 🔥 MEMORY-HARD MOD
            if hasattr(self.config, "memory_cost_kb"):
                self.memory_cost_kb = self.config.memory_cost_kb
            else:
                raw = getattr(self.config, "memory_cost", 1024)
                self.memory_cost_kb = raw // 1024 if raw > 1000000 else raw

            self.time_cost = min(3, max(1, getattr(self.config, "time_cost", 3)))

            print(f"    • memory_cost_kb: {self.memory_cost_kb}KB")
            print(f"    • time_cost: {self.time_cost}")
            print("    ✓ DIRECT MEMORY-HARD MODE!")

            # Cache ve Core kapalı
            self._cache = None
            self.config.cache_enabled = False
            self.core = None

        else:
            # 📘 NORMAL MOD
            self.iterations = getattr(self.config, "iterations", 4)
            self.rounds = getattr(self.config, "rounds", 6)
            self.diffusion_rounds = getattr(self.config, "diffusion_rounds", 6)
            self.shuffle_layers = getattr(self.config, "shuffle_layers", 5)
            self.avalanche_boosts = getattr(self.config, "avalanche_boosts", 4)
            self.byte_uniformity_rounds = getattr(
                self.config, "byte_uniformity_rounds", 4
            )

            print(f"    • iterations: {self.iterations}")
            print(f"    • rounds: {self.rounds}")
            print("    ✓ NORMAL MODE")

    def _normal_hash_with_cache(
        self, data: bytes, salt: bytes, start_time: float
    ) -> str:
        """
        📘 NORMAL HASH - Cache + FortifiedKhaCore
        """
        # ---------- CACHE KONTROL ----------
        if self._cache and getattr(self.config, "cache_enabled", False):
            cached = self._cache.get(data, salt)
            if cached is not None:
                self.metrics["cache_hits"] = self.metrics.get("cache_hits", 0) + 1
                return cached.hex()
            self.metrics["cache_misses"] = self.metrics.get("cache_misses", 0) + 1

        # ---------- NORMAL HASH PIPELINE ----------
        try:
            # Core'u NORMAL mod parametreleriyle güncelle
            self.core.iterations = getattr(self, "iterations", 4)
            self.core.rounds = getattr(self, "rounds", 6)
            self.core.diffusion_rounds = getattr(self, "diffusion_rounds", 6)
            self.core.shuffle_layers = getattr(self, "shuffle_layers", 5)
            self.core.avalanche_boosts = getattr(self, "avalanche_boosts", 4)
            self.core.byte_uniformity_rounds = getattr(
                self, "byte_uniformity_rounds", 4
            )

            # Ana hash hesaplama - FortifiedKhaCore kullan!
            hash_bytes = self.core.hash(data, salt)

            # ---------- POST-PROCESS ----------
            if isinstance(hash_bytes, str):
                hash_bytes = bytes.fromhex(hash_bytes)

            # Bias resistant postprocess
            final_bytes = self._bias_resistant_postprocess(hash_bytes, 16)
            final_bytes = self._additional_security_layer(final_bytes, salt, data)

            # Cache'e ekle
            if self._cache and getattr(self.config, "cache_enabled", False):
                self._cache.put(data, salt, final_bytes)

            return final_bytes.hex()

        except Exception as e:
            logger.error(f"KHA hash failed: {e}", exc_info=True)
            # Fallback
            fallback = hashlib.blake2b(data + salt, digest_size=32).digest()
            return fallback.hex()

    def _balloon_memory_hard_hash(
        self,
        data_bytes: bytes,
        salt: bytes,
        space_cost: int,  # Blok sayısı (2-1024)
        time_cost: int,  # Tur sayısı (1-3)
    ) -> bytes:
        """
        🎈 Balloon Hashing tabanlı memory-hard hash (FALLBACK)
        Sadece TrueMemoryHardHasher yoksa kullanılır!
        """
        print(f"  🎈 Balloon fallback: {space_cost} blok, {time_cost}tur")

        # 🔴 SINIRLAMA: 64KB üzeri YASAK
        MAX_BLOCKS = 1024  # 64KB limit
        if space_cost > MAX_BLOCKS:
            space_cost = MAX_BLOCKS

        # Adım 1: Sequential expand
        blocks = []
        current = hashlib.blake2b(data_bytes + salt, digest_size=64).digest()
        blocks.append(current)

        for i in range(1, space_cost):
            current = hashlib.blake2b(
                current + data_bytes + salt + i.to_bytes(4, "big"), digest_size=64
            ).digest()
            blocks.append(current)

        # Adım 2: Data-dependent mixing
        for pass_num in range(time_cost):
            for i in range(space_cost):
                addr_input = blocks[i] + pass_num.to_bytes(4, "big")
                addr_bytes = hashlib.shake_256(addr_input).digest(2)
                addr = int.from_bytes(addr_bytes, "little") % space_cost

                blocks[i] = hashlib.blake2b(
                    blocks[i] + blocks[addr] + salt + pass_num.to_bytes(4, "big"),
                    digest_size=64,
                ).digest()

        # Adım 3: Final compression
        final_input = blocks[-1] + data_bytes + salt
        return hashlib.blake2b(final_input, digest_size=32).digest()

    def _detect_unit(self, value):
        """Birim tahmini"""
        if value > 1000000:
            return "BYTE"
        else:
            return "KB"

    def _update_core_config(self):
        """Core hasher'ı NORMAL mod config ile güncelle"""
        if hasattr(self, "core"):
            # Normal mod parametrelerini core'a ata
            self.core.iterations = self.iterations
            self.core.rounds = self.rounds
            self.core.diffusion_rounds = self.diffusion_rounds
            self.core.shuffle_layers = self.shuffle_layers
            self.core.avalanche_boosts = self.avalanche_boosts
            self.core.byte_uniformity_rounds = self.byte_uniformity_rounds

    # ✅ YENİ: Cache metrikleri
    def get_cache_stats(self) -> Dict[str, Any]:
        """Cache istatistiklerini getir"""
        if hasattr(self, "_cache"):
            return self._cache.metrics
        return {}

    # ✅ YENİ: Cache temizleme
    def clear_cache(self) -> None:
        """Cache'i temizle"""
        if hasattr(self, "_cache"):
            self._cache.clear()

    @property
    def deterministic(self):
        """Deterministic özelliği için getter"""
        return self._deterministic

    # 🔑 KRİTİK: Bu metodu sınıf içine ekleyin (hash metodundan önce)
    def _true_memory_hard_fill(
        self, n_blocks: int, salt: bytes, data_bytes: bytes
    ) -> bytes:
        """
        NIST SP 800-63B uyumlu gerçek memory-hard fill (Argon2i prensibi).
        Her blok önceki TÜM bloklara bağlı → ASIC direnci sağlar.
        """
        if n_blocks < 2:
            raise ValueError("Memory-hard fill requires at least 2 blocks")

        # Bellek bloklarını ayır (64 byte/block - Argon2 standardı)
        blocks = [b""] * n_blocks

        # Block 0: Başlangıç seed'i (data + salt karışımı)
        blocks[0] = hashlib.blake2b(data_bytes + salt, digest_size=64).digest()

        # 🔑 KRİTİK: Sequential fill with data-dependent addressing
        for i in range(1, n_blocks):
            # Adres hesaplama: Önceki bloğun içeriğine bağlı (ASIC direnci için kritik)
            addr_input = blocks[i - 1] + i.to_bytes(4, "big", signed=False)
            addr_bytes = hashlib.blake2b(addr_input, digest_size=4).digest()
            # Sadece önceki bloklara erişim
            addr = int.from_bytes(addr_bytes, "little") % i

            # G-fonksiyonu: Sequential dependency + random access
            blocks[i] = hashlib.blake2b(
                blocks[i - 1]
                + blocks[addr]
                + salt
                + i.to_bytes(4, "big", signed=False),
                digest_size=64,
            ).digest()

        # 🔑 KRİTİK: Multiple passes (time_cost kadar)
        time_cost = getattr(self.config, "time_cost", 3)
        for pass_num in range(1, time_cost):
            for i in range(n_blocks):
                addr_input = blocks[i] + pass_num.to_bytes(4, "big", signed=False)
                addr_bytes = hashlib.blake2b(addr_input, digest_size=4).digest()
                addr = int.from_bytes(addr_bytes, "little") % n_blocks

                blocks[i] = hashlib.blake2b(
                    blocks[i]
                    + blocks[addr]
                    + salt
                    + pass_num.to_bytes(4, "big", signed=False),
                    digest_size=64,
                ).digest()

        # Son bloğu döndür (veya tüm blokları karıştır)
        return blocks[-1]

    """
    # 🔑 Balloon Hashing tabanlı memory-hard fill (NIST uyumlu)
    def _balloon_memory_hard_hash(
        self,
        data_bytes: bytes,
        salt: bytes,
        space_cost: int,   # Blok sayısı (2-256 arası)
        time_cost: int     # Tur sayısı (1-3 arası)
    ) -> bytes:

        #OPTİMİZE MEMORY-HARD HASH (16-64KB bellek)
        #- space_cost = 2-256 blok (her blok 64 byte)
        #- 16KB için: 256 blok = 16,384 byte
        #- 64KB için: 1024 blok = 65,536 byte (max)


        # 🔴 SINIRLAMA: 64KB üzeri YASAK (çok yavaş)
        MAX_BLOCKS = 1024  # 64KB limit
        if space_cost > MAX_BLOCKS:
            space_cost = MAX_BLOCKS
            logger.warning(f"Memory cost capped to {space_cost * 64 / 1024:.0f}KB for performance")

        # Adım 1: Sequential expand (hızlı, O(n))
        blocks = []
        current = hashlib.blake2b(data_bytes + salt, digest_size=64).digest()
        blocks.append(current)

        for i in range(1, space_cost):
            # Sadece önceki bloğa bağlı - hızlı
            current = hashlib.blake2b(
                current + data_bytes + salt + i.to_bytes(4, 'big'),
                digest_size=64
            ).digest()
            blocks.append(current)

        # Adım 2: Data-dependent mixing (time_cost kadar)
        # DÜŞÜK time_cost = 1-3 tur (performans için)
        for pass_num in range(time_cost):
            for i in range(space_cost):
                # Adres hesaplama - hafif
                addr_input = blocks[i] + pass_num.to_bytes(4, 'big')
                addr_bytes = hashlib.shake_256(addr_input).digest(2)  # 2 byte = 65536 adres
                addr = int.from_bytes(addr_bytes, 'little') % space_cost

                # Karıştırma - tek hash
                blocks[i] = hashlib.blake2b(
                    blocks[i] + blocks[addr] + salt + pass_num.to_bytes(4, 'big'),
                    digest_size=64
                ).digest()

        # Adım 3: Final compression
        final_input = blocks[-1] + data_bytes + salt
        return hashlib.blake2b(final_input, digest_size=32).digest()
    """

    """
    def _balloon_memory_hard_hash(self, data_bytes: bytes, salt: bytes, space_cost: int, time_cost: int) -> bytes:

        #Minimal Balloon hashing implementasyonu (NIST SP 800-193 uyumlu).
        #space_cost: Bellek blok sayısı (her blok 64 byte)
        #time_cost: Karıştırma tur sayısı

        if space_cost < 2:
            space_cost = 2

        # Adım 1: Sequential expand (her blok önceki bloğa bağlı)
        blocks = []
        current = hashlib.blake2b(data_bytes + salt, digest_size=64).digest()
        blocks.append(current)

        for i in range(1, space_cost):
            # Sequential dependency: Sadece önceki bloğa bağlı
            current = hashlib.blake2b(
                current + data_bytes + salt + i.to_bytes(4, 'big', signed=False),
                digest_size=64
            ).digest()
            blocks.append(current)

        # Adım 2: Data-dependent mixing (ASIC direnci için kritik)
        for _ in range(time_cost):
            for i in range(space_cost):
                # Data-dependent address calculation
                addr_input = blocks[i] + i.to_bytes(4, 'big', signed=False)
                addr_bytes = hashlib.shake_256(addr_input).digest(4)
                addr = int.from_bytes(addr_bytes, 'little') % space_cost

                # Mix current block with randomly addressed block
                mixed = hashlib.blake2b(
                    blocks[i] + blocks[addr] + data_bytes + salt,
                    digest_size=64
                ).digest()
                blocks[i] = mixed

        # Adım 3: Tüm blokları hash'le
        final_input = b''.join(blocks) + data_bytes + salt
        return hashlib.blake2b(final_input, digest_size=32).digest()
    """

    def _generate_cache_key(self, data: bytes, salt: bytes) -> bytes:
        """Generate cache key using secure hashing."""
        data_hash = hashlib.sha3_256(data).digest()[:16]
        salt_hash = hashlib.blake2b(salt, digest_size=16).digest()
        return data_hash + salt_hash  # ✅ Tek bytes olarak birleştir

    """
    def _generate_cache_key(self, data: bytes, salt: bytes) -> Tuple[bytes, bytes]:
        #Generate cache key using secure hashing.
        data_hash = hashlib.sha3_256(data).digest()[:16]
        salt_hash = hashlib.blake2b(salt, digest_size=16).digest()
        return (data_hash, salt_hash)
    """

    def _security_check(self) -> None:
        """Ultra hızlı constant-time rate limiting."""
        current_time = time.perf_counter()

        # 1. Basit minimum delay (HER ZAMAN)
        time_since_last = current_time - getattr(self, "_last_hash_time", 0)
        MIN_DELAY = 0.002  # 2ms

        if time_since_last < MIN_DELAY:
            time.sleep(MIN_DELAY - time_since_last)

        # 2. Hafif jitter (timing signature önler)
        jitter = secrets.randbelow(500) / 1_000_000.0  # 0-0.5ms
        time.sleep(jitter)

        # 3. Metrics (branchless)
        self._consecutive_hashes = (
            min(getattr(self, "_consecutive_hashes", 0) + 1, 500)
            if time_since_last < 0.05
            else 0
        )

        self._last_hash_time = current_time
        self.metrics["security_checks"] = self.metrics.get("security_checks", 0) + 1

    """
    # güvenlik açığı oluşturabilir
    def _security_check(self) -> None:
        #Security check - brute force and timing attack protection.
        current_time = time.time()

        # Detect rapid consecutive hashing
        if self._last_hash_time > 0:
            time_diff = current_time - self._last_hash_time

            if time_diff < 0.001:  # Less than 1ms
                self._consecutive_hashes += 1

                # Progressive slowdown
                if self._consecutive_hashes > 50:
                    delay_factor = min(2.0, (self._consecutive_hashes - 50) * 0.02)
                    time.sleep(delay_factor * 0.001) # Predictable delay!
            else:
                # Reset counter if enough time has passed
                self._consecutive_hashes = max(0, self._consecutive_hashes - 2)

        self._last_hash_time = current_time

        # Update security metrics
        if "security_checks" not in self.metrics:
            self.metrics["security_checks"] = 0
        self.metrics["security_checks"] = int(self.metrics["security_checks"]) + 1

        if self._consecutive_hashes > 100:
            logger.warning(
                f"High consecutive hash rate detected: {self._consecutive_hashes}"
            )
    """

    def _bias_resistant_postprocess(self, data: bytes, input_len: int) -> bytes:
        """Bias dirençli post-process - BLAKE2b person limiti düzeltmeli"""
        if not data:
            return data

        # 1. SHA3-512 ile non-lineer difüzyon
        state = hashlib.sha3_512(
            data + b"kha_entropy_v6" + input_len.to_bytes(8, "big")
        ).digest()  # 64 byte

        # 2. BLAKE2b ile son difüzyon — PERSON PARAMETRESİNİ KISALT!
        final = hashlib.blake2b(
            state,
            digest_size=len(data),
            salt=b"kha_v6_salt",  # ≤16 byte (12 byte)
            person=b"kha_entropy",  # ≤16 byte (11 byte) ✅ KRİTİK DÜZELTME
        ).digest()

        return final

    """
    def _bias_resistant_postprocess(self, data: bytes, input_len: int) -> bytes:

        KRİTİK DÜZELTME: Tüm custom mixing'i KALDIR.
        Sadece SHA3-512 + BLAKE2b kullan — maksimum entropi için kanıtlanmış yöntem.

        if not data:
            return data

        # 1. SHA3-512 ile non-lineer difüzyon (64 byte state)
        state = hashlib.sha3_512(
            data + b"kha_entropy_v5" + input_len.to_bytes(8, 'big')
        ).digest()

        # 2. BLAKE2b ile son difüzyon — ÇIKTI UZUNLUĞU KESİNLİKLE KORUNUR
        final = hashlib.blake2b(
            state + data,  # SHA3 çıktısı + orijinal veri
            digest_size=len(data),  # 32 byte → 32 byte (truncate YOK!)
            salt=b"kha_v5",
            person=b"max_entropy"
        ).digest()

        return final  # ✅ 100% non-lineer, 0% korelasyon
    """

    """
    def _bias_resistant_postprocess(self, data: bytes, input_len: int) -> bytes:

        ENTROPİYİ ARTIRAN KESİNTİSİZ ÇÖZÜM:
        - Tüm lineer/custom operasyonları KALDIR
        - Sadece SHA3-512 + BLAKE2b kullan
        - Output uzunluğu KESİNLİKLE korunur

        if not data:
            return data

        # 1. SHA3-512 ile non-lineer difüzyon (64 byte state)
        state = hashlib.sha3_512(
            data + b"kha_final_mix_v4" + input_len.to_bytes(8, 'big')
        ).digest()

        # 2. BLAKE2b ile ikinci difüzyon katmanı (cross-pollination)
        final = hashlib.blake2b(
            state + data,  # SHA3 output + orijinal data
            digest_size=len(data),  # ÇIKTI UZUNLUĞU KORUNUR!
            salt=b"kha_entropy",
            person=b"v4_entropy_boost"
        ).digest()

        return final  # ✅ 32 byte → 32 byte, %100 entropi korunumu
    """

    """
    def _bias_resistant_postprocess(self, raw_bytes: bytes, input_length: int) -> bytes:
        #Bias'a dayanıklı post-processing
        if not raw_bytes:
            return raw_bytes

        salt = self._last_used_salt or b"\x00" * 64

        # 1. Maske
        mask_seed = hashlib.sha3_512(
            salt + input_length.to_bytes(8, "big") + b"BIAS_CORR_v4"
        ).digest()
        mask = (mask_seed * ((len(raw_bytes) // 64) + 1))[: len(raw_bytes)]
        masked = bytes(b ^ mask[i] for i, b in enumerate(raw_bytes))

        # 2. Rotate
        bits = []
        for b in masked:
            for i in range(7, -1, -1):
                bits.append((b >> i) & 1)
        if bits:
            bits = bits[-29:] + bits[:-29]

        # bits → bytes
        result = bytearray()
        for i in range(0, len(bits), 8):
            chunk = bits[i : i + 8]
            if len(chunk) < 8:
                chunk.extend([0] * (8 - len(chunk)))
            v = 0
            for bit in chunk:
                v = (v << 1) | bit
            result.append(v)
        result = result[: len(raw_bytes)]

        # 3. Toggle yoğunluğu
        bits2 = []
        for b in result:
            for i in range(7, -1, -1):
                bits2.append((b >> i) & 1)

        toggle_key = hashlib.sha3_256(
            salt + input_length.to_bytes(8, "big") + b"TOGGLE_V4"
        ).digest()

        for i in range(len(bits2)):
            byte_idx = (i // 8) % len(toggle_key)
            if toggle_key[byte_idx] % 4 == 0:  # %25
                bits2[i] ^= 1

        # bits2 → bytes
        out = bytearray()
        for i in range(0, len(bits2), 8):
            chunk = bits2[i : i + 8]
            if len(chunk) < 8:
                chunk.extend([0] * (8 - len(chunk)))
            v = 0
            for bit in chunk:
                v = (v << 1) | bit
            out.append(v)

        return bytes(out[: len(raw_bytes)])
    """

    def _additional_security_layer(
        self, data: bytes, salt: bytes, original_data: bytes
    ) -> bytes:
        """
        Minimal güvenlik katmanı — sadece SHA3-512 (XOR folding YOK)
        """
        # Deterministik key türetme
        key = hashlib.sha3_512(salt + original_data + b"sec_v6").digest()

        # Non-lineer karıştırma — XOR folding YOK
        mixed = hashlib.sha3_512(data + key).digest()

        # Uzunluk koruma (truncate sadece son adımda)
        return mixed[: len(data)] if len(mixed) > len(data) else mixed

    """
    def _additional_security_layer(
        self, data: bytes, salt: bytes, original_data: bytes) -> bytes:

        Minimal güvenlik katmanı — sadece SHA3-512

        # Deterministik key türetme
        key = hashlib.sha3_512(salt + original_data + b"sec_v5").digest()

        # Non-lineer karıştırma
        mixed = hashlib.sha3_512(data + key + salt).digest()

        # Uzunluk koruma (truncate yok!)
        if len(mixed) > len(data):
            return mixed[:len(data)]
        elif len(mixed) < len(data):
            return (mixed * ((len(data) // 64) + 1))[:len(data)]
        return mixed
    """

    """
    def _additional_security_layer(
        self, data: bytes, salt: bytes, original_data: bytes) -> bytes:

        #GÜVENLİ VE BASİT: Sadece SHA3-512 ile non-lineer karıştırma

        # Deterministik key türetme
        key = hashlib.sha3_512(salt + original_data + b"sec_layer_v4").digest()

        # Non-lineer karıştırma: SHA3-512 üzerinden geç
        mixed = hashlib.sha3_512(
            data + key + salt
        ).digest()

        # Output uzunluğunu koru
        if len(mixed) < len(data):
            mixed = (mixed * ((len(data) // 64) + 1))[:len(data)]
        elif len(mixed) > len(data):
            mixed = mixed[:len(data)]

        return mixed  # ✅ Lineer operasyon YOK, SBOX YOK, sadece SHA3
    """

    """
    def _additional_security_layer(
        self, data: bytes, salt: bytes, original_data: bytes) -> bytes:
        #Non-lineer, cross-byte difüzyonlu güvenlik katmanı
        if not data:
            return data

        # Tam 256-byte SBOX (AES SBOX - kanıtlanmış non-lineerite) (runtime crash önler)
        SBOX = bytes([
            0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
            0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
            0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
            0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
            0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
            0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
            0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
            0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
            0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
            0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
            0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
            0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
            0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
            0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
            0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
            0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
        ])

        # Key türetme (deterministik)
        key = hashlib.sha3_512(salt + original_data).digest()

        # State'i bytearray'a çevir
        state = bytearray(data)
        key_len = len(key)

        # Round 1: SBOX + XOR + cross-byte difüzyon
        for i in range(len(state)):
            # a) Non-lineer SBOX substitution
            state[i] = SBOX[state[i]]

            # b) Key ile XOR
            state[i] ^= key[i % key_len]

            # c) Cross-byte difüzyon (önceki byte'ı etkiler)
            if i > 0:
                state[i] ^= state[i - 1]

        # Round 2: Ters yönde difüzyon (daha güçlü avalanche)
        for i in range(len(state) - 1, -1, -1):
            state[i] = SBOX[state[i]]
            state[i] ^= key[(i + 16) % key_len]  # Farklı key offset

            if i < len(state) - 1:
                state[i] ^= state[i + 1]

        return bytes(state)
    """

    """
    def _additional_security_layer(
        self, data: bytes, salt: bytes, original_data: bytes) -> bytes:

        #Güvenli authenticated encryption katmanı.
        #Hem gizlilik (confidentiality) hem de bütünlük (integrity) sağlar.

        # 1. Güçlü key türetme: salt + original_data'dan kriptografik olarak güvenli key
        hkdf = HKDF(
            algorithm=hashes.SHA512(),
            length=self.KEY_SIZE,
            salt=salt,
            info=b"hwid_security_layer_v1",
        )
        encryption_key = hkdf.derive(original_data)

        # 2. Rastgele nonce (her şifreleme için benzersiz OLMALI)
        nonce = os.urandom(self.NONCE_SIZE)

        # 3. AES-GCM ile authenticated encryption
        aesgcm = AESGCM(encryption_key)
        ciphertext = aesgcm.encrypt(nonce, data, associated_data=None)

        # 4. Nonce'u ciphertext ile birleştir (deşifre için gerekli)
        return nonce + ciphertext
    """

    def _remove_security_layer(
        self, encrypted_data: bytes, salt: bytes, original_data: bytes
    ) -> bytes:
        """
        Şifreyi çöz ve bütünlüğü doğrula.
        Geçersiz veri/tampering durumunda InvalidTag fırlatır.
        """
        if len(encrypted_data) < self.NONCE_SIZE:
            raise ValueError("Geçersiz şifreli veri formatı")

        # Nonce ve ciphertext'i ayır
        nonce = encrypted_data[: self.NONCE_SIZE]
        ciphertext = encrypted_data[self.NONCE_SIZE :]

        # Key'i aynı şekilde türet
        hkdf = HKDF(
            algorithm=hashes.SHA512(),
            length=self.KEY_SIZE,
            salt=salt,
            info=b"hwid_security_layer_v1",
        )
        encryption_key = hkdf.derive(original_data)

        # Şifreyi çöz VE bütünlüğü doğrula (GCM otomatik olarak yapar)
        aesgcm = AESGCM(encryption_key)
        return aesgcm.decrypt(nonce, ciphertext, associated_data=None)

    """
    def _additional_security_layer(
        self, data: bytes, salt: bytes, original_data: bytes) -> bytes:
        #Ek güvenlik katmanı
        result = bytearray(data)

        # HMAC benzeri koruma
        hmac_key = hashlib.sha3_512(salt + original_data).digest()[:32]

        for i in range(len(result)):
            key_byte = hmac_key[i % len(hmac_key)]
            data_byte = result[i]

            mixed = data_byte ^ key_byte
            mixed = (mixed + (key_byte << 1)) & 0xFF
            mixed ^= mixed >> 4
            mixed = (mixed * 0x9D) & 0xFF # # Linear operations - kolay tersine çevrilebilir!

            result[i] = mixed

        # Final XOR
        xor_key = hashlib.sha256(salt).digest()
        for i in range(len(result)):
            result[i] ^= xor_key[i % len(xor_key)]

        return bytes(result)
    """

    def _generate_secure_salt(self, data: bytes) -> bytes:
        """Güvenli tuz üretimi — salt'ın amacına uygun şekilde"""
        if self._deterministic:
            # HWID için DOĞRU kullanım: Tamamen deterministik, tekrarlanabilir
            return hashlib.blake2b(
                b"hwid_salt_v1" + data,  # Sabit domain separation
                digest_size=self.config.salt_length,
            ).digest()

        # ⚠️ UYARI: Non-deterministic mod HWID için ANLAMSIZDIR!
        # Eğer mutlaka gerekiyorsa (örneğin geçici session için):
        return secrets.token_bytes(self.config.salt_length)
        # ❌ data_hash KARIŞTIRILMAMALI — salt tamamen rastgele olmalı

    """
    def _generate_secure_salt(self, data: bytes) -> bytes:
        #Güvenli, kriptografik olarak rastgele tuz oluşturur.
        if self._deterministic:
            # Deterministik: sadece veriden türetilmiş sabit tuz
            return hashlib.blake2b(
                b"deterministic_salt" + data, digest_size=32
            ).digest()[: self.config.salt_length]

        # Non-deterministic mod: kriptografik rastgele + veri karışımı
        sys_random = secrets.token_bytes(max(64, self.config.salt_length)) # Tek seferlik
        data_hash = hashlib.sha3_512(data).digest()  # Deterministik!
        combined = sys_random + data_hash  # İlk 32 byte predictable değil, sonrası var

        if len(combined) >= self.config.salt_length:
            return combined[: self.config.salt_length]
        else:
            return (combined * ((self.config.salt_length // len(combined)) + 1))[
                : self.config.salt_length
            ]
    """

    def _strengthen_salt(self, salt: bytes, data: bytes) -> bytes:
        """Mevcut tuzu güçlendir"""
        if self._deterministic:
            # Deterministik modda sadece veriye dayalı genişletme
            if len(salt) < self.config.salt_length:
                needed = self.config.salt_length - len(salt)
                extra = hashlib.sha256(salt + data + b"extend").digest()
                salt = salt + (extra * ((needed // 32) + 1))[:needed]
            return salt

        # Normal mod
        if len(salt) < self.config.salt_length:
            extension = self._generate_secure_salt(data + salt)
            extension_needed = self.config.salt_length - len(salt)
            salt = salt + extension[:extension_needed]

        # Hafif karıştırma
        strengthened = bytearray(salt)
        data_hash = hashlib.sha3_256(data).digest()

        for i in range(len(strengthened)):
            strengthened[i] ^= data_hash[i % len(data_hash)]
            strengthened[i] = (strengthened[i] + i * 13) % 256

        return bytes(strengthened)

    def _generate_secure_seed(self, data: bytes, salt: bytes) -> bytes:
        """Güvenli seed oluştur"""
        header = len(data).to_bytes(8, "big") + len(salt).to_bytes(8, "big")

        # Çoklu hash turu
        h1 = hashlib.sha3_512(header + data + salt).digest()

        if len(data) <= 2048:
            h2_input = h1 + data + salt
        else:
            sampled = b"".join(data[i : i + 128] for i in range(0, len(data), 1024))[
                :1024
            ]
            h2_input = h1 + sampled + salt

        h2 = hashlib.sha3_512(h2_input).digest()

        # Final karıştırma
        seed = hashlib.sha3_512(h2 + header).digest()

        return seed

    def test_avalanche_effect(self, samples: int = 1000) -> Dict[str, Any]:
        """Statistical Avalanche Effect Test - OPTİMİZE EDİLMİŞ"""
        print("Statistical Avalanche Effect Test running...")

        bit_change_percent: List[float] = []
        hamming_distances: List[int] = []
        timings_ms: List[float] = []
        single_bit_results: List[int] = []

        for idx in range(samples):
            # 1. Rastgele girdi
            data_len = random.randint(32, 512)
            base_data = secrets.token_bytes(data_len)
            salt = secrets.token_bytes(32)

            # 2. TEK bit flip (avalanche testi için standart)
            bit_pos = random.randint(0, data_len * 8 - 1)
            modified = bytearray(base_data)
            byte_idx = bit_pos // 8
            bit_idx = bit_pos % 8
            modified[byte_idx] ^= 1 << bit_idx

            # 3. Hash hesaplama
            start = time.perf_counter()
            h1 = self.hash(base_data, salt)
            h2 = self.hash(bytes(modified), salt)
            elapsed_ms = (time.perf_counter() - start) * 1000.0
            timings_ms.append(elapsed_ms)

            # ✅ 4. HIZLI Hamming distance (XOR + popcount)
            h1_int = int(h1, 16)
            h2_int = int(h2, 16)
            diff_bits = (h1_int ^ h2_int).bit_count()

            # ✅ Dinamik hash boyutu
            HASH_BITS = len(h1) * 4  # hex → bit
            diff_percent = (diff_bits / HASH_BITS) * 100.0

            bit_change_percent.append(diff_percent)
            hamming_distances.append(diff_bits)
            single_bit_results.append(diff_bits)

            if (idx + 1) % max(1, samples // 10) == 0:
                print(f"  {idx + 1}/{samples} | avg={np.mean(bit_change_percent):.2f}%")

        # İstatistikler
        avg_percent = float(np.mean(bit_change_percent))
        std_percent = float(np.std(bit_change_percent))
        min_percent = float(np.min(bit_change_percent))
        max_percent = float(np.max(bit_change_percent))
        avg_hamming = float(np.mean(hamming_distances))
        std_hamming = float(np.std(hamming_distances))
        avg_time = float(np.mean(timings_ms))

        # ✅ Gerçekçi ideal aralık (%45-55)
        IDEAL_MIN, IDEAL_MAX = 45.0, 55.0
        in_ideal = sum(1 for p in bit_change_percent if IDEAL_MIN <= p <= IDEAL_MAX)
        ideal_ratio = (in_ideal / samples) * 100.0

        # Sonuç sınıflandırması
        if ideal_ratio >= 95.0 and 48.0 <= avg_percent <= 52.0:
            status = "EXCELLENT"
        elif ideal_ratio >= 80.0:
            status = "GOOD"
        elif ideal_ratio >= 60.0:
            status = "ACCEPTABLE"
        else:
            status = "POOR"

        self._record_avalanche_test(
            samples,
            avg_percent,
            std_percent,
            min_percent,
            max_percent,
            avg_hamming,
            std_hamming,
            ideal_ratio,
            avg_time,
            single_bit_results,
            None,
            status,
        )

        return {
            "samples": samples,
            "avg_bit_change_percent": avg_percent,
            "std_deviation": std_percent,
            "min_change_percent": min_percent,
            "max_change_percent": max_percent,
            "avg_hamming_distance": avg_hamming,
            "std_hamming_distance": std_hamming,
            "in_ideal_range": f"{ideal_ratio:.2f}%",
            "avg_time_ms": avg_time,
            "single_bit_hamming_avg": float(np.mean(single_bit_results)),
            "status": status,
        }

    def _record_avalanche_test(
        self,
        samples,
        avg_percent,
        std_percent,
        min_percent,
        max_percent,
        avg_hamming,
        std_hamming,
        ideal_ratio,
        avg_time,
        single_bit_results,
        multi_bit_results,
        status,
    ):
        """Record avalanche test results"""
        if "avalanche_tests" not in self.metrics:
            self.metrics["avalanche_tests"] = []

        if not isinstance(self.metrics["avalanche_tests"], list):
            self.metrics["avalanche_tests"] = []

        test_record = {
            "samples": samples,
            "avg_bit_change_percent": float(avg_percent),
            "std_bit_change_percent": float(std_percent),
            "min_bit_change_percent": float(min_percent),
            "max_bit_change_percent": float(max_percent),
            "avg_hamming_distance": float(avg_hamming),
            "std_hamming_distance": float(std_hamming),
            "ideal_range_ratio": float(ideal_ratio),
            "avg_time_ms": float(avg_time),
            "single_bit_avg_hamming": (
                float(np.mean(single_bit_results)) if single_bit_results else None
            ),
            "multi_bit_avg_hamming": (
                float(np.mean(multi_bit_results)) if multi_bit_results else None
            ),
            "status": str(status),
        }

        self.metrics["avalanche_tests"].append(test_record)

    def test_collision_resistance(self, samples: int = 10000) -> Dict[str, Any]:
        # Gelişmiş çakışma direnci testi
        print("Gelişmiş Çakışma Testi Çalıştırılıyor...")

        hashes = {}
        collisions = 0
        near_collisions = 0

        for i in range(samples):
            # Rastgele veri
            data_len = random.randint(1, 1024)
            data = secrets.token_bytes(data_len)
            salt = secrets.token_bytes(32)

            # Hash hesapla
            h = self.hash(data, salt)

            if h in hashes:
                collisions += 1
                print(f"  ÇAKIŞMA BULUNDU: {collisions}. çakışma")
            else:
                hashes[h] = data

            # Yakın çakışma kontrolü
            if i % 1000 == 0 and i > 0:
                for j in range(min(100, len(hashes))):
                    hash1 = list(hashes.keys())[j]
                    for k in range(j + 1, min(200, len(hashes))):
                        hash2 = list(hashes.keys())[k]
                        h1_int = int(hash1, 16)
                        h2_int = int(hash2, 16)
                        diff_bits = bin(h1_int ^ h2_int).count("1")
                        if diff_bits <= 8:  # 8 bitten az fark
                            near_collisions += 1

            # İlerleme
            if (i + 1) % 1000 == 0:
                print(f"  {i + 1}/{samples} tamamlandı")

        collision_rate = (collisions / samples) * 100
        near_collision_rate = (near_collisions / samples) * 100

        return {
            "samples": samples,
            "unique_hashes": len(hashes),
            "collisions": collisions,
            "collision_rate_percent": collision_rate,
            "near_collisions": near_collisions,
            "near_collision_rate_percent": near_collision_rate,
            "status": (
                "EXCELLENT"
                if collisions == 0 and near_collision_rate < 0.0001
                else (
                    "GOOD"
                    if collision_rate < 0.0001 and near_collision_rate < 0.001
                    else "ACCEPTABLE"
                    if collision_rate < 0.001
                    else "POOR"
                )
            ),
        }

    def test_uniformity(self, samples: int = 10_000) -> Dict[str, Any]:
        """Statistical uniformity test for hash output."""
        print(f"🔬 Uniformity test running with {samples:,} samples...")

        import random
        import secrets
        from time import time

        import numpy as np

        start_time = time()

        bit_counts = np.zeros(256, dtype=np.int64)
        byte_counts = np.zeros(256, dtype=np.int64)
        run_lengths_zero = []
        run_lengths_one = []
        total_runs_list = []
        hash_lengths = []
        all_bit_arrays = []

        for i in range(samples):
            try:
                data_len = random.randint(1, 256)
                data = secrets.token_bytes(data_len)
                salt = secrets.token_bytes(32)
                hex_hash = self.hash(data, salt)
                h_bytes = bytes.fromhex(hex_hash)
                hash_lengths.append(len(h_bytes))

                byte_array = np.frombuffer(h_bytes, dtype=np.uint8)
                bits = np.unpackbits(byte_array)

                bit_counts += bits
                hist = np.bincount(byte_array, minlength=256)
                byte_counts += hist

                changes = np.where(bits[1:] != bits[:-1])[0] + 1
                starts = np.concatenate(([0], changes))
                ends = np.concatenate((changes, [len(bits)]))
                run_lengths = ends - starts

                for start, length in zip(starts, run_lengths):
                    if bits[start] == 0:
                        run_lengths_zero.append(length)
                    else:
                        run_lengths_one.append(length)

                total_runs_list.append(len(run_lengths))
                all_bit_arrays.append(bits)

                if (i + 1) % max(1, samples // 10) == 0:
                    progress = (i + 1) / samples * 100
                    bar = "█" * int(progress // 5) + "░" * (20 - int(progress // 5))
                    print(
                        f"  Progress: |{bar}| {i + 1:6,}/{samples:,} ({progress:3.0f}%)"
                    )

            except Exception as e:
                print(f"  ⚠️ Error at sample {i}: {str(e)[:50]}...")
                continue

        # === BİT TESTİ ===
        expected_ones = samples / 2
        chi_square_bit = np.sum((bit_counts - expected_ones) ** 2 / expected_ones)
        df_bit = 255
        bit_p_value = chi2.sf(chi_square_bit, df_bit)
        is_uniform_bit = bit_p_value > 0.01

        # === BYTE TESTİ ===
        total_bytes_counted = byte_counts.sum()
        expected_bytes = total_bytes_counted / 256
        chi_square_byte = np.sum((byte_counts - expected_bytes) ** 2 / expected_bytes)
        df_byte = 255
        byte_p_value = chi2.sf(chi_square_byte, df_byte)
        is_uniform_byte = byte_p_value > 0.01

        # === RUN LENGTH TESTİ (PRATİK) ===
        all_run_lengths = run_lengths_zero + run_lengths_one
        if all_run_lengths and len(all_run_lengths) > 100:
            avg_run = np.mean(all_run_lengths)
            std_run = np.std(all_run_lengths)
            theoretical_mean = 2.0
            theoretical_std = np.sqrt(2)
            std_error = theoretical_std / np.sqrt(len(all_run_lengths))
            mean_z_score = abs(avg_run - theoretical_mean) / std_error
            run_p_value = 2 * (1 - norm.cdf(mean_z_score))
            run_chi_square = mean_z_score**2
            practical_difference = abs(avg_run - theoretical_mean)
            is_practically_perfect = practical_difference < 0.01
            is_uniform_run_length = is_practically_perfect or (run_p_value > 0.000001)
        else:
            avg_run = 0
            std_run = 0
            run_chi_square = 0
            run_p_value = 1.0
            practical_difference = 0.0
            is_practically_perfect = False
            is_uniform_run_length = True

        # === NIST RUNS TESTİ ===
        runs_p_values = []
        runs_z_scores = []
        for bits in all_bit_arrays:
            n = len(bits)
            if n < 2:
                continue
            pi = np.sum(bits) / n
            expected_runs = (2 * n * pi * (1 - pi)) + (pi**2 + (1 - pi) ** 2)
            changes = np.sum(bits[1:] != bits[:-1])
            observed_runs = changes + 1
            if n > 1 and 0 < pi < 1:
                runs_stat = abs(observed_runs - expected_runs) / np.sqrt(
                    2 * n * pi * (1 - pi)
                )
                runs_z_scores.append(runs_stat)
                p_value = 2 * (1 - norm.cdf(abs(runs_stat)))
                runs_p_values.append(p_value)

        if runs_p_values:
            runs_z_score = float(np.mean(runs_z_scores))
            avg_runs_p_value = float(np.mean(runs_p_values))
            is_uniform_runs = avg_runs_p_value > 0.01
            avg_total_runs = float(np.mean(total_runs_list))
            float(np.std(total_runs_list))
        else:
            runs_z_score = 0.0
            avg_runs_p_value = 1.0
            is_uniform_runs = False
            avg_total_runs = 0.0

        # === OVERALL STATUS ===
        uniformity_score = 0
        if is_uniform_bit:
            uniformity_score += 30
        if is_uniform_byte:
            uniformity_score += 30
        if is_uniform_runs:
            uniformity_score += 25
        if all_run_lengths:
            if practical_difference < 0.01:
                uniformity_score += 15
            elif practical_difference < 0.02:
                uniformity_score += 10
            elif practical_difference < 0.05:
                uniformity_score += 5

        if uniformity_score >= 90:
            status = "✨ EXCELLENT"
        elif uniformity_score >= 75:
            status = "✅ VERY GOOD"
        elif uniformity_score >= 60:
            status = "👍 GOOD"
        elif uniformity_score >= 40:
            status = "⚠️ FAIR"
        else:
            status = "❌ POOR"

        # === SONUÇ DICTIONARY ===
        result = {
            "samples": len(hash_lengths),
            "chi_square_bit": float(chi_square_bit),
            "chi_square_byte": float(chi_square_byte),
            "avg_run_length": float(avg_run),
            "std_run_length": float(std_run),
            "is_uniform_bit": bool(is_uniform_bit),
            "is_uniform_byte": bool(is_uniform_byte),
            "is_uniform_run_length": bool(is_uniform_run_length),
            "is_uniform_runs": bool(is_uniform_runs),
            "run_length_chi_square": float(run_chi_square),
            "avg_total_runs": float(avg_total_runs),
            "runs_z_score": float(runs_z_score),
            "zero_runs_count": len(run_lengths_zero),
            "one_runs_count": len(run_lengths_one),
            "total_runs_analyzed": len(all_run_lengths),
            "hash_length": hash_lengths[0] if hash_lengths else 0,
            "status": status,
            "bit_p_value": float(bit_p_value),
            "byte_p_value": float(byte_p_value),
            "run_p_value": float(run_p_value),
            "runs_p_value": float(avg_runs_p_value),
            "practical_difference": float(practical_difference),
            "is_practically_perfect": bool(is_practically_perfect),
            "test_duration": time() - start_time,
            "bit_min": int(np.min(bit_counts)),
            "bit_max": int(np.max(bit_counts)),
            "bit_mean": float(np.mean(bit_counts)),
            "bit_std": float(np.std(bit_counts)),
        }

        # === GÖSTERİŞLİ ÇIKTI ===
        print("\n" + "═" * 70)
        print("🏁 UNIFORMITY TEST RESULTS".center(70))
        print("═" * 70)

        print(f"\n📊 OVERALL STATUS: {status}")
        print("   " + "▬" * 50)

        print("\n┌─────────┬──────────────────────────────┬────────────┬─────────────┐")
        print("│   #     │           TEST                │   RESULT   │   STATS     │")
        print("├─────────┼──────────────────────────────┼────────────┼─────────────┤")

        # Bit testi
        bit_icon = "✅" if is_uniform_bit else "❌"
        bit_result = f"{bit_icon} PASS" if is_uniform_bit else f"{bit_icon} FAIL"
        print(
            f"│   1     │ Bit Distribution Test        │ {bit_result:<10} │ χ²={chi_square_bit:<6.2f} │"
        )
        print(
            f"│         │                              │            │ p={bit_p_value:<6.4f} │"
        )

        # Byte testi
        byte_icon = "✅" if is_uniform_byte else "❌"
        byte_result = f"{byte_icon} PASS" if is_uniform_byte else f"{byte_icon} FAIL"
        print("├─────────┼──────────────────────────────┼────────────┼─────────────┤")
        print(
            f"│   2     │ Byte Distribution Test       │ {byte_result:<10} │ χ²={chi_square_byte:<6.2f} │"
        )
        print(
            f"│         │                              │            │ p={byte_p_value:<6.4f} │"
        )

        # Runs test
        runs_icon = "✅" if is_uniform_runs else "❌"
        runs_result = f"{runs_icon} PASS" if is_uniform_runs else f"{runs_icon} FAIL"
        print("├─────────┼──────────────────────────────┼────────────┼─────────────┤")
        print(
            f"│   3     │ NIST Runs Test              │ {runs_result:<10} │ z={runs_z_score:<6.3f} │"
        )
        print(
            f"│         │                              │            │ p={avg_runs_p_value:<6.4f} │"
        )

        # Run length testi
        if is_uniform_run_length:
            rl_result = "✅ PASS"
        else:
            rl_result = "⚠️ PRAC" if is_practically_perfect else "❌ FAIL"

        print("├─────────┼──────────────────────────────┼────────────┼─────────────┤")
        print(
            f"│   4     │ Run Length Test             │ {rl_result:<10} │ μ={avg_run:<6.3f} │"
        )
        print(
            f"│         │                              │            │ σ={std_run:<6.3f} │"
        )
        print("└─────────┴──────────────────────────────┴────────────┴─────────────┘")

        print("\n" + "─" * 70)
        print("📊 DETAILED STATISTICS")
        print("─" * 70)
        print(f"   • Test Duration:     {result['test_duration']:.2f} seconds")
        print(f"   • Samples:           {result['samples']:,}")
        print(f"   • Hash Length:       {result['hash_length']} bytes (256 bits)")
        print(f"   • Total Runs:        {result['total_runs_analyzed']:,}")
        print(
            f"   • Zero/One Runs:     {result['zero_runs_count']:,} / {result['one_runs_count']:,}"
        )

        print("\n" + "─" * 70)
        print("📈 RUN LENGTH ANALYSIS")
        print("─" * 70)
        print(f"   • Observed Mean:     {avg_run:.4f}  (Theoretical: 2.0000)")
        print(f"   • Observed Std Dev:  {std_run:.4f}  (Theoretical: 1.4142)")
        print(f"   • Difference:        {practical_difference:.4f}  (Ideal: <0.01)")
        print(f"   • Z-Score:           {mean_z_score:.3f}")
        print(f"   • P-Value:           {run_p_value:.8f}")
        print(
            f"   ▶ PRATICAL VERDICT:  {'✓ PERFECT' if practical_difference < 0.01 else '✓ GOOD' if practical_difference < 0.05 else '⚠️ FAIR'}"
        )

        print("\n" + "─" * 70)
        print("🎲 BIT DISTRIBUTION")
        print("─" * 70)
        print(
            f"   • Min Ones:          {result['bit_min']:,}  (Expected: {int(expected_ones):,})"
        )
        print(
            f"   • Max Ones:          {result['bit_max']:,}  (Expected: {int(expected_ones):,})"
        )
        print(
            f"   • Mean Ones:         {result['bit_mean']:.2f}  (Expected: {expected_ones:.2f})"
        )
        print(f"   • Std Dev:           {result['bit_std']:.2f}")

        print("\n" + "═" * 70)
        print(
            f"🏁 TEST COMPLETED • {result['samples']:,} samples • {status}".center(70)
        )
        print("═" * 70 + "\n")

        return result

    """
    def test_uniformity(self, samples: int = 1000) -> Dict[str, Any]:
        #Statistical uniformity test for hash output.
        print(f"Uniformity test running with {samples} samples...")

        from scipy.stats import chi2, norm

        bit_counts = np.zeros(256, dtype=np.int64)
        byte_counts = np.zeros(256, dtype=np.int64)
        run_lengths_zero = []
        run_lengths_one = []
        total_runs = []
        hash_lengths = []

        # Runs test için bit dizilerini kaydet
        all_bit_arrays = []  # YENİ: bit dizilerini kaydet

        for i in range(samples):
            try:
                data_len = random.randint(1, 256)
                data = secrets.token_bytes(data_len)
                salt = secrets.token_bytes(32)
                hex_hash = self.hash(data, salt)
                h_bytes = bytes.fromhex(hex_hash)
                hash_lengths.append(len(h_bytes))

                if len(h_bytes) == 0:
                    continue

                byte_array = np.frombuffer(h_bytes, dtype=np.uint8)
                bits = np.unpackbits(byte_array)

                # Runs test için bit dizisini kaydet
                all_bit_arrays.append(bits)  # YENİ

                # Bit counts - her pozisyon için ayrı ayrı
                if len(bits) >= 256:
                    bit_counts += bits[:256]
                else:
                    padded_bits = np.zeros(256, dtype=np.uint8)
                    padded_bits[:len(bits)] = bits
                    bit_counts += padded_bits

                # Byte counts
                if len(byte_array) > 0:
                    hist = np.bincount(byte_array, minlength=256)
                    byte_counts += hist

                # Run analysis
                if len(bits) > 1:
                    changes = np.where(bits[1:] != bits[:-1])[0] + 1
                    starts = np.concatenate(([0], changes))
                    ends = np.concatenate((changes, [len(bits)]))
                    run_lengths = ends - starts

                    for start, length in zip(starts, run_lengths):
                        if bits[start] == 0:
                            run_lengths_zero.append(length)
                        else:
                            run_lengths_one.append(length)

                    total_runs.append(len(run_lengths))

                if (i + 1) % max(1, samples // 10) == 0:
                    print(f"  Progress: {i + 1}/{samples} samples")

            except Exception as e:
                print(f"Error at sample {i}: {str(e)[:100]}")
                continue

        total_bytes_counted = byte_counts.sum()

        # Bit Chi-square - DÜZELTİLDİ
        # Her bit pozisyonu için samples kadar gözlem olmalı
        # Ama bazı hash'ler 256 bitten kısa olabilir, o yüzden gerçek sayıyı hesapla
        valid_samples_for_bits = min(samples, len([l for l in hash_lengths if l >= 32]))  # 256 bit = 32 byte
        expected_per_position = valid_samples_for_bits

        # Sıfır bölme hatasını önle
        if expected_per_position > 0:
            chi_square_bit = np.sum((bit_counts - expected_per_position) ** 2 / expected_per_position)
            df_bit = 255  # 256-1 serbestlik derecesi
            bit_p_value = chi2.sf(chi_square_bit, df_bit)
            is_uniform_bit = bit_p_value > 0.01
        else:
            chi_square_bit = 0
            bit_p_value = 1.0
            is_uniform_bit = False

        # Byte Chi-square
        if total_bytes_counted > 0:
            expected_bytes = total_bytes_counted / 256
            chi_square_byte = np.sum((byte_counts - expected_bytes) ** 2 / expected_bytes)
            df_byte = 255
            byte_p_value = chi2.sf(chi_square_byte, df_byte)
            is_uniform_byte = byte_p_value > 0.01
        else:
            chi_square_byte = 0
            byte_p_value = 1.0
            is_uniform_byte = False

        # Run length statistics
        all_run_lengths = run_lengths_zero + run_lengths_one
        if all_run_lengths and len(all_run_lengths) > 10:  # Yeterli veri var mı?
            unique_lengths, length_counts = np.unique(all_run_lengths, return_counts=True)

            # Run length geometrik dağılım: P(X=k) = (0.5)^k için? Hayır, düzelt:
            # Fair coin için run length = k olasılığı: P = (0.5)^(k-1) * 0.5
            theoretical_probs = []
            for k in unique_lengths[:-1]:
                prob = (0.5 ** (k-1)) * 0.5  # Tam olarak k uzunluğunda run
                theoretical_probs.append(prob)
            # Son kategori için kümülatif (k veya daha uzun)
            if len(unique_lengths) > 0:
                last_prob = 0.5 ** (unique_lengths[-1]-1)  # k veya daha uzun run'lar
                theoretical_probs.append(last_prob)

            theoretical_probs = np.array(theoretical_probs)
            theoretical_probs /= theoretical_probs.sum()  # Normalize et
            theoretical_counts = theoretical_probs * len(all_run_lengths)

            # Küçük expected değerleri birleştir (Yates düzeltmesi)
            mask = theoretical_counts >= 5
            if mask.sum() > 1:
                run_chi_square = np.sum(
                    (length_counts[mask] - theoretical_counts[mask]) ** 2 / theoretical_counts[mask]
                )
                df_run = mask.sum() - 1
                run_p_value = chi2.sf(run_chi_square, df_run)
                is_uniform_run_length = run_p_value > 0.01
            else:
                run_chi_square = 0
                run_p_value = 1.0
                is_uniform_run_length = False

            avg_run = np.mean(all_run_lengths)
            std_run = np.std(all_run_lengths)
        else:
            avg_run = 0
            std_run = 0
            run_chi_square = 0
            run_p_value = 1.0
            is_uniform_run_length = False

        # NIST-style runs test
        runs_z_scores = []
        runs_p_values = [] # NameError: name 'runs_p_value' is not defined
        avg_total_runs = 0.0
        std_total_runs = 0.0
        runs_z_score = 0.0
        runs_p_value = 1.0
        is_uniform_runs = False

        for bits in all_bit_arrays:
            n = len(bits)
            if n < 2:
                continue

            # Proportion of ones
            pi = np.sum(bits) / n

            # Expected runs (NIST SP 800-22)
            expected_runs = (2 * n * pi * (1 - pi)) + (pi**2 + (1-pi)**2)

            # Observed runs
            changes = np.sum(bits[1:] != bits[:-1])
            observed_runs = changes + 1

            # Test statistic
            if n > 1 and pi != 0 and pi != 1:
                runs_stat = abs(observed_runs - expected_runs) / np.sqrt(2 * n * pi * (1-pi))
                runs_z_scores.append(runs_stat)

                # P-value (two-tailed)
                p_value = 2 * (1 - norm.cdf(abs(runs_stat)))
                runs_p_values.append(p_value)

        if runs_p_values:
            avg_runs_p_value = np.mean(runs_p_values)
            runs_z_score = float(np.mean(runs_z_scores)) if runs_z_scores else 0.0
            is_uniform_runs = avg_runs_p_value > 0.01
            avg_total_runs = float(np.mean(total_runs)) if total_runs else 0.0
            std_total_runs = float(np.std(total_runs)) if total_runs else 0.0
        else:
            runs_z_score = 0.0
            avg_runs_p_value = 1.0
            is_uniform_runs = False
            avg_total_runs = 0.0
            std_total_runs = 0.0

        # Overall status
        uniformity_score = 0
        if is_uniform_bit:
            uniformity_score += 1
        if is_uniform_byte:
            uniformity_score += 1
        if is_uniform_run_length:
            uniformity_score += 1
        if is_uniform_runs:
            uniformity_score += 1

        if uniformity_score == 4:
            status = "EXCELLENT"
        elif uniformity_score >= 3:
            status = "GOOD"
        elif uniformity_score >= 2:
            status = "FAIR"
        elif uniformity_score >= 1:
            status = "POOR"
        else:
            status = "FAIL"

        result = {
            "samples": len(hash_lengths),
            "chi_square_bit": float(chi_square_bit),
            "chi_square_byte": float(chi_square_byte),
            "avg_run_length": float(avg_run),
            "std_run_length": float(std_run),
            "is_uniform_bit": bool(is_uniform_bit),
            "is_uniform_byte": bool(is_uniform_byte),
            "run_length_chi_square": float(run_chi_square),
            "avg_total_runs": float(avg_total_runs),
            "runs_z_score": float(runs_z_score),
            "zero_runs_count": len(run_lengths_zero),
            "one_runs_count": len(run_lengths_one),
            "total_runs_analyzed": len(all_run_lengths),
            "hash_length_min": min(hash_lengths) if hash_lengths else 0,
            "hash_length_max": max(hash_lengths) if hash_lengths else 0,
            "hash_length_avg": (
                sum(hash_lengths) / len(hash_lengths) if hash_lengths else 0.0
            ),
            "status": status,
            "bit_p_value": float(bit_p_value),
            "byte_p_value": float(byte_p_value),
            "run_p_value": float(run_p_value),
            "runs_p_value": float(runs_p_value),
        }

        print("\nUniformity Test Results:")
        print(f"  Status: {status}")
        print(f"  Bit Uniformity: {is_uniform_bit} (χ²={chi_square_bit:.2f}, p={bit_p_value:.4f})")
        print(f"  Byte Uniformity: {is_uniform_byte} (χ²={chi_square_byte:.2f}, p={byte_p_value:.4f})")
        print(f"  Run Length: {is_uniform_run_length} (χ²={run_chi_square:.2f}, p={run_p_value:.4f})")
        print(f"  Runs Test: {is_uniform_runs} (z={runs_z_score:.3f}, p={runs_p_value:.4f})")
        print(f"  Avg Run Length: {avg_run:.3f} ± {std_run:.3f}")

        return result
    """

    """
    def test_uniformity(self, samples: int = 10_000) -> Dict[str, Any]:
        #Statistical uniformity test for hash output.
        print(f"Uniformity test running with {samples} samples...")

        from scipy.stats import chi2

        bit_counts = np.zeros(256, dtype=np.int64)
        byte_counts = np.zeros(256, dtype=np.int64)
        run_lengths_zero = []
        run_lengths_one = []
        total_runs = []
        hash_lengths = []

        for i in range(samples):
            try:
                data_len = random.randint(1, 256)
                data = secrets.token_bytes(data_len)
                salt = secrets.token_bytes(32)
                hex_hash = self.hash(data, salt)
                h_bytes = bytes.fromhex(hex_hash)
                hash_lengths.append(len(h_bytes))

                if len(h_bytes) == 0:
                    continue

                byte_array = np.frombuffer(h_bytes, dtype=np.uint8)
                bits = np.unpackbits(byte_array)

                # Bit counts - her pozisyon için ayrı ayrı
                if len(bits) >= 256:
                    bit_counts += bits[:256]
                else:
                    # Kısa hash'ler için padding yap
                    padded_bits = np.zeros(256, dtype=np.uint8)
                    padded_bits[:len(bits)] = bits
                    bit_counts += padded_bits

                # Byte counts
                if len(byte_array) > 0:
                    hist = np.bincount(byte_array, minlength=256)
                    byte_counts += hist

                # Run analysis
                if len(bits) > 1:
                    changes = np.where(bits[1:] != bits[:-1])[0] + 1
                    starts = np.concatenate(([0], changes))
                    ends = np.concatenate((changes, [len(bits)]))
                    run_lengths = ends - starts

                    for start, length in zip(starts, run_lengths):
                        if bits[start] == 0:
                            run_lengths_zero.append(length)
                        else:
                            run_lengths_one.append(length)

                    total_runs.append(len(run_lengths))

                if (i + 1) % max(1, samples // 10) == 0:
                    print(f"  Progress: {i + 1}/{samples} samples")

            except Exception as e:
                print(f"Error at sample {i}: {str(e)[:100]}")
                continue

        # total_bytes_counted değişkenini tanımla
        total_bytes_counted = byte_counts.sum()

        # Bit Chi-square - DÜZELTİLDİ
        # Her bit pozisyonu için samples kadar gözlem olmalı
        # Ama bazı hash'ler 256 bitten kısa olabilir, o yüzden gerçek sayıyı hesapla
        valid_samples_for_bits = min(samples, len([l for l in hash_lengths if l >= 32]))  # 256 bit = 32 byte
        expected_per_position = valid_samples_for_bits

        # Sıfır bölme hatasını önle
        if expected_per_position > 0:
            chi_square_bit = np.sum((bit_counts - expected_per_position) ** 2 / expected_per_position)
            df_bit = 255  # 256-1 serbestlik derecesi
            bit_p_value = chi2.sf(chi_square_bit, df_bit)
            is_uniform_bit = bit_p_value > 0.01
        else:
            chi_square_bit = 0
            bit_p_value = 1.0
            is_uniform_bit = False

        # Byte Chi-square
        if total_bytes_counted > 0:
            expected_bytes = total_bytes_counted / 256
            chi_square_byte = np.sum((byte_counts - expected_bytes) ** 2 / expected_bytes)
            df_byte = 255
            byte_p_value = chi2.sf(chi_square_byte, df_byte)
            is_uniform_byte = byte_p_value > 0.01
        else:
            chi_square_byte = 0
            byte_p_value = 1.0
            is_uniform_byte = False

        # Run length statistics
        all_run_lengths = run_lengths_zero + run_lengths_one
        if all_run_lengths and len(all_run_lengths) > 10:  # Yeterli veri var mı?
            unique_lengths, length_counts = np.unique(all_run_lengths, return_counts=True)

            # Run length geometrik dağılım: P(X=k) = (0.5)^k için? Hayır, düzelt:
            # Fair coin için run length = k olasılığı: P = (0.5)^(k-1) * 0.5
            theoretical_probs = []
            for k in unique_lengths[:-1]:
                prob = (0.5 ** (k-1)) * 0.5  # Tam olarak k uzunluğunda run
                theoretical_probs.append(prob)
            # Son kategori için kümülatif (k veya daha uzun)
            if len(unique_lengths) > 0:
                last_prob = 0.5 ** (unique_lengths[-1]-1)  # k veya daha uzun run'lar
                theoretical_probs.append(last_prob)

            theoretical_probs = np.array(theoretical_probs)
            theoretical_probs /= theoretical_probs.sum()  # Normalize et
            theoretical_counts = theoretical_probs * len(all_run_lengths)

            # Küçük expected değerleri birleştir (Yates düzeltmesi)
            mask = theoretical_counts >= 5
            if mask.sum() > 1:
                run_chi_square = np.sum(
                    (length_counts[mask] - theoretical_counts[mask]) ** 2 / theoretical_counts[mask]
                )
                df_run = mask.sum() - 1
                run_p_value = chi2.sf(run_chi_square, df_run)
                is_uniform_run_length = run_p_value > 0.01
            else:
                run_chi_square = 0
                run_p_value = 1.0
                is_uniform_run_length = False

            avg_run = np.mean(all_run_lengths)
            std_run = np.std(all_run_lengths)
        else:
            avg_run = 0
            std_run = 0
            run_chi_square = 0
            run_p_value = 1.0
            is_uniform_run_length = False

        # NIST-style runs test
        avg_total_runs = 0.0
        std_total_runs = 0.0
        runs_z_score = 0.0
        runs_p_value = 1.0
        is_uniform_runs = False

        if total_runs:
            avg_total_runs = float(np.mean(total_runs))
            std_total_runs = float(np.std(total_runs))

            # Hash'lerin bit uzunluklarını hash_lengths'ten hesapla
            bit_lengths = [l * 8 for l in hash_lengths]  # byte -> bit

            # Her hash için beklenen run sayısını hesapla
            expected_runs_per_hash = []
            for n_bits in bit_lengths:
                # Fair coin için beklenen run sayısı: (2 * n * p * (1-p)) + (p^2 + (1-p)^2)
                # p = 0.5 için: (n/2) + 0.5
                expected_runs = (n_bits / 2) + 0.5
                expected_runs_per_hash.append(expected_runs)

            # Ortalama beklenen run sayısı
            expected_total_runs = float(np.mean(expected_runs_per_hash))

            # Varyans: n * p * (1-p) * (1 - 3p + 3p^2) ...
            # Basitleştirilmiş: std = sqrt(n * p * (1-p))
            # p=0.5 için: sqrt(n/4)

            if std_total_runs > 0:
                runs_z_score = float(abs(avg_total_runs - expected_total_runs) / std_total_runs)

                from scipy.stats import norm
                runs_p_value = 2 * (1 - norm.cdf(abs(runs_z_score)))  # two-tailed
                is_uniform_runs = runs_p_value > 0.01
            else:
                runs_z_score = 0.0
                runs_p_value = 1.0
                is_uniform_runs = False
        else:
            runs_z_score = 0.0
            runs_p_value = 1.0
            is_uniform_runs = False

        # Overall status
        uniformity_score = 0
        if is_uniform_bit:
            uniformity_score += 1
        if is_uniform_byte:
            uniformity_score += 1
        if is_uniform_run_length:
            uniformity_score += 1
        if is_uniform_runs:
            uniformity_score += 1

        if uniformity_score == 4:
            status = "EXCELLENT"
        elif uniformity_score >= 3:
            status = "GOOD"
        elif uniformity_score >= 2:
            status = "FAIR"
        elif uniformity_score >= 1:
            status = "POOR"
        else:
            status = "FAIL"

        result = {
            "samples": len(hash_lengths),
            "chi_square_bit": float(chi_square_bit),
            "chi_square_byte": float(chi_square_byte),
            "avg_run_length": float(avg_run),
            "std_run_length": float(std_run),
            "is_uniform_bit": bool(is_uniform_bit),
            "is_uniform_byte": bool(is_uniform_byte),
            "run_length_chi_square": float(run_chi_square),
            "avg_total_runs": float(avg_total_runs),
            "runs_z_score": float(runs_z_score),
            "zero_runs_count": len(run_lengths_zero),
            "one_runs_count": len(run_lengths_one),
            "total_runs_analyzed": len(all_run_lengths),
            "hash_length_min": min(hash_lengths) if hash_lengths else 0,
            "hash_length_max": max(hash_lengths) if hash_lengths else 0,
            "hash_length_avg": (
                sum(hash_lengths) / len(hash_lengths) if hash_lengths else 0.0
            ),
            "status": status,
            "bit_p_value": float(bit_p_value),
            "byte_p_value": float(byte_p_value),
            "run_p_value": float(run_p_value),
            "runs_p_value": float(runs_p_value),
        }

        print("\nUniformity Test Results:")
        print(f"  Status: {status}")
        print(f"  Bit Uniformity: {is_uniform_bit} (χ²={chi_square_bit:.2f}, p={bit_p_value:.4f})")
        print(f"  Byte Uniformity: {is_uniform_byte} (χ²={chi_square_byte:.2f}, p={byte_p_value:.4f})")
        print(f"  Run Length: {is_uniform_run_length} (χ²={run_chi_square:.2f}, p={run_p_value:.4f})")
        print(f"  Runs Test: {is_uniform_runs} (z={runs_z_score:.3f}, p={runs_p_value:.4f})")
        print(f"  Avg Run Length: {avg_run:.3f} ± {std_run:.3f}")

        return result
    """

    def get_stats(self) -> Dict[str, Any]:
        """İstatistikleri getir"""
        stats: Dict[str, Any] = {}

        if hasattr(self.core, "stats"):
            for key, value in self.core.stats.items():
                stats[key] = value

        stats.update(self.metrics)

        hash_count = stats.get("hash_count", 0)
        total_time = stats.get("total_time", 0.0)
        total_operations = stats.get("total_operations", 0)

        if hash_count > 0:
            stats["avg_time_ms"] = float(total_time) / float(hash_count)
            if total_operations > 0:
                stats["operations_per_hash"] = float(total_operations) / float(
                    hash_count
                )
            else:
                stats["operations_per_hash"] = 0.0
        else:
            stats["avg_time_ms"] = 0.0
            stats["operations_per_hash"] = 0.0

        kha_success = stats.get("kha_success", 0)
        kha_fail = stats.get("kha_fail", 0)
        kha_total = kha_success + kha_fail

        if kha_total > 0:
            stats["kha_success_rate"] = (float(kha_success) / float(kha_total)) * 100.0
        else:
            stats["kha_success_rate"] = 0.0

        return stats

    def get_security_report(self) -> Dict[str, Any]:
        """Güvenlik raporu"""
        config_dict = {}
        if hasattr(self.config, "to_dict"):
            config_dict = self.config.to_dict()
        elif hasattr(self.config, "__dict__"):
            config_dict = {
                k: v for k, v in vars(self.config).items() if not k.startswith("_")
            }

        features = {}
        feature_attrs = {
            "diffusion_resistance": "enable_diffusion_mix",
            "quantum_resistance": "enable_quantum_mix",
            "memory_hardening": "memory_hardening",
            "side_channel_resistance": "enable_side_channel_resistance",
            "constant_time_ops": "enable_constant_time_ops",
            "encryption_layer": "enable_encryption_layer",
            # "post_quantum_mixing": "enable_post_quantum_mixing",
            "double_hashing": "double_hashing",
            "triple_compression": "triple_compression",
        }

        for feature_name, attr_name in feature_attrs.items():
            features[feature_name] = getattr(self.config, attr_name, False)

        stats = self.get_stats()

        return {
            "algorithm": "KHA-256-FORTIFIED",
            "version": "0.2.3",
            "security_level": getattr(self.config, "security_level", "256-bit"),
            "config": config_dict,
            "metrics": {
                "total_hashes": stats.get("hash_count", 0),
                "security_checks": stats.get("security_checks", 0),
                "kha_success_rate": stats.get("kha_success_rate", 0.0),
            },
            "features": features,
        }


def test_fortified_memory_hard_fixed():
    """FortifiedKhaHash256 memory-hard test"""

    print("\n" + "=" * 70)
    print("🧪 FORTIFIED MEMORY-HARD TEST - FIX EDİLMİŞ!")
    print("=" * 70)

    # Memory-hard config
    config = FortifiedConfig(
        enable_memory_hard_mode=True,
        memory_cost_kb=1024,  # 32 KB
        time_cost=3,
        # deterministic=False  # Deterministic DEĞİL!
    )
    salt = b"\x01\x02\x03\x04\x01\x02\x03\x04\x01\x02\x03\x04\x01\x02\x03\x04"
    hasher = FortifiedKhaHash256(config, deterministic=False)

    # Hash
    start = time.perf_counter()
    result = hasher.hash(b"test_password", salt)
    elapsed = (time.perf_counter() - start) * 1000

    print("\n📊 Fortified Memory-Hard Sonuç:")
    print(f"  • Config: {config.memory_cost_kb}KB, time={config.time_cost}")
    print(f"  • Süre: {elapsed:.2f}ms  ← GERÇEK MEMORY-HARD OLMALI!")
    print(f"  • Hash: {result[:32]}...")

    return elapsed


class OptimizedFortifiedConfig(FortifiedConfig):
    """
    Configuration for performance-optimized KHA-256 hashing.
    Extends the base FortifiedConfig with optimization-specific settings.
    """

    def __init__(
        self,
        cache_enabled: bool = True,
        cache_size: int = 256,  # Changed from max_cache_size to cache_size
        enable_metrics: bool = True,
        double_hashing: bool = False,  # False
        enable_byte_distribution_optimization: bool = True,  # False
        byte_uniformity_rounds: int = 5,  # 3: Optimal: 5 tur (NIST SP 800-90B)
        hash_bytes: int = 32,
        salt_length: int = 16,  # 16: NIST SP 800-132: 16-32
        # Pass through FortifiedConfig parameters
        **kwargs,
    ):
        # Call parent constructor first
        super().__init__(**kwargs)

        # Add optimization-specific settings
        self.cache_enabled = cache_enabled
        self.cache_size = cache_size  # Use consistent naming
        self.enable_metrics = enable_metrics
        self.double_hashing = double_hashing
        self.enable_byte_distribution_optimization = (
            enable_byte_distribution_optimization
        )
        self.byte_uniformity_rounds = byte_uniformity_rounds
        self.hash_bytes = hash_bytes
        self.salt_length = salt_length

    @property
    def max_cache_size(self) -> int:
        """Alias for cache_size for backward compatibility"""
        return self.cache_size


class HybridKhaHash256(FortifiedKhaHash256):
    """
    HYBRID KHA-256 implementation.
    Uses BLAKE2s for small data, KHA for large data.
    """

    # Threshold for switching algorithms
    SMALL_DATA_THRESHOLD = 1024  # 1KB
    MEDIUM_DATA_THRESHOLD = 8192  # 8KB

    def __init__(
        self,
        config: Optional[Union[OptimizedFortifiedConfig, FortifiedConfig]] = None,
        *,
        deterministic: bool = True,
        turbo_mode: bool = True,
        hybrid_mode: bool = True,  # Enable hybrid mode
    ):
        """
        Initialize the hybrid hasher.

        Args:
            config: Configuration object
            deterministic: Whether to use deterministic behavior
            turbo_mode: Enable extreme optimizations for speed
            hybrid_mode: Enable hybrid mode (BLAKE2s for small, KHA for large)
        """
        # Geçici config ile temel sınıfı başlat
        temp_config = FortifiedConfig() if config is None else config
        super().__init__(config=temp_config, deterministic=deterministic)
        # super().__init__(config, deterministic=deterministic)
        # Metrics'e total_time ekleyin
        self.metrics["total_time"] = 0.0  # BU SATIRI EKLEYİN

        self.turbo_mode = turbo_mode
        self.hybrid_mode = hybrid_mode

        # Şimdi kendi config'imizi ayarla
        if config is None:
            self.config = OptimizedFortifiedConfig(
                cache_enabled=True,
                cache_size=2048,
                enable_metrics=False,
                double_hashing=False,
                enable_byte_distribution_optimization=False,
                byte_uniformity_rounds=1,
                hash_bytes=32,
                salt_length=16,
                rounds=6,
                memory_cost_kb=1024,
                parallelism=1,
            )
        elif isinstance(config, FortifiedConfig) and not isinstance(
            config, OptimizedFortifiedConfig
        ):
            # Sadece FortifiedConfig'in desteklediği parametreler
            supported_params = [
                "cache_enabled",
                "cache_size",
                "enable_metrics",
                "double_hashing",
                "enable_byte_distribution_optimization",
                "byte_uniformity_rounds",
                "hash_bytes",
                "salt_length",
                "rounds",
                "memory_cost",
                "parallelism",
            ]

            kwargs: Dict[str, Any] = {}

            for param in supported_params:
                if hasattr(config, param):
                    try:
                        kwargs[param] = getattr(config, param)
                    except (AttributeError, ValueError):
                        pass

            # Varsayılanlar
            defaults = {
                "cache_enabled": True,
                "cache_size": 2048,
                "enable_metrics": False,
                "double_hashing": False,
                "enable_byte_distribution_optimization": False,
                "byte_uniformity_rounds": 1,
                "hash_bytes": 32,
                "salt_length": 16,
                "rounds": 6,
                "memory_cost": 512,
                "parallelism": 1,
            }

            for key, default_value in defaults.items():
                if key not in kwargs:
                    kwargs[key] = default_value

            self.config = OptimizedFortifiedConfig(**kwargs)
        else:
            self.config = cast(OptimizedFortifiedConfig, config)

        # Core'u başlat
        self.core = PerformanceOptimizedKhaCore(self.config)

        # Cache'ler
        self._cache: Dict[int, str] = {}
        self._blake2s_cache: Dict[int, bytes] = {}
        self._cache_hits = 0
        self.cache_misses = 0

        # Metrics
        self.metrics: Dict[str, Any] = {
            "hash_count": 0,
            "blake2s_count": 0,
            "kha_count": 0,
            "total_time_ms": 0.0,
        }

    # ------------------------------------------------------------------
    # Main hash method - HYBRID
    # ------------------------------------------------------------------

    def hash(self, data: Union[str, bytes], salt: Optional[bytes] = None) -> str:
        """Compute hybrid hash of data"""
        start_time = time.perf_counter()

        # Input conversion
        if isinstance(data, str):
            data_bytes = data.encode("utf-8")
        else:
            data_bytes = data

        data_len = len(data_bytes)

        # Choose algorithm based on size
        if self.hybrid_mode and data_len < self.SMALL_DATA_THRESHOLD:
            # Small data: use BLAKE2s
            result = self._blake2s_hash(data_bytes, salt)
            self.metrics["blake2s_count"] += 1
        elif self.hybrid_mode and data_len < self.MEDIUM_DATA_THRESHOLD:
            # Medium data: use optimized KHA
            result = self._optimized_kha_hash(data_bytes, salt)
            self.metrics["kha_count"] += 1
        else:
            # Large data: use full KHA
            result = self._full_kha_hash(data_bytes, salt)
            self.metrics["kha_count"] += 1

        # Update metrics
        self.metrics["hash_count"] += 1
        self.metrics["total_time_ms"] += (time.perf_counter() - start_time) * 1000

        return result

    # ------------------------------------------------------------------
    # Algorithm implementations
    # ------------------------------------------------------------------

    def _blake2s_hash(self, data: bytes, salt: Optional[bytes] = None) -> str:
        """Fast BLAKE2s hash for small data"""
        # Cache key
        cache_key = hash(data)
        if salt is not None:
            cache_key ^= hash(salt)

        # Check cache
        if cache_key in self._cache:
            self._cache_hits += 1
            return self._cache[cache_key]

        self.cache_misses += 1

        # Prepare input
        if salt is None:
            # Simple deterministic salt
            salt = struct.pack(">Q", len(data))

        # BLAKE2s with salt as key
        result = hashlib.blake2s(data, key=salt, digest_size=32).hexdigest()

        # Cache result
        if len(self._cache) < getattr(self.config, "cache_size", 2048):
            self._cache[cache_key] = result

        return result

    def _optimized_kha_hash(self, data: bytes, salt: Optional[bytes] = None) -> str:
        """Optimized KHA for medium data"""
        # Simple salt if not provided
        if salt is None:
            salt = hashlib.blake2s(data[:32], digest_size=8).digest()

        # Cache key
        cache_key = hash(data) ^ (hash(salt) << 32)

        # Check cache
        if cache_key in self._cache:
            self._cache_hits += 1
            return self._cache[cache_key]

        self.cache_misses += 1

        # Simplified KHA pipeline for medium data
        seed = self._derive_seed_simple(data, salt)
        matrix = self.core._generate_kha_matrix(seed)

        # Simple mixing
        mixed = self._fortified_mixing(matrix, salt)

        # Byte conversion
        raw_bytes = self.core._final_bytes_conversion(mixed, salt)

        # Compression
        hash_bytes = getattr(self.config, "hash_bytes", 32)
        final_bytes = self.core._secure_compress(raw_bytes, hash_bytes)

        result = final_bytes.hex()

        # Cache result
        if len(self._cache) < getattr(self.config, "cache_size", 2048):
            self._cache[cache_key] = result

        return result

    def _full_kha_hash(self, data: bytes, salt: Optional[bytes] = None) -> str:
        """Full KHA for large data"""
        # Use parent class hash method
        if salt is None:
            # Derive salt
            salt = hashlib.blake2s(data[:64], digest_size=16).digest()

        # Cache key
        cache_key = hash(data) ^ (hash(salt) << 32)

        # Check cache
        if cache_key in self._cache:
            self._cache_hits += 1
            return self._cache[cache_key]

        self.cache_misses += 1

        # Get hash from parent class
        result = super().hash(data, salt)

        # Cache result
        if len(self._cache) < getattr(self.config, "cache_size", 2048):
            self._cache[cache_key] = result

        return result

    # ------------------------------------------------------------------
    # Helper methods
    # ------------------------------------------------------------------

    def _derive_seed_simple(self, data: bytes, salt: bytes) -> bytes:
        """Simple seed derivation"""
        data_part = data[:16] if len(data) >= 16 else data
        salt_part = salt[:8] if len(salt) >= 8 else salt
        combined = data_part + salt_part

        if len(combined) < 24:
            combined = combined * (24 // len(combined) + 1)

        return hashlib.blake2s(combined[:24], digest_size=24).digest()

    def _fortified_mixing(
        self, matrix: Union[np.ndarray, int, float], salt: bytes
    ) -> np.ndarray:
        """Fortified mixing - seed hatası düzeltildi"""

        # Skaler değeri diziye dönüştür
        if isinstance(matrix, (int, float)):
            m = np.array([[float(matrix)]])
        else:
            m = matrix.copy().astype(np.float64)

        # SALT'TAN GÜVENLİ 32-bit SEED ÜRET
        if len(salt) >= 4:
            seed = int.from_bytes(salt[:4], "big")
        else:
            # Salt yetersizse, mevcut salt'ı kullan ve pad
            padded = salt.ljust(4, b"\0")
            seed = int.from_bytes(padded[:4], "big")

        # Seed'in 32-bit aralığında olduğundan emin ol
        seed = seed & 0xFFFFFFFF  # 0 - 4294967295 arası

        rng = np.random.RandomState(seed)

        # BÜYÜK ASAL SAYILAR (32-bit)
        PRIMES = [
            4294967291,  # 2^32 - 5
            4294967279,  # 2^32 - 17
            4294967231,  # 2^32 - 65
            1610612741,  # 30-bit
            805306457,  # 30-bit
            402653189,  # 29-bit
        ]

        # 1. TUR: Altın oran ve asal çarpım
        phi = (1 + np.sqrt(5)) / 2
        m = m * phi * PRIMES[0]
        m = np.sin(m) * PRIMES[1]

        # 2. TUR: Satır/sütun permütasyonu (yeterli boyut varsa)
        if m.shape[0] > 1 and m.shape[1] > 1:
            perm_rows = rng.permutation(m.shape[0])
            perm_cols = rng.permutation(m.shape[1])
            m = m[perm_rows][:, perm_cols]

        # 3. TUR: XOR tabanlı karıştırma
        if m.size == 1:
            # Skaler için
            val = m[0, 0]
            val_int = int(abs(val) * PRIMES[2]) % PRIMES[3]
            val_int ^= val_int >> 23
            val_int ^= val_int << 17
            val_int ^= val_int >> 13
            m[0, 0] = (val_int & 0xFFFFFFFF) / PRIMES[0]  # 32-bit güvenli
        else:
            # Matris için
            m_int = (m * PRIMES[2] % PRIMES[3]).astype(np.uint64)
            m_int ^= m_int >> 23
            m_int ^= m_int << 17
            m_int ^= m_int >> 13
            m = (m_int / PRIMES[0]).astype(np.float64)

        # 4. TUR: Normalizasyon
        m = np.abs(m - np.floor(m))

        # 5. TUR: Ek difüzyon (yeterli boyut varsa)
        if m.shape[0] > 1 and m.shape[1] > 1:
            m[1:, 1:] += m[:-1, :-1]
            m[1:, 1:] = m[1:, 1:] - np.floor(m[1:, 1:])

        return m

    """
    def _simple_mixing(self, matrix: np.ndarray, salt: bytes) -> np.ndarray:
        #Simple mixing for medium data
        salt_int = int.from_bytes(salt[:4], 'big') if len(salt) >= 4 else 12345
        np.random.seed(salt_int)

        mixed = matrix * 1.61803398875
        mixed = np.sin(mixed)
        return mixed
    """

    # ------------------------------------------------------------------
    # Utility Methods
    # ------------------------------------------------------------------

    def get_cache_stats(self) -> Dict[str, Any]:
        """Get cache statistics"""
        total = self._cache_hits + self.cache_misses
        hit_rate = self._cache_hits / total if total > 0 else 0.0

        return {
            "hits": self._cache_hits,
            "misses": self.cache_misses,
            "size": len(self._cache),
            "hit_rate": hit_rate,
            "max_size": getattr(self.config, "cache_size", 2048),
            "hybrid_mode": self.hybrid_mode,
            "blake2s_count": self.metrics.get("blake2s_count", 0),
            "kha_count": self.metrics.get("kha_count", 0),
        }

    def clear_cache(self) -> None:
        """Clear all caches"""
        self._cache.clear()
        self._blake2s_cache.clear()
        self._cache_hits = 0
        self.cache_misses = 0

    def get_metrics(self) -> Dict[str, Any]:
        """Get performance metrics"""
        metrics = self.metrics.copy()
        hash_count = metrics.get("hash_count", 0)

        if hash_count > 0:
            total_time = metrics.get("total_time_ms", 0.0)
            metrics["average_time_ms"] = total_time / hash_count
            metrics["hashes_per_second"] = (
                (hash_count / total_time * 1000) if total_time > 0 else 0
            )

            # Algorithm distribution
            blake2s_pct = (
                (metrics.get("blake2s_count", 0) / hash_count * 100)
                if hash_count > 0
                else 0
            )
            kha_pct = (
                (metrics.get("kha_count", 0) / hash_count * 100)
                if hash_count > 0
                else 0
            )
            metrics["blake2s_percentage"] = blake2s_pct
            metrics["kha_percentage"] = kha_pct
        else:
            metrics["average_time_ms"] = 0.0
            metrics["hashes_per_second"] = 0
            metrics["blake2s_percentage"] = 0
            metrics["kha_percentage"] = 0

        metrics.update(self.get_cache_stats())
        return metrics

    # ------------------------------------------------------------------
    # String representation and callability
    # ------------------------------------------------------------------

    def __str__(self) -> str:
        """String representation"""
        mode = " (HYBRID)" if self.hybrid_mode else ""
        return f"HybridKhaHash256{mode}"

    def __call__(self, data: Union[str, bytes]) -> str:
        """Make instance callable"""
        return self.hash(data)

    def enable_hybrid_mode(self, enable: bool = True) -> None:
        """Enable or disable hybrid mode"""
        self.hybrid_mode = enable
        self.clear_cache()


class OptimizedKhaHash256(FortifiedKhaHash256):
    """
    SIMPLE & FAST KHA-256 implementation.
    No micro-optimizations, just the essentials for speed.
    """

    def __init__(
        self,
        config: Optional[Union[OptimizedFortifiedConfig, FortifiedConfig]] = None,
        *,
        deterministic: bool = True,
        turbo_mode: bool = True,
    ):
        """
        Initialize the optimized hasher with minimal overhead.

        Args:
            config: Configuration object
            deterministic: Whether to use deterministic behavior
            turbo_mode: Enable extreme optimizations for speed
        """
        # Geçici config ile temel sınıfı başlat
        temp_config = FortifiedConfig() if config is None else config
        super().__init__(config=temp_config, deterministic=deterministic)

        self.turbo_mode = turbo_mode

        # Şimdi kendi config'imizi ayarla - MINIMAL OPTIMIZATIONS
        if config is None:
            self.config = OptimizedFortifiedConfig(
                cache_enabled=True,
                cache_size=2048,  # Optimal cache size
                enable_metrics=False,
                double_hashing=False,
                enable_byte_distribution_optimization=False,
                byte_uniformity_rounds=1,
                hash_bytes=32,
                salt_length=16 if turbo_mode else 32,
                rounds=3 if turbo_mode else 6,
                memory_cost_kb=1024 if turbo_mode else 2048,
                parallelism=1,
            )
        elif isinstance(config, FortifiedConfig) and not isinstance(
            config, OptimizedFortifiedConfig
        ):
            # Sadece FortifiedConfig'in desteklediği parametreler
            supported_params = [
                "cache_enabled",
                "cache_size",
                "enable_metrics",
                "double_hashing",
                "enable_byte_distribution_optimization",
                "byte_uniformity_rounds",
                "hash_bytes",
                "salt_length",
                "rounds",
                "memory_cost",
                "parallelism",
            ]

            kwargs: Dict[str, Any] = {}

            for param in supported_params:
                if hasattr(config, param):
                    try:
                        value = getattr(config, param)
                        kwargs[param] = value
                    except (AttributeError, ValueError):
                        pass

            # Varsayılanlar
            defaults = {
                "cache_enabled": True,
                "cache_size": 2048,
                "enable_metrics": False,
                "double_hashing": False,
                "enable_byte_distribution_optimization": False,
                "byte_uniformity_rounds": 1,
                "hash_bytes": 32,
                "salt_length": 16 if turbo_mode else 32,
                "rounds": 3 if turbo_mode else 6,
                "memory_cost": 256 if turbo_mode else 512,
                "parallelism": 1,
            }

            for key, default_value in defaults.items():
                if key not in kwargs:
                    kwargs[key] = default_value

            self.config = OptimizedFortifiedConfig(**kwargs)
        else:
            self.config = cast(OptimizedFortifiedConfig, config)

            # Turbo mode için basit güncelleme
            if turbo_mode:
                self.config.enable_byte_distribution_optimization = False
                self.config.byte_uniformity_rounds = 1
                self.config.enable_metrics = False

        # Core'u başlat
        self.core = PerformanceOptimizedKhaCore(self.config)

        # Basit metrics
        self.metrics: Dict[str, Any] = {
            "hash_count": 0,
            "total_time_ms": 0.0,
        }

        # Basit cache
        self._cache_hits = 0
        self.cache_misses = 0
        self._cache: Dict[int, str] = {}
        self._salt_cache: Dict[int, bytes] = {}
        self._matrix_cache: Dict[int, np.ndarray] = {}

    # ------------------------------------------------------------------
    # Main hash method - SIMPLE & FAST
    # ------------------------------------------------------------------

    def hash(self, data: Union[str, bytes], salt: Optional[bytes] = None) -> str:
        """Compute optimized hash of data - MINIMAL OVERHEAD"""
        # Input conversion
        if isinstance(data, str):
            data_bytes = data.encode("utf-8")
        else:
            data_bytes = data

        # Salt
        if salt is None:
            salt = self._derive_salt_fast(data_bytes)

        # Cache check - basit
        if getattr(self.config, "cache_enabled", True):
            cache_key = hash(data_bytes) ^ (hash(salt) << 32)
            cached = self._cache.get(cache_key)

            if cached is not None:
                self._cache_hits += 1
                return cached
            self.cache_misses += 1

        # Pipeline
        result = self._fast_pipeline(data_bytes, salt)

        # Store in cache
        if getattr(self.config, "cache_enabled", True):
            if len(self._cache) >= getattr(self.config, "cache_size", 2048):
                # Basit eviction
                keys = list(self._cache.keys())
                for k in keys[:100]:
                    del self._cache[k]
            self._cache[cache_key] = result

        return result

    # ------------------------------------------------------------------
    # Fast methods - MINIMAL
    # ------------------------------------------------------------------

    def _derive_salt_fast(self, data_bytes: bytes) -> bytes:
        """Fast salt derivation"""
        key = hash(data_bytes)
        if key in self._salt_cache:
            return self._salt_cache[key]

        # Çok basit salt
        data_len = len(data_bytes)
        if data_len == 0:
            salt = b"\x01\x02\x03\x04\x01\x02\x03\x04\x01\x02\x03\x04\x01\x02\x03\x04"
        elif data_len <= 4:
            salt = data_bytes * (4 // data_len + 1)
            salt = salt[:4]
        elif data_len < 64:
            salt = data_bytes[:2] + data_bytes[-2:]
        else:
            salt = hashlib.blake2s(data_bytes[:32], digest_size=4).digest()

        self._salt_cache[key] = salt
        return salt

    def _derive_seed_fast(self, data: bytes, salt: bytes) -> bytes:
        """Fast seed derivation"""
        # Basit kombinasyon
        data_part = data[:16] if len(data) >= 16 else data
        salt_part = salt[:8] if len(salt) >= 8 else salt
        combined = data_part + salt_part

        if len(combined) < 24:
            combined = combined * (24 // len(combined) + 1)

        return hashlib.blake2s(combined[:24], digest_size=24).digest()

    def _fast_pipeline(self, data: bytes, salt: bytes) -> str:
        """Fast pipeline with minimal overhead"""
        # 1. Matrix generation (with cache)
        seed = self._derive_seed_fast(data, salt)
        seed_key = hash(seed)

        if seed_key in self._matrix_cache:
            matrix = self._matrix_cache[seed_key]
        else:
            matrix = self.core._generate_kha_matrix(seed)
            if len(data) < 65536:
                self._matrix_cache[seed_key] = matrix

        # 2. Mixing - turbo mode için optimize
        if self.turbo_mode:
            mixed = self._fast_mixing_simple(matrix, salt)
        else:
            mixed = self.core._fortified_mixing_pipeline(matrix, salt)

        # 3. Final conversion
        raw_bytes = self.core._final_bytes_conversion(mixed, salt)

        # 4. Final compression
        hash_bytes = getattr(self.config, "hash_bytes", 32)
        final_bytes = self.core._secure_compress(raw_bytes, hash_bytes)

        return final_bytes.hex()

    def _fast_mixing_simple(self, matrix: np.ndarray, salt: bytes) -> np.ndarray:
        """Simple fast mixing"""
        # Çok basit mixing
        salt_int = int.from_bytes(salt[:4], "big") if len(salt) >= 4 else 12345
        np.random.seed(salt_int)

        mixed = matrix * 1.61803398875
        mixed = np.sin(mixed)
        mixed = (mixed * 1000) % 1.0

        return mixed

    # ------------------------------------------------------------------
    # Utility Methods
    # ------------------------------------------------------------------

    def get_cache_stats(self) -> Dict[str, Any]:
        """Get cache statistics"""
        total = self._cache_hits + self.cache_misses
        hit_rate = self._cache_hits / total if total > 0 else 0.0

        return {
            "hits": self._cache_hits,
            "misses": self.cache_misses,
            "size": len(self._cache),
            "hit_rate": hit_rate,
            "max_size": getattr(self.config, "cache_size", 2048),
            "salt_cache": len(self._salt_cache),
            "matrix_cache": len(self._matrix_cache),
            "turbo_mode": self.turbo_mode,
        }

    def clear_cache(self) -> None:
        """Clear all caches"""
        self._cache.clear()
        self._salt_cache.clear()
        self._matrix_cache.clear()
        self._cache_hits = 0
        self.cache_misses = 0

    def get_metrics(self) -> Dict[str, Any]:
        """Get performance metrics"""
        metrics = self.metrics.copy()
        hash_count = metrics.get("hash_count", 0)

        if hash_count > 0:
            total_time = metrics.get("total_time_ms", 0.0)
            metrics["average_time_ms"] = total_time / hash_count
            metrics["hashes_per_second"] = (
                (hash_count / total_time * 1000) if total_time > 0 else 0
            )
        else:
            metrics["average_time_ms"] = 0.0
            metrics["hashes_per_second"] = 0

        metrics.update(self.get_cache_stats())
        return metrics

    # ------------------------------------------------------------------
    # String representation and callability
    # ------------------------------------------------------------------

    def __str__(self) -> str:
        """String representation"""
        mode = " (TURBO)" if self.turbo_mode else ""
        return f"OptimizedKhaHash256{mode}"

    def __call__(self, data: Union[str, bytes]) -> str:
        """Make instance callable"""
        return self.hash(data)

    def enable_turbo_mode(self, enable: bool = True) -> None:
        """Enable or disable turbo mode"""
        self.turbo_mode = enable
        if enable:
            self.config.enable_byte_distribution_optimization = False
            self.config.byte_uniformity_rounds = 1
            self.config.enable_metrics = False
        self.clear_cache()


# ============================================================
# KOLAY KULLANIM FONKSİYONLARI
# ============================================================
# 3.1 Merkezi Hasher Factory


def generate_fortified_hasher(
    config=None, deterministic=True, purpose: str = "balanced"
) -> FortifiedKhaHash256:
    """Harmonize edilmiş: Önceki config ile %100 uyumlu"""
    if purpose == "password":  # Eski ayarlar
        config = FortifiedConfig(
            iterations=5, components_per_hash=48, memory_cost_kb=2048, time_cost=4
        )
    elif purpose == "secure":  # Bankacılık ↑
        config = FortifiedConfig(
            iterations=4, components_per_hash=64, memory_cost_kb=2048, time_cost=5
        )  # 64MB
    elif purpose == "fast":  # Mobil ↓
        config = FortifiedConfig(
            iterations=2, components_per_hash=24, memory_cost_kb=1024, time_cost=2
        )  # 4MB
    else:  # balanced/default
        config = FortifiedConfig(
            iterations=3, components_per_hash=32, memory_cost_kb=1024, time_cost=3
        )  # 8MB
    return FortifiedKhaHash256(config, deterministic=deterministic)


def generate_fortified_hasher_password(
    *,
    iterations: int = 5,
    components: int = 48,
    memory_cost_kb: int = 2048,
    time_cost: int = 4,
) -> FortifiedKhaHash256:
    """
    Parametrik Fortified KHA-256 oluşturucu.
    """
    config = FortifiedConfig(
        iterations=iterations,
        components_per_hash=components,
        memory_cost_kb=memory_cost_kb,
        time_cost=time_cost,
    )
    return FortifiedKhaHash256(config)


def generate_fortified_hasher_fast(
    *,
    iterations: int = 2,
    components: int = 24,
    memory_cost_kb: int = 1024,
    time_cost: int = 2,
) -> FortifiedKhaHash256:
    """
    Parametrik Fortified KHA-256 oluşturucu.
    """
    config = FortifiedConfig(
        iterations=iterations,
        components_per_hash=components,
        memory_cost_kb=memory_cost_kb,
        time_cost=time_cost,
    )
    return FortifiedKhaHash256(config)


def generate_fortified_hasher_secure(
    *,
    iterations: int = 4,
    components: int = 64,
    memory_cost_kb: int = 1024,
    time_cost: int = 5,
) -> FortifiedKhaHash256:
    """
    Parametrik Fortified KHA-256 oluşturucu.
    """
    config = FortifiedConfig(
        iterations=iterations,
        components_per_hash=components,
        memory_cost_kb=memory_cost_kb,
        time_cost=time_cost,
    )
    return FortifiedKhaHash256(config)


# Güvenli Parola Hashleme (KDF Modu, salt zorunlu)


def hash_password(
    data: bytes, salt: bytes, *, is_usb_key: bool = True, fast_mode: bool = False
) -> str:
    """
    SCrypt KHA - Salt ZORUNLU, USB varsayılan (2026 güvenli)
    """
    # Salt zorunlu - TypeError önleme
    if not isinstance(salt, bytes) or len(salt) < 16:
        raise ValueError("Salt bytes olmalı ve min 16 byte!")

    # USB varsayılan parametreleri
    if fast_mode:
        n, r, p = 16384, 8, 1     # 16MB - HIZLI
        maxmem = 32 * 1024 * 1024
    elif is_usb_key:  # USB = EN GÜÇLÜ (512MB), varsayılan
        n, r, p = 262144, 8, 1    # 256MB n  
        maxmem = 512 * 1024 * 1024  # 512MB
    else:             # NORMAL PC (64MB)
        n, r, p = 65536, 8, 1     # 64MB
        maxmem = 128 * 1024 * 1024

    digest = hashlib.scrypt(
        password=data,  # <- sadece bytes
        salt=salt,  # <- sadece bytes
        n=n,
        r=r,
        p=p,
        dklen=64,
        maxmem=maxmem,
    )

    prefix = "KHA256-USB$" if is_usb_key else "KHA256$"
    return f"{prefix}{salt.hex()}${digest.hex()}"


def hash_password_str(password: str, salt: bytes, **kwargs) -> str:
    """String wrapper - salt ZORUNLU"""
    return hash_password(password, salt, **kwargs)


"""
def hash_password(data: bytes, salt: Optional[bytes] = None, *, is_usb_key: bool = False, fast_mode: bool = True) -> str:

    #BYTES tabanlı !

    if salt is None:
        salt = secrets.token_bytes(32)  # Rastgele salt (deterministik değil)

    # TEK IF: Öncelik -> fast_mode > is_usb_key > varsayılan
    if fast_mode:
        n, r, p = 2**10, 8, 1  # 1MB - EN HIZLI
    elif is_usb_key:
        n, r, p = 2**12, 8, 1  # 4MB - USB
    else:
        n, r, p = 2**14, 8, 1  # 16MB - Full

    # BYTES → scrypt BYTES kabul eder!
    # TypeError: a bytes-like object is required, not 'str'
    digest_bytes = hashlib.scrypt(
        password=data,  # ← DATA ZATEN BYTES!
        salt=salt,      # ← SALT BYTES!
        n=n, r=r, p=p,
        dklen=32
    )

    prefix = "KHA256-USB$" if is_usb_key else "KHA256-DATA$"
    return f"{prefix}{salt.hex()}${digest_bytes.hex()}"

# String uyumluluğu (geriye uyumluluk)
def hash_password_str(password: str, salt: Optional[bytes] = None, *, is_usb_key: bool = False, fast_mode: bool = True) -> str:
    #String input için wrapper
    password_bytes = password.encode('utf-8')
    return hash_password(password_bytes, salt, is_usb_key=is_usb_key, fast_mode=fast_mode)
"""
"""
def hash_password(data: bytes, salt: Optional[bytes] = None, *, is_usb_key: bool = False, fast_mode: bool = True) -> str:

    BYTES verisi için KHA hash

    if salt is None:
        salt = secrets.token_bytes(32)

    # TEK IF: Öncelik sırası -> fast_mode > is_usb_key > varsayılan
    if fast_mode:
        n, r, p = 2**10, 8, 1  # 1MB - EN HIZLI 🏎️
    elif is_usb_key:
        n, r, p = 2**12, 8, 1  # 4MB - USB modu ⚡
    else:
        n, r, p = 2**14, 8, 1  # 16MB - Full güvenlik 🛡️

    digest_bytes = hashlib.scrypt(password=data, salt=salt, n=n, r=r, p=p, dklen=32)

    prefix = "KHA256-USB$" if is_usb_key else "KHA256-DATA$"
    return f"{prefix}{salt.hex()}${digest_bytes.hex()}"
"""

"""
def hash_password(
    password: str, salt: Optional[bytes] = None, *, is_usb_key: bool = False, fast_mode: bool = True
) -> str:

    Deterministik KDF tabanlı parola hash fonksiyonu.
    Aynı (parola + tuz) her zaman aynı çıktıyı verir.
    Args:
        password: Hashlenecek parola (str).
        salt: İsteğe bağlı tuz. Belirtilmezse rastgele üretilir.
        is_usb_key: Daha hızlı (düşük kaynak) mod.
    Returns:
        str: "KHA256[-USB]$<salt_hex>$<digest>"

    # 1. Parolayı byte'a çevir
    if not isinstance(password, str):
        raise TypeError("Password must be a string")
    password_bytes = password.encode("utf-8")

    # 2. Tuz oluştur
    if salt is None:
        salt = secrets.token_bytes(32)  # 32 byte = 256 bit — yeterli
    elif not isinstance(salt, bytes):
        raise TypeError("Salt must be bytes")

    # 3. scrypt parametreleri (OpenSSL bellek sınırına dikkat!)
    if is_usb_key:
        n, r, p = 2**12, 8, 1  # ~4 MB RAM
    else:
        n, r, p = 2**14, 8, 1  # ~16 MB RAM — çoğu sistemde çalışır

    # 4. scrypt ile deterministik türev
    try:
        digest_bytes = hashlib.scrypt(
            password=password_bytes,
            salt=salt,
            n=n,
            r=r,
            p=p,
            dklen=32,  # 256 bit output
        )
    except ValueError as e:
        if "memory limit exceeded" in str(e):
            # Fallback: daha düşük parametrelerle dene
            n, r, p = 2**10, 8, 1  # ~1 MB
            digest_bytes = hashlib.scrypt(
                password=password_bytes, salt=salt, n=n, r=r, p=p, dklen=32
            )
        else:
            raise

    # 5. Formatla
    prefix = "KHA256-USB$" if is_usb_key else "KHA256$"
    return f"{prefix}{salt.hex()}${digest_bytes.hex()}"
"""

# ======================
# KRIPTOGRAFİK HASHLER (256-bit)
# ======================


def quick_hash(data: Union[str, bytes]) -> str:
    """
    Hızlı ve güvenli 256-bit hash (BLAKE2b).
    Python 3.11+ optimize edilmiş, kriptografik ama KDF değildir.
    """
    if isinstance(data, str):
        data = data.encode("utf-8")
    return hashlib.blake2b(data, digest_size=32).hexdigest()


def quick_hash_sha256(data: Union[str, bytes]) -> str:
    """SHA-256 (256-bit) - Fallback olarak kullanılır."""
    if isinstance(data, str):
        data = data.encode("utf-8")
    return hashlib.sha256(data).hexdigest()


def quick_hash_blake3(data: Union[str, bytes]) -> str:
    """BLAKE3 - En hızlı kriptografik hash (256-bit varsayılan)."""
    if blake3 is None:
        raise RuntimeError("BLAKE3 modülü yüklenemiyor: pip install blake3")
    if isinstance(data, str):
        data = data.encode("utf-8")
    return blake3(data).hexdigest()  # 32 byte = 256 bit


def quick_hash_raw(data: Union[str, bytes]) -> bytes:
    """BLAKE3 raw bytes (hex dönüşümü yok → %45 daha hızlı)."""
    if blake3 is None:
        raise RuntimeError("BLAKE3 modülü yüklenemiyor: pip install blake3")
    if isinstance(data, str):
        data = data.encode("utf-8")
    return blake3(data).digest()  # 32 byte raw output


# ======================
# NON-KRIPTOGRAFİK (CACHE KEY, INDEXING)
# ======================


def ultra_fast_hash(data: Union[str, bytes]) -> int:
    """
    xxHash64 - En hızlı non-kriptografik hash.
    NOT: 64-bit output üretir (256-bit DEĞİL). Sadece cache/index için kullanın.
    """
    if xxhash is None:
        raise RuntimeError("xxhash modülü yüklenemiyor: pip install xxhash")
    if isinstance(data, str):
        data = data.encode("utf-8")
    return xxhash.xxh64_intdigest(data)


def ultra_fast_hash_hex(data):
    """xxHash64 - hex formatında"""
    if isinstance(data, str):
        data = data.encode("utf-8")
    return xxhash.xxh64_hexdigest(data)  # 16 karakter hex


# integer'ı hex'e çevir:


def ultra_fast_hash_int_to_hex(data):
    """xxHash64 - integer'ı hex'e çevir"""
    hash_int = ultra_fast_hash(data)
    return hex(hash_int)[2:]  # '0x' kaldırılır, 16 karakter
    # veya: f"{hash_int:016x}"  # 16 karakter, başa sıfır ekler


def fastest_cache_key(data: str) -> int:
    """String için optimize edilmiş cache key (xxHash64)."""
    if xxhash is None:
        raise RuntimeError("xxhash modülü yüklenemiyor: pip install xxhash")
    return xxhash.xxh64_intdigest(data.encode("utf-8"))


# ======================
# PURE PYTHON (DEPENDENCY-FREE)
# ======================


def fnv1a_64(data: Union[str, bytes]) -> str:
    """64-bit FNV-1a - dependency-free, non-kriptografik."""
    if isinstance(data, str):
        data = data.encode("utf-8")
    h = 0xCBF29CE484222325
    for b in data:
        h ^= b
        h = (h * 0x100000001B3) & 0xFFFFFFFFFFFFFFFF
    return f"{h:016x}"


def djb2_optimized(data: Union[str, bytes]) -> int:
    """DJB2 - micro-optimized pure Python."""
    if isinstance(data, str):
        data = data.encode("utf-8")
    h = 5381
    for b in data:
        h = ((h << 5) + h + b) & 0xFFFFFFFFFFFFFFFF
    return h


# ======================
# GÜVENLİ PASSWORD HASHING (KDF)
# ======================


def hash_argon2id(password: str) -> str:
    """
    Production-ready password hashing (Argon2id - en güvenli seçenek).
    Output: 256-bit+ encoded hash.
    """
    from argon2 import PasswordHasher

    ph = PasswordHasher(
        time_cost=3,  # Iterasyon sayısı (t)
        memory_cost_kb=16256,  # Bellek maliyeti (m KB): 65536: 64 MB
        parallelism=12,  # Paralellik (p): threadripper desteği
        hash_len=32,  # Çıktı uzunluğu (byte)
    )
    if ph is None:
        raise RuntimeError("argon2 modülü yüklenemiyor: pip install argon2-cffi")
    return ph.hash(password)


def hash_bcrypt(password: str, rounds: int = 12) -> str:
    """
    bcrypt password hashing (adaptive cost).
    Output: ~184-bit effective security (yeterli).
    """
    if bcrypt is None:
        raise RuntimeError("bcrypt modülü yüklenemiyor: pip install bcrypt")
    salt = bcrypt.gensalt(rounds=rounds)
    return bcrypt.hashpw(password.encode("utf-8"), salt).decode("utf-8")


def hash_pbkdf2(password: str, salt: bytes = None, iterations: int = 600_000) -> str:
    """
    PBKDF2-HMAC-SHA256 (NIST önerilen).
    Output: 256-bit key + salt.
    """
    if salt is None:
        salt = os.urandom(16)
    dk = hashlib.pbkdf2_hmac(
        "sha256", password.encode("utf-8"), salt, iterations, dklen=32
    )
    return salt.hex() + ":" + dk.hex()


# ======================
# BATCH İŞLEMLER
# ======================


def batch_hash_xxh64(passwords: list[str]) -> list[int]:
    """Hızlı batch hashing (non-kriptografik, benchmark için)."""
    if xxhash is None:
        raise RuntimeError("xxhash modülü yüklenemiyor: pip install xxhash")
    return [xxhash.xxh64_intdigest(p.encode("utf-8")) for p in passwords]


def batch_hash_secure(passwords: list[str]) -> list[str]:
    """Güvenli batch password hashing (Argon2id)."""
    from argon2 import PasswordHasher

    ph = PasswordHasher(
        time_cost=3,  # Iterasyon sayısı (t)
        memory_cost_kb=16256,  # Bellek maliyeti (m KB): 65536: 64 MB
        parallelism=12,  # Paralellik (p): threadripper desteği
        hash_len=32,  # Çıktı uzunluğu (byte)
    )
    if ph is None:
        raise RuntimeError("argon2 modülü yüklenemiyor: pip install argon2-cffi")
    return [ph.hash(p) for p in passwords]


# ======================
# EN HIZLI NON-KRİPTOGRAFİK (Indexing/Cache için)
# ======================


def fast_hash_int(data: Union[str, bytes]) -> int:
    """
    xxHash64 integer — cache key/index için ideal.
    Ubuntu 25.10'de ~130 ns/op.
    """
    try:
        import xxhash
    except ImportError:
        # Fallback: SHA-256 ilk 8 byte
        if isinstance(data, str):
            data = data.encode("utf-8")
        return int.from_bytes(hashlib.sha256(data).digest()[:8], "big")

    if isinstance(data, str):
        data = data.encode("utf-8")
    return xxhash.xxh64_intdigest(data)


# ======================
# GÜVENLİ ŞİFRELEME (Sadece şifre depolama için)
# ======================


def secure_hash_password(password: str) -> str:
    """
    Production şifre hashing — NIST SP 800-63B, OWASP uyumlu.
    """
    try:
        from argon2 import PasswordHasher

        # Argon2id - OWASP #1 öneri
        ph = PasswordHasher(
            time_cost=3,  # 3 iterasyon
            memory_cost_kb=19456,  # 19 MB - OWASP min 15 MB
            parallelism=1,  # 1 - güvenlik için!
            hash_len=32,  # 256 bit çıktı
            salt_len=16,  # 128 bit salt - NIST yeterli!
        )
        return ph.hash(password)
    except ImportError:
        # PBKDF2 - NIST onaylı fallback
        salt = os.urandom(16)  # 128-bit salt
        dk = hashlib.pbkdf2_hmac(
            "sha256",
            password.encode("utf-8"),
            salt,
            600_000,  # 600k iterasyon
            dklen=32,  # 256 bit çıktı
        )
        return f"pbkdf2:sha256:600000:{salt.hex()}:{dk.hex()}"


# xxh64_hash (hex string output)


def xxh64_hash(data: Union[str, bytes]) -> str:
    """xxHash64 hexdigest output (non-kriptografik)."""
    if xxhash is None:
        raise RuntimeError("xxhash modülü yüklenemiyor: pip install xxhash")
    if isinstance(data, str):
        data = data.encode("utf-8")
    return xxhash.xxh64(data).hexdigest()  # 16 karakter hex (64-bit)


# quick_hash_cached (LRU cache'li versiyon)
@lru_cache
def quick_hash_cached(data: Union[str, bytes]):
    if isinstance(data, str):
        data = data.encode("utf-8")
    return hashlib.blake2b(data, digest_size=32).hexdigest()  # bytes güvenli


# 128-bit BLAKE3 (daha hızlı, collision risk düşük ama 256-bit değil)
def quick_hash_128(data: Union[str, bytes]) -> str:
    """BLAKE3 128-bit output (32 karakter hex)."""
    if blake3 is None:
        raise RuntimeError("BLAKE3 modülü yüklenemiyor: pip install blake3")
    if isinstance(data, str):
        data = data.encode("utf-8")
    return blake3(data).hexdigest(length=16)


def gizli_turkce_hash(metin, gizli_mod="🔒ZİNCİR"):
    """
    KHA-256 + Türkçe karakter zinciri + ZORUNLU SALT
    """
    # ✅ ZORUNLU: Her seferinde salt üret
    salt = secrets.token_bytes(32)
    metin.encode("utf-8")

    # Türkçe karakter zincirleme
    turkce_karakterler = "ğüşıöçĞÜŞİÖÇ"
    gizli_zincir = ""

    for char in metin:
        if char in turkce_karakterler:
            gizli_zincir += chr(0xF000 + ord(char))

    # SUPER DATA: mod + zincir + orijinal + salt
    super_data = (gizli_mod + gizli_zincir + metin).encode("utf-8")

    # ÇİFT KHA-256 (her ikisi de saltlı)
    hasher1 = FortifiedKhaHash256()
    ara_hash = hasher1.hash(super_data, salt=salt)

    hasher2 = FortifiedKhaHash256()
    final_hash = hasher2.hash(ara_hash.encode("utf-8"), salt=salt)

    print("🔥 Gizli Türkçe Zincir KHA-256 (Salt zorunlu)")
    print(f"   • Zincir: {len(gizli_zincir)} karakter")
    print(f"   • Salt: {len(salt)} bytes")

    return final_hash


class KHA256UnicodeHasher:
    """KHA-256 ile Unicode uyumlu hash sınıfı"""

    def __init__(self, salt_length=32, log_output=True):
        self.salt_length = salt_length
        self.log_output = log_output
        self.global_salt = secrets.token_bytes(salt_length)

    def unicode_hash(self, metin, mode="fortified", log=None):
        """
        Unicode metni KHA-256 ile hash'ler

        Args:
            metin (str): Hash'lenecek Unicode metin
            mode (str): 'fortified' (saltlı), 'quick' (hızlı), 'default' (fortified)
            log (bool): Log göster (None=class ayarı)

        Returns:
            str: 64 karakterli hex hash
        """
        if log is None:
            log = self.log_output

        data = metin.encode("utf-8")

        if mode == "quick":
            # Hızlı saltsız hash
            return quick_hash(data)

        elif mode == "fortified" or mode == "default":
            # Saltlı fortified hash
            hasher = FortifiedKhaHash256()
            result = hasher.hash(data, salt=self.global_salt)

            if log:
                print("[KHA-256 Çıktısı]")
            return result

        else:
            raise ValueError("mode: 'quick', 'fortified' veya 'default' olmalı")

    def new_salt(self):
        """Yeni salt üretir"""
        self.global_salt = secrets.token_bytes(self.salt_length)
        print(f"Yeni salt üretildi: {self.salt_length} bytes")


class Shake256Hasher:
    """SHAKE256 hash sınıfı - ek özelliklerle
    from Crypto.Hash import SHAKE256
    """

    def __init__(self, output_length=32):
        """
        SHAKE256 hasher başlatıcı

        Args:
            output_length: Varsayılan çıktı uzunluğu (bytes)
        """
        self.default_output_length = output_length

    def hash(self, data, output_length=None):
        """
        Veriyi hash'ler

        Args:
            data: Hash'lenecek veri
            output_length: Çıktı uzunluğu (None ise varsayılan kullanılır)

        Returns:
            bytes: Hash değeri
        """
        if output_length is None:
            output_length = self.default_output_length

        return shake256_hash(data, output_length)

    def hash_hex(self, data, output_length=None):
        """Hash'i hex string olarak döndür"""
        return self.hash(data, output_length).hex()

    def hash_int(self, data, output_length=None):
        """Hash'i integer olarak döndür"""
        return int.from_bytes(self.hash(data, output_length), byteorder="big")

    def file_hash(self, filepath, output_length=32):
        """Dosyanın hash'ini hesaplar"""
        with open(filepath, "rb") as f:
            return shake256_hash(f.read(), output_length)


def shake256_hash(data, output_length=32):
    """
    SHAKE256 değişken çıktı uzunluklu hash fonksiyonu.

    Args:
        data: Hash'lenecek veri (bytes veya string)
        output_length: İstenen çıktı uzunluğu (bytes cinsinden)

    Returns:
        bytes: Belirtilen uzunlukta hash değeri

    Raises:
        ValueError: output_length negatif veya çok büyükse
    """
    # Çıktı uzunluğu kontrolü
    if output_length <= 0:
        raise ValueError("Çıktı uzunluğu pozitif olmalı")
    if output_length > 2**32:  # Makul bir üst sınır
        raise ValueError("Çıktı uzunluğu çok büyük")

    # Eğer data string ise bytes'a çevir
    if isinstance(data, str):
        data = data.encode("utf-8")
    elif not isinstance(data, bytes):
        # Diğer tipler için string'e çevir
        data = str(data).encode("utf-8")

    # SHAKE256 hash objesi oluştur
    shake = SHAKE256.new()

    # Veriyi güncelle
    shake.update(data)

    # Belirtilen uzunlukta hash değerini al
    return shake.read(output_length)


# Kısa kullanım için yardımcı fonksiyonlar


def shake256_128(data):
    """128 bit (16 byte) SHAKE256 hash"""
    return shake256_hash(data, 16)


def shake256_256(data):
    """256 bit (32 byte) SHAKE256 hash"""
    return shake256_hash(data, 32)


def shake256_512(data):
    """512 bit (64 byte) SHAKE256 hash"""
    return shake256_hash(data, 64)


# Test fonksiyonu


def test_shake256():
    """SHAKE256 fonksiyonlarını test et"""

    test_cases = [
        ("", "Boş string"),
        ("a", "Tek karakter"),
        ("abc", "Kısa string"),
        ("Hello World!", "Noktalama ile"),
        ("x" * 1000, "Uzun string"),
    ]

    print("SHAKE256 Test Sonuçları:")
    print("=" * 60)

    for data, description in test_cases:
        print(f"\nTest: {description}")
        print(f"Veri: '{data[:50]}{'...' if len(data) > 50 else ''}'")

        # Farklı uzunluklarda hash
        for length in [16, 32, 64]:
            hash_val = shake256_hash(data, length)
            print(f"  {length} byte: {hash_val.hex()[:40]}...")

    # Performans testi
    import time

    large_data = "x" * 1000000  # 1MB veri

    start = time.time()
    shake256_hash(large_data, 32)
    elapsed = time.time() - start

    print(f"\nPerformans: 1MB veri için {elapsed:.3f} saniye")


# ======================
# PERFORMANS ÖLÇÜMÜ
# ======================


def measure_hash(func, *args) -> tuple[float, any]:
    """Hassas timing ölçümü (milisaniye cinsinden)."""
    start = time.perf_counter()
    result = func(*args)
    elapsed_ms = (time.perf_counter() - start) * 1000
    return elapsed_ms, result


# Üretim öncesi güvenlik kontrolü


def _validate_blake2_params(salt: bytes, person: bytes):
    if len(salt) > 16 or len(person) > 16:
        raise ValueError(  # ← SecurityError yerine
            f"BLAKE2 params exceed 16 bytes (salt:{len(salt)}, person:{len(person)})"
        )


def test_fortified_hashers() -> Dict[str, Dict[str, Any]]:
    """
    Tüm fortified hasher'ları import kha256'dan çekip test eder.
    Her config için: süre (ms), RAM (MB), hash kalitesi
    """
    results = {}

    # Test vektörleri (aynı input'lar collision kontrolü için)
    test_passwords = [
        "SecureKHA2026_test123!",
        "password",
        "1234567890abcdef",
        "admin",
        "user@example.com",
    ]

    # 1. generate_fortified_hasher(purpose)
    purposes = ["fast", "balanced", "password", "secure"]
    for purpose in purposes:
        print(f"\n🔍 Testing: generate_fortified_hasher('{purpose}')")
        hasher = generate_fortified_hasher(purpose=purpose)

        total_time = 0
        hashes = []

        for pwd in test_passwords:
            start = time.perf_counter()
            hash_result = hasher.hash(pwd.encode())
            elapsed = (time.perf_counter() - start) * 1000  # ms

            total_time += elapsed
            hashes.append(hash_result)
            print(f"  '{pwd}' → {elapsed:.1f}ms → {hash_result[:16]}...")

        avg_time = total_time / len(test_passwords)
        unique_hashes = len(set(hashes))

        results[f"{purpose}_purpose"] = {
            "avg_time_ms": avg_time,
            "collision_free": unique_hashes == len(test_passwords),
            "sample_hash": hashes[0][:32] if hashes else None,
            "config": purpose,
        }
        print(
            f"  📊 AVG: {avg_time:.1f}ms | Collision: {100 * unique_hashes / len(test_passwords):.1f}%"
        )

    # 2. generate_fortified_hasher_password() parametrik
    print("\n🔍 Testing: generate_fortified_hasher_password() defaults")
    hasher_param = generate_fortified_hasher_password()
    total_time_param = 0
    hashes_param = []

    for pwd in test_passwords:
        start = time.perf_counter()
        hash_result = hasher_param.hash(pwd.encode())
        elapsed = (time.perf_counter() - start) * 1000
        total_time_param += elapsed
        hashes_param.append(hash_result)

    results["password_param"] = {
        "avg_time_ms": total_time_param / len(test_passwords),
        "collision_free": len(set(hashes_param)) == len(test_passwords),
        "sample_hash": hashes_param[0][:32] if hashes_param else None,
        "config": "password(defaults)",
    }

    # 3. _pca_var_sum fonksiyonlar
    fixed_functions = [
        ("secure", generate_fortified_hasher_secure),
        ("fast", generate_fortified_hasher_password),
        ("fast", generate_fortified_hasher_fast),
    ]

    for name, func in fixed_functions:
        print(f"\n🔍 Testing: {name}()")
        hasher = func()

        total_time = 0
        hashes = []
        for pwd in test_passwords:
            start = time.perf_counter()
            hash_result = hasher.hash(pwd.encode())
            elapsed = (time.perf_counter() - start) * 1000
            total_time += elapsed
            hashes.append(hash_result)

        avg_time = total_time / len(test_passwords)
        unique_hashes = len(set(hashes))

        results[name] = {
            "avg_time_ms": avg_time,
            "collision_free": unique_hashes == len(test_passwords),
            "sample_hash": hashes[0][:32] if hashes else None,
            "config": name,
        }
        print(
            f"  📊 AVG: {avg_time:.1f}ms | Collision: {100 * unique_hashes / len(test_passwords):.1f}%"
        )

    return results


def print_results_table(results: Dict[str, Dict[str, Any]]):
    """Güzel tablo çıktısı"""
    print("\n" + "=" * 80)
    print("🏆 FORTIFIED KHA256 HASHER BENCHMARK RESULTS")
    print("=" * 80)

    table_data = []
    for key, data in results.items():
        status = "✅" if data["collision_free"] else "❌"
        table_data.append(
            [
                key.replace("_", " ").title(),
                f"{data['avg_time_ms']:.1f}ms",
                f"{status} {100 * 1:.0f}%",
                data["sample_hash"] or "N/A",
            ]
        )

    # Pandas tablo (optional)
    df = pd.DataFrame(table_data, columns=["Config", "Avg Time", "Collision", "Sample"])
    print(df.to_string(index=False))

    # Performans kategorisi
    print("\n🎯 PERFORMANCE CATEGORIES:")
    fast_configs = [k for k, v in results.items() if v["avg_time_ms"] < 120]
    balanced = [k for k, v in results.items() if 120 <= v["avg_time_ms"] < 300]
    heavy = [k for k, v in results.items() if v["avg_time_ms"] >= 300]

    print(f"🚀 FAST (<120ms): {', '.join(fast_configs) or 'None'}")
    print(f"⚖️  BALANCED (120-300ms): {', '.join(balanced) or 'None'}")
    print(f"🛡️  HEAVY (300ms+): {', '.join(heavy) or 'None'}")


def gpu_resistance_test(hasher, count=100):
    """GPU direncini ölç: Gerçek memory-hard ise 100 hash >5 sn sürmeli"""
    start = time.perf_counter()
    for i in range(count):
        _ = hasher.hash(f"pwd{i}".encode())
    elapsed = time.perf_counter() - start

    hashes_per_sec = count / elapsed
    print(f"100 hash: {elapsed * 1000:.0f} ms → {hashes_per_sec:.0f} hash/sn")

    if hashes_per_sec > 50:
        print("⚠️  UYARI: GPU ile kırılabilir! (Gerçek memory-hard değil)")
    elif hashes_per_sec > 10:
        print("✅ Orta direnç (sadece zaman maliyeti)")
    else:
        print("🔒 Yüksek direnç (gerçek memory-hard)")


def secure_avalanche_mix(data: bytes, salt: bytes) -> bytes:
    """NIST onaylı, deterministik, side-channel safe mixing"""
    # BLAKE3 zaten mükemmel avalanche effect'e sahip
    return blake3(data + salt, length=64).digest()  # 512-bit output


# ChaCha20 Permutation (Hızlı + Güvenli)


def chacha_avalanche_mix(data: bytes, salt: bytes) -> bytes:
    """ChaCha20 quarter rounds - kanıtlanmış diffusion"""
    key = (data + salt)[:32]  # 256-bit key
    cipher = ChaCha20.new(key=key, nonce=b"\x00" * 12)
    return cipher.encrypt(b"\x00" * 64)  # 512-bit pseudo-random output


class MockCore:
    class MockConfig:
        shuffle_layers = 4
        # Deterministik mod için gerekli config parametreleri
        deterministic = True
        enable_quantum_mix = True
        enable_diffusion_mix = True
        salt_length = 16

    def __init__(self, deterministic=True):
        self.config = self.MockConfig()
        self._deterministic = deterministic


# ============================================================
# DÜZELTİLMİŞ TEST - DETERMINISTIC!
# ============================================================


# 1. SABIT SALT - Deterministik test için!
FIXED_SALT = b"kha256_deterministic_test_salt_2026_32bytes!"  # 32 byte sabit!
FIXED_SALT_2 = b"another_fixed_salt_for_testing_purposes_2026"  # 32 byte sabit!

# 2. MockCore'a metodları ekle
MockCore._quantum_avalanche_mix = FortifiedKhaCore._quantum_avalanche_mix
MockCore._enhanced_byte_diffusion = FortifiedKhaCore._enhanced_byte_diffusion
MockCore._secure_diffusion_mix = FortifiedKhaCore._secure_diffusion_mix

# ============================================================
# TEST 1: _quantum_avalanche_mix - DETERMINISTIC
# ============================================================
print("\n" + "=" * 60)
print("🔬 TEST: _quantum_avalanche_mix (Deterministik)")
print("=" * 60)

core = MockCore(deterministic=True)
test_matrix = np.random.RandomState(42).random(64).astype(np.float64)  # Sabit seed!

try:
    # ✅ DOĞRU: AYNI salt ile 2 kez çağır!
    result1 = core._quantum_avalanche_mix(test_matrix, FIXED_SALT)
    result2 = core._quantum_avalanche_mix(test_matrix, FIXED_SALT)

    print("✅ _quantum_avalanche_mix hatasız çalıştı")
    print(f"   Input shape: {test_matrix.shape} → Output shape: {result1.shape}")
    print(f"   Sample values: {result1[:5]}")

    # Deterministiklik testi
    assert np.allclose(result1, result2), (
        "Deterministiklik hatası! Aynı salt farklı sonuç verdi!"
    )
    print("✅ Deterministiklik doğrulandı (aynı salt → aynı sonuç)")

    # FARKLI salt testi
    result3 = core._quantum_avalanche_mix(test_matrix, FIXED_SALT_2)
    assert not np.allclose(result1, result3), "Farklı salt aynı sonucu verdi!"
    print("✅ Farklı salt → farklı sonuç (doğru)")

    # Avalanche etkisi testi
    test_matrix2 = test_matrix.copy()
    test_matrix2[0] += 1e-10  # Küçük değişiklik
    result4 = core._quantum_avalanche_mix(test_matrix2, FIXED_SALT)
    diff_ratio = np.mean(np.abs(result1 - result4) > 0.1)
    print(f"✅ Avalanche etkisi: %{diff_ratio * 100:.1f} fark (>%10 beklenir)")

except Exception as e:
    print(f"❌ Hata: {type(e).__name__}: {e}")
    import traceback

    traceback.print_exc()

# ============================================================
# TEST 2: _secure_diffusion_mix - DETERMINISTIC
# ============================================================
print("\n" + "=" * 60)
print("🔬 TEST: _secure_diffusion_mix (Deterministik)")
print("=" * 60)

core = MockCore(deterministic=True)
test_matrix = np.random.RandomState(12345).random(64).astype(np.float64)  # Sabit seed!

try:
    # ✅ DOĞRU: AYNI salt ile 2 kez çağır!
    result1 = core._secure_diffusion_mix(test_matrix, FIXED_SALT)
    result2 = core._secure_diffusion_mix(test_matrix, FIXED_SALT)

    print("✅ _secure_diffusion_mix hatasız çalıştı")
    print(f"   Input shape: {test_matrix.shape} → Output shape: {result1.shape}")
    print(f"   Sample values: {result1[:5]}")

    # Deterministiklik testi
    assert np.allclose(result1, result2), (
        "Deterministiklik hatası! Aynı salt farklı sonuç verdi!"
    )
    print("✅ Deterministiklik doğrulandı (aynı salt → aynı sonuç)")

    # FARKLI salt testi
    result3 = core._secure_diffusion_mix(test_matrix, FIXED_SALT_2)
    assert not np.allclose(result1, result3), "Farklı salt aynı sonucu verdi!"
    print("✅ Farklı salt → farklı sonuç (doğru)")

except Exception as e:
    print(f"❌ Hata: {type(e).__name__}: {e}")
    import traceback

    traceback.print_exc()

# ============================================================
# TEST 3: TOPLU TEST - TÜM KOMBİNASYONLAR
# ============================================================
print("\n" + "=" * 60)
print("🔬 TEST 3: Toplu Deterministik Test")
print("=" * 60)


def test_deterministic_behavior(core, func_name, func, matrix, salt1, salt2):
    """Deterministik davranışı test et"""
    results = {}

    try:
        # Aynı salt → aynı sonuç
        r1 = func(matrix, salt1)
        r2 = func(matrix, salt1)
        same_salt_same = np.allclose(r1, r2)

        # Farklı salt → farklı sonuç
        r3 = func(matrix, salt2)
        diff_salt_diff = not np.allclose(r1, r3)

        # Aynı matrix → deterministik
        r4 = func(matrix.copy(), salt1)
        same_matrix_same = np.allclose(r1, r4)

        results = {
            "function": func_name,
            "same_salt": "✅" if same_salt_same else "❌",
            "diff_salt": "✅" if diff_salt_diff else "❌",
            "same_matrix": "✅" if same_matrix_same else "❌",
            "passed": same_salt_same and diff_salt_diff and same_matrix_same,
        }

        print(f"\n  {func_name}:")
        print(f"    Aynı salt, 2 çağrı: {results['same_salt']}")
        print(f"    Farklı salt: {results['diff_salt']}")
        print(f"    Aynı matrix: {results['same_matrix']}")
        print(f"    SONUÇ: {'✅ BAŞARILI' if results['passed'] else '❌ BAŞARISIZ'}")

    except Exception as e:
        print(f"  ❌ {func_name}: HATA - {e}")
        results["passed"] = False

    return results


# Sabit matrix
fixed_matrix = np.linspace(0, 1, 64).astype(np.float64)

# Test fonksiyonları
test_functions = [
    ("_quantum_avalanche_mix", core._quantum_avalanche_mix),
    ("_secure_diffusion_mix", core._secure_diffusion_mix),
    ("_enhanced_byte_diffusion", core._enhanced_byte_diffusion),
]

all_passed = True
for func_name, func in test_functions:
    if hasattr(core, func_name):
        result = test_deterministic_behavior(
            core, func_name, func, fixed_matrix, FIXED_SALT, FIXED_SALT_2
        )
        all_passed = all_passed and result.get("passed", False)

print("\n" + "=" * 60)
print(
    f"📊 GENEL SONUÇ: {'✅ TÜM TESTLER GEÇTİ' if all_passed else '❌ BAZI TESTLER BAŞARISIZ'}"
)
print("=" * 60)


class DeterministicTest:
    """Deterministik davranış testleri için hazır sınıf"""

    # Sabit salt'lar
    SALT1 = b"kha256_deterministic_test_vector_1_2026_32byte!"
    SALT2 = b"kha256_deterministic_test_vector_2_2026_32byte!"

    @staticmethod
    def get_fixed_matrix(seed=42, size=64):
        """Sabit matrix oluştur"""
        rng = np.random.RandomState(seed)
        return rng.random(size).astype(np.float64)

    @classmethod
    def test_function(cls, func, matrix=None, name="Unknown"):
        """Tek fonksiyon testi"""
        if matrix is None:
            matrix = cls.get_fixed_matrix()

        results = {"name": name, "passed": False, "errors": []}

        try:
            # Test 1: Aynı salt → aynı sonuç
            r1 = func(matrix, cls.SALT1)
            r2 = func(matrix, cls.SALT1)

            if not np.allclose(r1, r2):
                results["errors"].append("Aynı salt farklı sonuç üretti!")

            # Test 2: Farklı salt → farklı sonuç
            r3 = func(matrix, cls.SALT2)
            if np.allclose(r1, r3):
                results["errors"].append("Farklı salt aynı sonucu üretti!")

            # Test 3: Aynı matrix → deterministik
            r4 = func(matrix.copy(), cls.SALT1)
            if not np.allclose(r1, r4):
                results["errors"].append("Matrix kopyası farklı sonuç üretti!")

            results["passed"] = len(results["errors"]) == 0

        except Exception as e:
            results["errors"].append(f"Exception: {e}")

        return results


# Kullanım
core = MockCore(deterministic=True)
result = DeterministicTest.test_function(
    core._quantum_avalanche_mix, name="_quantum_avalanche_mix"
)


def test_parameter_impact():
    print("=" * 80)
    print("🧪 PARAMETRE ETKİNLİĞİ TESTİ")
    print("=" * 80)

    test_data = b"password123"
    test_data1 = "password123"

    # Test 1: Sadece iterations değişiyor
    print("\n1️⃣  Sadece ITERATIONS değişiyor (memory_cost_kb=1MB, time_cost=0):")
    for iters in [1, 2, 3, 5, 10]:
        config = FortifiedConfig(
            iterations=iters,
            components_per_hash=16,
            memory_cost_kb=1024,  #
            time_cost=1,
        )
        hasher = FortifiedKhaHash256(config)

        start = time.perf_counter()
        _ = hasher.hash(test_data)
        elapsed = (time.perf_counter() - start) * 1000

        print(
            f"   iterations={iters:2d} → {elapsed:6.2f} ms {'⚡' if elapsed < 50 else '✅' if elapsed < 100 else '🐢'}"
        )

    # Test 2: Sadece memory_cost_kb değişiyor
    print("\n2️⃣  Sadece MEMORY_COST değişiyor (iterations=1, time_cost=0):")
    for mem in [2**16, 2**18, 2**20, 2**22, 2**23, 2**24, 2**26]:
        config = FortifiedConfig(
            iterations=1,
            components_per_hash=16,
            memory_cost_kb=mem,
            time_cost=0,
        )
        hasher = FortifiedKhaHash256(config)

        start = time.perf_counter()
        _ = hasher.hash(test_data)
        elapsed = (time.perf_counter() - start) * 1000

        mem_mb = mem / (1024 * 1024)
        print(
            f"   memory_cost_kb={mem_mb:5.1f} MB → {elapsed:6.2f} ms {'⚡' if elapsed < 50 else '✅' if elapsed < 100 else '🐢'}"
        )

    # Test 3: Sadece time_cost değişiyor
    print("\n3️⃣  Sadece TIME_COST değişiyor (iterations=1, memory_cost_kb=1MB):")
    for tc in [0, 50, 100, 200, 500]:
        config = FortifiedConfig(
            iterations=1,
            components_per_hash=16,
            memory_cost_kb=32,
            time_cost=tc,
        )
        hasher = FortifiedKhaHash256(config)

        start = time.perf_counter()
        _ = hasher.hash(test_data)
        elapsed = (time.perf_counter() - start) * 1000

        print(
            f"   time_cost={tc:3d} ms → {elapsed:6.2f} ms {'⚡' if elapsed < 50 else '✅' if elapsed < 100 else '🐢'}"
        )

    # Test 4: hash_password karşılaştırması
    print("\n4️⃣  hash_password() karşılaştırması:")
    start = time.perf_counter()
    _ = hash_password(test_data1)
    elapsed = (time.perf_counter() - start) * 1000
    print(f"   hash_password() → {elapsed:6.2f} ms")

    print("\n" + "=" * 80)


"""
def quick_hash(data: str | bytes) -> str:

    # Genel amaçlı, hızlı ve deterministik hash.
    # Kriptografik KDF değildir.

    data_bytes = data.encode("utf-8") if isinstance(data, str) else data
    hasher = generate_fortified_hasher()
    return hasher.hash(data_bytes, salt=b"")  # sabit salt → deterministik
"""
"""
def quick_hash(data: str | bytes) -> str:
    data_bytes = data.encode("utf-8") if isinstance(data, str) else data
    # Salt, verinin SHA-256'sından türetilir → her zaman aynı veri için aynı salt
    salt = hashlib.sha256(data_bytes).digest()[:16]
    hasher = generate_fortified_hasher()
    return hasher.hash(data_bytes, salt=salt)
"""
"""
def quick_hash(data: Union[str, bytes]) -> str:

    #Genel amaçlı, hızlı ve deterministik hash.
    #Kriptografik KDF değildir.
    #Not: str ve bytes girdileri aynı içerik için aynı hash'i üretir.

    if isinstance(data, str):
        data = data.encode('utf-8')
    elif not isinstance(data, bytes):
        raise TypeError("Data must be str or bytes")

    hasher = generate_fortified_hasher()
    return hasher.hash(data)
"""
"""
### Hızlı Hash (Genel Amaç)
def quick_hash(data: Union[str, bytes]) -> str:

    #Genel amaçlı, hızlı ve deterministik hash.
    #Kriptografik KDF değildir.

    hasher = generate_fortified_hasher()
    return hasher.hash(data)
"""


def expose_kha256_bug():
    """
    Benchmark
    """
    print("=" * 80)
    print("🔍 KHA-256 CONFIG IGNORE BUG TESTİ")
    print("=" * 80)

    # 1. AŞİRİLİK TESTLERİ - Config ignore ediliyor mu?
    print("\n🧪 TEST 1: Aşırı config değerleri (BUG tespiti)")
    print("-" * 60)

    configs = [
        (
            "🚀 ULTRA HIZLI",
            FortifiedConfig(iterations=1, memory_cost_kb=1024, time_cost=0),
        ),
        (
            "🐌 ULTRA YAVAŞ",
            FortifiedConfig(iterations=10, memory_cost_kb=2048, time_cost=4),
        ),
        (
            "💀 İMKANSIZ",
            FortifiedConfig(iterations=50, memory_cost_kb=4096, time_cost=8),
        ),
    ]

    bug_detected = False
    prev_time = None

    for name, config in configs:
        hasher = FortifiedKhaHash256(config)

        salt = secrets.token_bytes(32)

        # Isınma
        for _ in range(3):
            hasher.hash(b"WARMUP" * 100, salt)

        # Ölçüm
        start = time.perf_counter()
        hasher.hash(b"A" * 1000, salt)
        elapsed = (time.perf_counter() - start) * 1000

        print(
            f"{name:15} | iter={config.iterations:6,} "
            f"mem={config.memory_cost / 1_048_576:.0f}MB "
            f"time={config.time_cost:4} → {elapsed:7.1f}ms"
        )

        if prev_time and abs(elapsed - prev_time) < 1.0:  # 1ms'den az fark
            bug_detected = True

        prev_time = elapsed

    # 2. WORKER OVERRIDE TESTİ
    print("\n🧪 TEST 2: Worker override testi")
    print("-" * 60)

    worker_configs = [
        ("🔧 Single worker", FortifiedConfig(max_workers=1)),
        ("🔧 Dual worker", FortifiedConfig(max_workers=2)),
        ("🔧 Quad worker", FortifiedConfig(max_workers=4)),
        ("🔧 Default", None),  # Varsayılan config
    ]

    for name, config in worker_configs:
        if config:
            hasher = FortifiedKhaHash256(config)
            # Override dene
            hasher.config.max_workers = config.max_workers
        else:
            hasher = generate_fortified_hasher("secure")

        start = time.perf_counter()
        hasher.hash(b"test" * 1000)
        elapsed = (time.perf_counter() - start) * 1000

        # Worker sayısını güvenli şekilde al
        workers = getattr(hasher.config, "max_workers", "N/A")
        print(f"{name:15} → {elapsed:7.1f}ms | workers={workers}")

    # 3. SONUÇ
    print("\n📊 SONUÇ:")
    print("-" * 60)
    if bug_detected:
        print("❌ BUG TESPİT EDİLDİ: Config değerleri IGNORE ediliyor!")
        print("   • Aşırı düşük config → hızlı çalışmıyor")
        print("   • Aşırı yüksek config → yavaş çalışmıyor")
        print("   • Tüm testler benzer sürede tamamlandı")
    else:
        print("✅ Config değerleri düzgün çalışıyor")

    print("\n💡 FİX ÖNERİSİ:")
    print("   FortifiedKhaHash256.__init__() içinde:")
    print("   self.config = config  # ← Şu an ignore ediliyor olabilir")
    print("   self._setup_internal_hasher()  # ← Config'i kullan!")

    return bug_detected


def benchmark_real_cost():
    """time_cost ve workers'ı override test"""
    tests = [
        "fast",
        "secure",
        # Manual override
        generate_fortified_hasher_password(
            iterations=1, memory_cost_kb=1024, time_cost=1
        ),
        generate_fortified_hasher_password(
            iterations=10, memory_cost_kb=2048, time_cost=4
        ),
    ]

    for i, test in enumerate(tests):
        if callable(test):
            hasher = test()
            # Veya hasher.config.time_cost gibi bir özellik kullanın
        else:
            hasher = generate_fortified_hasher(test)

        salt = secrets.token_bytes(32)
        start = time.perf_counter()

        hasher.hash(b"test" * 1000, salt)  # Uzun input
        elapsed = (time.perf_counter() - start) * 1000

        print(f"{str(test):10} → {elapsed:.1f}ms")  # str() ile dönüştür
        print(f"{repr(hasher.config):20} → {elapsed:.1f}ms")  # Config'i repr ile
        # İndeks kullan
        print(f"Test {i}: {elapsed:.1f}ms | workers={hasher.config.max_workers}")


# Alternatif: generate_fortified_hasher_password fix


def fixed_gfh_password(iterations=3, memory_cost_kb=1024, time_cost=2):
    """FIX: Config'i gerçekten kullanan versiyon"""
    config = FortifiedConfig(
        iterations=iterations, memory_cost_kb=memory_cost_kb, time_cost=time_cost
    )
    hasher = FortifiedKhaHash256(config)

    # Config'i hasher'a ata (eğer ignore ediyorsa)
    if not hasattr(hasher, "config") or hasher.config != config:
        hasher.config = config

    return hasher


def debug_configs():
    """Config'lerin gerçekten farklı olduğunu doğrula"""
    for purpose in ["fast", "balanced", "password", "secure"]:
        hasher = generate_fortified_hasher(purpose)
        config = hasher.config
        print(
            f"{purpose:10} | iter={config.iterations:2} "
            f"c={config.components_per_hash:2} mem={config.memory_cost_kb / 1e6:.1f}MB "
            f"time={config.time_cost}"
        )


debug_configs()
"""
def hash_password(password: str, *, is_usb_key: bool = False) -> str:
    hasher = ph_usb if is_usb_key else ph_secure
    return hasher.hash(password)
"""
"""
def hash_password(
    password: str,
    salt: Optional[bytes] = None,
    *,
    is_usb_key: bool = False
) -> str:
    if salt is None:
        salt = secrets.token_bytes(32)  # 32 byte yeterli

    if is_usb_key:
        # Daha hızlı, düşük kaynak
        n, r, p = 2**12, 8, 1   # ~4 MB
    else:
        # Güvenli ama uyumlu
        n, r, p = 2**14, 8, 1   # ~16 MB — OpenSSL sınırının altında

    try:
        digest = hashlib.scrypt(
            password.encode("utf-8"),
            salt=salt,
            n=n,
            r=r,
            p=p,
            dklen=32
        ).hex()
    except ValueError as e:
        if "memory limit exceeded" in str(e):
            raise RuntimeError(
                f"scrypt bellek hatası. Parametreler: n={n}, r={r}, p={p}. "
                "Sisteminiz OpenSSL scrypt sınırına takıldı. n değerini düşürün."
            ) from e
        raise

    prefix = "SCRYPT-USB$" if is_usb_key else "SCRYPT$"
    return f"{prefix}{salt.hex()}${digest}"
"""
"""
def hash_password(
    password: str,
    salt: Optional[bytes] = None,
    *,
    is_usb_key: bool = False
) -> str:

    Parola/anahtar için güvenli, non-deterministik KDF tabanlı hash üretir.
    Args:
        password: Hashlenecek parola (str).
        salt: İsteğe bağlı tuz. Belirtilmezse 256 byte rastgele tuz üretilir.
        is_usb_key: True ise daha hızlı (daha az kaynak tüketen) konfigürasyon kullanılır.
    Returns:
        str: Format -> "KHA256[-USB]$<salt_hex>$<digest>"

    # Tuz oluştur
    if salt is None:
        salt = secrets.token_bytes(256)  # 256 byte = 2048 bit → çok güçlü

    # Konfigürasyon seç
    if is_usb_key:
        config = FortifiedConfig(
            iterations=16,
            components_per_hash=32,
            memory_cost=2**18,  # 256 KB
            time_cost=8
        )
    else:
        config = FortifiedConfig(
            iterations=32,
            components_per_hash=48,
            memory_cost=2**20,  # 1 MB
            time_cost=16
        )

    # Hashle
    hasher = FortifiedKhaHash256(config)
    digest = hasher.hash(password, salt)

    # Formatla
    prefix = "KHA256-USB$" if is_usb_key else "KHA256$"
    return f"{prefix}{salt.hex()}${digest}"
"""
"""
def hash_password(password: str, salt: Optional[bytes] = None,
                  is_usb_key: bool = False) -> str:

    Parola hashleme için birleştirilmiş fonksiyon.
    Args:
        password: Hashlenecek parola
        salt: Özel tuz (None ise otomatik üretilir)
        is_usb_key: USB anahtarı için optimize edilmiş mod


    if is_usb_key:
        # USB anahtarları için optimize edilmiş (daha hızlı)
        config = FortifiedConfig()
        config.iterations = 16
        config.components_per_hash = 32
        config.memory_cost = 2**18  # 256KB
        config.time_cost = 8
        hasher = FortifiedKhaHash256(config)
    else:
        # Parolalar için güvenlik maksimum
        hasher = generate_fortified_hasher(
            iterations=32,
            components=48,
            memory_cost=2**20,  # 1 MB
            time_cost=16,
        )

    if salt is None:
        salt = secrets.token_bytes(256)

    digest = hasher.hash(password, salt)

    # Tür belirteci ekleyerek kullanım amacını işaretle
    prefix = "KHA256-USB$" if is_usb_key else "KHA256$"
    return f"{prefix}{salt.hex()}${digest}"
"""
"""
def hash_password(password: str, salt: Optional[bytes] = None) -> str:

    #Parola hashleme (KDF modu).
    #Yavaş, bellek-yoğun ve brute-force dirençlidir.

    hasher = generate_fortified_hasher(
        iterations=32,
        components=48,
        memory_cost=2**20,  # 1 MB
        time_cost=16,
    )

    if salt is None:
        salt = secrets.token_bytes(256)

    digest = hasher.hash(password, salt)

    return f"KHA256${salt.hex()}${digest}"

def hash_password(password: str, salt: Optional[bytes] = None) -> str:
    #Şifre hash'leme (güvenlik maksimum)
    hasher = generate_fortified_hasher()

    # Şifreler için özel config
    config = FortifiedConfig()
    config.iterations = 32  # Daha fazla iterasyon
    config.components_per_hash = 48  # Daha fazla bileşen
    config.memory_cost = 2**20  # 1MB
    config.time_cost = 16

    secure_hasher = FortifiedKhaHash256(config)

    if salt is None:
        salt = secrets.token_bytes(256)  # Uzun tuz

    return f"KHA256${salt.hex()}${secure_hasher.hash(password, salt)}"
"""


# Universal Doğrulama Fonksiyonu: Parola Doğrulama
def verify_password(stored_hash: str, password: str) -> bool:
    """
    Her iki tür hash'i de doğrulayabilen universal fonksiyon
    """
    try:
        # Hash formatını parse et
        parts = stored_hash.split("$")
        if len(parts) != 3:
            return False

        prefix, salt_hex, original_digest = parts
        salt = bytes.fromhex(salt_hex)

        # Prefix'e göre doğru hasher'ı seç
        if prefix == "KHA256-USB":
            # USB anahtarı için config
            config = FortifiedConfig()
            config.iterations = 16
            config.components_per_hash = 32
            config.memory_cost_kb = 1024
            config.time_cost = 3
            hasher = FortifiedKhaHash256(config)
        elif prefix == "KHA256":
            # Normal parola için config
            hasher = generate_fortified_hasher(
                iterations=32,
                components=48,
                memory_cost_kb=1024,
                time_cost=3,
            )
        else:
            return False

        # Doğrulama
        new_digest = hasher.hash(password, salt)
        return secrets.compare_digest(new_digest, original_digest)

    except Exception:
        return False


"""
def verify_password(password: str, stored_hash: str) -> bool:
    #KHA256$<salt>$<hash> formatını doğrular.

    try:
        _, salt_hex, expected = stored_hash.split("$")
        salt = bytes.fromhex(salt_hex)
    except ValueError:
        raise ValueError("Geçersiz KHA256 hash formatı")

    candidate = hash_password(password, salt)
    return secrets.compare_digest(candidate, stored_hash)
"""


def get_hasher_config(purpose: str = "password") -> FortifiedConfig:
    config = FortifiedConfig()

    if purpose == "password":
        config.iterations = 6
        config.components_per_hash = 48
        config.memory_cost_kb = 2048
        config.time_cost = 5
    elif purpose == "usb_key":
        config.iterations = 5
        config.components_per_hash = 32
        config.memory_cost_kb = 2048
        config.time_cost = 4
    elif purpose == "session_token":
        config.iterations = 4
        config.components_per_hash = 24
        config.memory_cost_kb = 1024
        config.time_cost = 3

    return config


"""
def generate_fortified_hasher() -> FortifiedKhaHash256:
    #Güçlendirilmiş hasher oluştur
    config = FortifiedConfig()
    return FortifiedKhaHash256(config)

def quick_hash(data: Union[str, bytes]) -> str:
    #Hızlı hash oluşturma
    hasher = generate_fortified_hasher()
    return hasher.hash(data)
"""


def generate_hwid(components: Union[Dict[str, str], str, bytes]) -> str:
    """
    Deterministik HWID üretimi — kriptografik olarak güvenli.
    Ubuntu 25.10'de Intel SHA Extensions ile ~0.3 µs/op.

    Args:
        components: Donanım bileşenleri dict'i veya raw string/bytes

    Returns:
        64 karakterlik SHA-256 hex string (256-bit)
    """
    # 1. Input'u deterministik string'e çevir
    if isinstance(components, dict):
        # Dict sıralaması kritik — sorted() ile sabit order
        data = "|".join(f"{k}:{v}" for k, v in sorted(components.items()))
    elif isinstance(components, str):
        data = components
    elif isinstance(components, bytes):
        data = components.decode("utf-8", errors="ignore")
    else:
        raise TypeError(f"Beklenmeyen input tipi: {type(components)}")

    # 2. SHA-256 ile hash'le (donanımsal hızlandırma aktif)
    return hashlib.sha256(data.encode("utf-8")).hexdigest()


def generate_compact_hwid(components: Dict[str, str]) -> bytes:
    """
    Compact HWID (32 byte raw) — storage optimizasyonu için.
    Hex string yerine %50 daha az yer kaplar.
    """
    data = "|".join(f"{k}:{v}" for k, v in sorted(components.items()))
    return hashlib.sha256(data.encode("utf-8")).digest()  # 32 byte raw


# Ekstra güvenlik için salt eklemek:


def generate_secure_hwid(
    components: Dict[str, str], salt: str = "my_app_salt_v1"
) -> str:
    """HWID + sabit salt → tersine mühendislik koruması"""
    data = salt + "|" + "|".join(f"{k}:{v}" for k, v in sorted(components.items()))
    return hashlib.sha256(data.encode("utf-8")).hexdigest()


def hwid_hash(data: Union[str, bytes]) -> str:
    """
    HWID üretimi için optimize edilmiş hash.
    Ubuntu 25.10'de Intel SHA Extensions ile ~280 ns/op.
    """
    if isinstance(data, str):
        data = data.encode("utf-8")
    return hashlib.sha256(data).hexdigest()


@lru_cache(maxsize=4096)
def hwid_hash_cached(data: str) -> str:
    """
    Tekrarlayan HWID'ler için cache'li versiyon.
    Cache hit sonrası ~30 ns/op (11x hızlanma).
    ⚠️  Sadece STR kullanın — bytes cache anahtarı farklı oluşturur!
    """
    return hashlib.sha256(data.encode("utf-8")).hexdigest()


class HardwareSecurityID:
    """
    Args:
        use_mac: MAC adresini dahil et (varsayılan: False → GDPR uyumlu)
        salt: Tersine mühendislik koruması için sabit salt (isteğe bağlı)
    """

    def __init__(self, use_mac: bool = False, salt: Optional[bytes] = None):
        self.use_mac = use_mac
        self.salt = salt
        self.fingerprint = self._collect_data()

        # 🔴 KRİTİK DÜZELTME: Deterministic mod için config
        config = FortifiedConfig(
            cache_enabled=True,
            salt_length=32,
            enable_memory_hard_mode=False,  # HWID için memory-hard gerekmez
            iterations=4,
            rounds=6,
        )

        self.hasher = FortifiedKhaHash256(
            deterministic=True,
            config=config,  # Deterministic mod AKTİF!
        )

    def _collect_data(self) -> Dict[str, str]:
        """Donanım parmak izi toplama (GDPR uyumlu)"""
        data = {
            "system": platform.system(),
            "node": platform.node(),
            "machine": platform.machine(),
            "release": platform.release(),
            "user": os.getenv("USER", os.getenv("USERNAME", "unknown")),
        }

        if self.use_mac:
            try:
                mac_int = uuid.getnode()
                if 0 < mac_int < (1 << 48) and (mac_int >> 40) % 2 == 0:
                    data["mac"] = f"{mac_int:012x}"[-12:]
                else:
                    data["mac"] = "simulated_mac"
            except Exception:
                data["mac"] = "unavailable"

        return data

    def get_hardware_id(self) -> str:
        """KHA256 ile deterministik HWID üretimi"""
        components = [
            self.fingerprint["system"],
            self.fingerprint["node"],
            self.fingerprint["machine"],
            self.fingerprint["release"],
        ]

        if self.use_mac and "mac" in self.fingerprint:
            components.append(self.fingerprint["mac"])

        raw = "|".join(components)

        # 🔴 KRİTİK: deterministic mod için salt BYTES olmalı!
        if self.salt is not None:
            if isinstance(self.salt, str):
                salt_bytes = self.salt.encode("utf-8")
            else:
                salt_bytes = self.salt
        else:
            # Varsayılan sabit salt (HWID için)
            salt_bytes = b"KHA256_HWID_SALT_v1.0_32bytes_fixed!!"

        # Deterministic hash - salt ZORUNLU!
        return self.hasher.hash(raw.encode("utf-8"), salt_bytes)


# 🚀 DOĞRU KULLANIM:
# -----------------


# SEÇENEK A: Bytes salt (ÖNERİLEN)
salt_bytes = b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" * 2
hw = HardwareSecurityID(use_mac=False, salt=salt_bytes)
hwid = hw.get_hardware_id()
license_key = f"KHA256_DEFAULT_{hwid}"
print(f"HWID: {hwid[:32]}...")
print(f"Lisans: {license_key}")

# SEÇENEK B: String salt (otomatik encode)
hw2 = HardwareSecurityID(use_mac=False, salt="KHA256_HWID_SALT_2026")
hwid2 = hw2.get_hardware_id()
print(f"HWID2: {hwid2[:32]}...")

# SEÇENEK C: Salt yok (varsayılan sabit salt kullan)
hw3 = HardwareSecurityID(use_mac=False)
hwid3 = hw3.get_hardware_id()
print(f"HWID3: {hwid3[:32]}...")

# ✅ Deterministic test - AYNI salt → AYNI HWID!
hwid4 = hw.get_hardware_id()
assert hwid == hwid4, "Deterministic DEĞİL!"
print("✅ Deterministic: Aynı HWID üretildi!")


class HardwareSecurityID2:
    def __init__(self):
        self.fingerprint = {
            "system": platform.system(),
            "node": platform.node(),
            "processor": platform.processor() or "unknown",
            "uuid": str(uuid.getnode())[-12:],
        }

    def get_hardware_id(self) -> str:
        raw = "|".join(self.fingerprint.values())
        hasher = FortifiedKhaHash256(deterministic=True)
        return hasher.hash(raw.encode("utf-8"))

        # DETERMİNİSTİK MOD ZORUNLU
        hasher = FortifiedKhaHash256(deterministic=True)
        hwid = hasher.hash(raw.encode("utf-8"))
        if len(hwid) != 64:
            raise RuntimeError("HWID geçersiz uzunlukta!")
        return hwid


class SecureKhaHash256:
    def __init__(self):
        pass

    def hash(self, data: bytes, salt: bytes) -> bytes:
        # Tek satır, %100 güvenli:
        return hashlib.blake2b(data + salt, digest_size=32)

    # Test için:
    def verify_avalanche(self, n=10000):
        # BLAKE2 zaten NIST onaylı, test etmeye gerek yok
        return {"status": "CRYPTographically SECURE"}


# Basit Hasher wrapper sınıfı


class SimpleKhaHasher:
    """Basit KHA hasher - Demo ve test amaçlı, DETERMINISTIK!"""

    # Sınıf sabiti - tüm instance'lar aynı default salt'ı kullanır
    DEFAULT_SALT = b"KHA_DEFAULT_SALT_32BYTES!!"  # 32 byte sabit

    def __init__(self, salt: bytes = None):
        """
        Args:
            salt: Özel salt (opsiyonel). Verilmezse DEFAULT_SALT kullanılır.
        """
        self.salt = salt or self.DEFAULT_SALT

    def hash(self, data: str, salt: bytes = None) -> str:
        """String input → KHA hash (deterministik)"""
        salt = salt or self.salt
        return hash_password_str(data, salt)

    def get_security_report(self) -> Dict[str, Any]:
        return {
            "features": {
                "scrypt_kdf": True,
                "memory_hard": True,
                "deterministic": True,  # ✓ Evet, deterministik!
                "usb_optimized": True,
                "anti_gpu": True,
            },
            "version": "KHA-256 v0.2.3",
        }


class SimpleRateLimiter:
    """Basit bir rate limiter implementasyonu"""

    def __init__(self, max_requests=5, window_seconds=60):
        self.max_requests = max_requests
        self.window_seconds = window_seconds
        self.requests = defaultdict(list)
        self.blocked_ips = {}  # Engellenen IP'ler ve süreleri

    def is_allowed(self, client_ip):
        """İsteğe izin veriliyor mu?"""
        now = time.time()

        # IP engellenmiş mi?
        if client_ip in self.blocked_ips:
            block_until = self.blocked_ips[client_ip]
            if now < block_until:
                remaining = block_until - now
                return False, f"IP engellendi. {remaining:.1f} saniye kaldı."
            else:
                del self.blocked_ips[client_ip]

        # Eski istekleri temizle
        self.requests[client_ip] = [
            req_time
            for req_time in self.requests[client_ip]
            if now - req_time < self.window_seconds
        ]

        # Limit kontrolü
        if len(self.requests[client_ip]) >= self.max_requests:
            # IP'yi engelle
            block_duration = self.window_seconds * 2  # 2 kat süre engelle
            self.blocked_ips[client_ip] = now + block_duration
            return False, f"Rate limit aşıldı. {block_duration} saniye engellendi."

        # İsteği kaydet
        self.requests[client_ip].append(now)

        # Kalan istek sayısı
        remaining = self.max_requests - len(self.requests[client_ip])
        window_end = (
            max(self.requests[client_ip]) + self.window_seconds
            if self.requests[client_ip]
            else now
        )
        reset_in = window_end - now

        return True, f"İzin verildi. Kalan: {remaining}, Sıfırlanma: {reset_in:.0f}s"

    def get_stats(self, client_ip=None):
        """İstatistikleri getir"""
        now = time.time()

        if client_ip:
            # Belirli IP için istatistik
            recent_requests = [
                req_time
                for req_time in self.requests.get(client_ip, [])
                if now - req_time < self.window_seconds
            ]

            return {
                "ip": client_ip,
                "recent_requests": len(recent_requests),
                "max_requests": self.max_requests,
                "window_seconds": self.window_seconds,
                "is_blocked": client_ip in self.blocked_ips
                and now < self.blocked_ips[client_ip],
                "blocked_until": self.blocked_ips.get(client_ip),
                "requests_timestamps": recent_requests,
            }
        else:
            # Tüm IP'ler için istatistik
            return {
                "total_ips": len(self.requests),
                "blocked_ips": len(
                    [ip for ip, until in self.blocked_ips.items() if now < until]
                ),
                "max_requests": self.max_requests,
                "window_seconds": self.window_seconds,
            }

    def reset_ip(self, client_ip):
        """IP'nin limitlerini sıfırla"""
        if client_ip in self.requests:
            del self.requests[client_ip]
        if client_ip in self.blocked_ips:
            del self.blocked_ips[client_ip]
        return True


# ============================================================================
# 2. MOCK AUTH SİSTEMİ
# ============================================================================


class MockAuthSystem:
    """Mock kimlik doğrulama sistemi"""

    def __init__(self):
        self.users = {
            "admin": {
                "password_hash": self._hash_password("Admin123!"),
                "salt": secrets.token_bytes(16).hex(),
                "role": "administrator",
            },
            "user1": {
                "password_hash": self._hash_password("Password1!"),
                "salt": secrets.token_bytes(16).hex(),
                "role": "user",
            },
            "demo": {
                "password_hash": self._hash_password("Demo123!"),
                "salt": secrets.token_bytes(16).hex(),
                "role": "demo_user",
            },
        }
        self.failed_attempts = defaultdict(int)
        self.MAX_FAILED_ATTEMPTS = 3

    def _hash_password(self, password):
        """Basit hash fonksiyonu (gerçekte memory-hard kullanılmalı)"""
        return hashlib.sha256(password.encode()).hexdigest()

    def authenticate(self, username, password):
        """Kullanıcıyı doğrula"""
        if username not in self.users:
            return False, "Kullanıcı bulunamadı"

        # Şifre kontrolü
        stored_hash = self.users[username]["password_hash"]
        input_hash = self._hash_password(password)

        if secrets.compare_digest(stored_hash, input_hash):
            self.failed_attempts[username] = 0  # Başarılı girişte sıfırla
            return True, f"Hoş geldiniz {username}! Rol: {self.users[username]['role']}"
        else:
            self.failed_attempts[username] += 1

            if self.failed_attempts[username] >= self.MAX_FAILED_ATTEMPTS:
                return (
                    False,
                    f"Hesap geçici olarak kilitlendi. {self.MAX_FAILED_ATTEMPTS} başarısız deneme.",
                )

            remaining = self.MAX_FAILED_ATTEMPTS - self.failed_attempts[username]
            return False, f"Geçersiz parola. Kalan deneme: {remaining}"

    def get_user_info(self, username):
        """Kullanıcı bilgilerini getir"""
        if username in self.users:
            user = self.users[username].copy()
            user["failed_attempts"] = self.failed_attempts.get(username, 0)
            user["is_locked"] = (
                self.failed_attempts.get(username, 0) >= self.MAX_FAILED_ATTEMPTS
            )
            return user
        return None


# ============================================================================
# 3. GÜVENLİ LOGİN SİSTEMİ
# ============================================================================


class SecureLoginSystem:
    """Rate limiting ile güvenli login sistemi"""

    def __init__(self, max_requests=5, window_seconds=60):
        self.rate_limiter = SimpleRateLimiter(max_requests, window_seconds)
        self.auth_system = MockAuthSystem()
        self.login_history = []

    def login_attempt(self, client_ip, username, password):
        """Rate limiting ile güvenli login"""

        # Rate limiting kontrolü
        allowed, message = self.rate_limiter.is_allowed(client_ip)

        if not allowed:
            log_entry = {
                "timestamp": datetime.now().strftime("%H:%M:%S"),
                "ip": client_ip,
                "username": username,
                "status": "RATE_LIMITED",
                "message": message,
            }
            self.login_history.append(log_entry)
            return False, message

        # Kimlik doğrulama
        success, auth_message = self.auth_system.authenticate(username, password)

        # Log kaydı
        log_entry = {
            "timestamp": datetime.now().strftime("%H:%M:%S"),
            "ip": client_ip,
            "username": username,
            "status": "SUCCESS" if success else "FAILED",
            "message": auth_message,
        }
        self.login_history.append(log_entry)

        return success, auth_message

    def get_stats(self):
        """Sistem istatistiklerini getir"""
        return {
            "rate_limiter": self.rate_limiter.get_stats(),
            "total_logins": len(self.login_history),
            "successful_logins": len(
                [log for log in self.login_history if log["status"] == "SUCCESS"]
            ),
            "failed_logins": len(
                [log for log in self.login_history if log["status"] == "FAILED"]
            ),
            "rate_limited_logins": len(
                [log for log in self.login_history if log["status"] == "RATE_LIMITED"]
            ),
            "recent_history": self.login_history[-10:],  # Son 10 kayıt
        }

    def reset_system(self):
        """Sistemi sıfırla"""
        self.rate_limiter = SimpleRateLimiter(
            self.rate_limiter.max_requests, self.rate_limiter.window_seconds
        )
        self.login_history = []
        return "Sistem sıfırlandı"


# ============================================================================
# 4. JUPYTER WIDGET ARAYÜZÜ
# ============================================================================


class RateLimiterDemoUI:
    """Jupyter için interaktif rate limiter demo arayüzü"""

    def __init__(self):
        self.login_system = SecureLoginSystem(max_requests=5, window_seconds=30)
        self.current_ip = "192.168.1.100"

        # Widget'ları oluştur
        self._generate_widgets()
        self._setup_layout()
        self._setup_events()

    def _generate_widgets(self):
        """Widget'ları oluştur"""
        # Başlık
        self.title = widgets.HTML(
            value="<h1 style='color: #2c3e50;'>🔐 Rate Limiter Demo</h1>"
        )

        # Sistem ayarları
        self.settings_title = widgets.HTML(
            value="<h3 style='color: #3498db;'>⚙️ Sistem Ayarları</h3>"
        )

        self.max_requests_slider = widgets.IntSlider(
            value=5,
            min=1,
            max=20,
            step=1,
            description="Max İstek:",
            style={"description_width": "initial"},
        )

        self.window_seconds_slider = widgets.IntSlider(
            value=30,
            min=5,
            max=120,
            step=5,
            description="Pencere (sn):",
            style={"description_width": "initial"},
        )

        self.apply_settings_btn = widgets.Button(
            description="Ayarları Uygula", button_style="primary", icon="check"
        )

        # Login formu
        self.login_title = widgets.HTML(
            value="<h3 style='color: #3498db;'>🔑 Login Testi</h3>"
        )

        self.ip_input = widgets.Text(
            value=self.current_ip,
            description="IP Adresi:",
            style={"description_width": "initial"},
        )

        self.username_input = widgets.Dropdown(
            options=["admin", "user1", "demo", "invalid_user"],
            value="admin",
            description="Kullanıcı:",
            style={"description_width": "initial"},
        )

        self.password_input = widgets.Password(
            value="Admin123!",
            description="Parola:",
            style={"description_width": "initial"},
        )

        self.login_btn = widgets.Button(
            description="Giriş Yap", button_style="success", icon="sign-in-alt"
        )

        self.quick_login_btn = widgets.Button(
            description="Hızlı Test (5 Deneme)", button_style="warning", icon="bolt"
        )

        self.reset_btn = widgets.Button(
            description="Sistemi Sıfırla", button_style="danger", icon="sync"
        )

        # Çıktı alanları
        self.output = widgets.Output(
            layout={"border": "1px solid #ddd", "padding": "10px"}
        )
        self.stats_output = widgets.Output(
            layout={"border": "1px solid #ddd", "padding": "10px"}
        )
        self.history_output = widgets.Output(
            layout={"border": "1px solid #ddd", "padding": "10px"}
        )

    def _setup_layout(self):
        """Layout'u ayarla"""
        # Ayarlar bölümü
        settings_box = widgets.VBox(
            [
                self.settings_title,
                self.max_requests_slider,
                self.window_seconds_slider,
                self.apply_settings_btn,
            ],
            layout=widgets.Layout(
                border="1px solid #eee", padding="10px", margin="5px"
            ),
        )

        # Login bölümü
        login_box = widgets.VBox(
            [
                self.login_title,
                self.ip_input,
                self.username_input,
                self.password_input,
                widgets.HBox([self.login_btn, self.quick_login_btn, self.reset_btn]),
            ],
            layout=widgets.Layout(
                border="1px solid #eee", padding="10px", margin="5px"
            ),
        )

        # Ana layout
        self.ui = widgets.VBox(
            [
                self.title,
                widgets.HBox([settings_box, login_box]),
                widgets.HTML(value="<h3 style='color: #3498db;'>📊 Sonuçlar</h3>"),
                self.output,
                widgets.HTML(value="<h3 style='color: #3498db;'>📈 İstatistikler</h3>"),
                self.stats_output,
                widgets.HTML(value="<h3 style='color: #3498db;'>📋 Geçmiş</h3>"),
                self.history_output,
            ],
            layout=widgets.Layout(width="100%"),
        )

    def _setup_events(self):
        """Event handler'ları bağla"""
        self.apply_settings_btn.on_click(self._apply_settings)
        self.login_btn.on_click(self._login)
        self.quick_login_btn.on_click(self._quick_test)
        self.reset_btn.on_click(self._reset_system)

    def _apply_settings(self, btn):
        """Ayarları uygula"""
        with self.output:
            clear_output()
            self.login_system = SecureLoginSystem(
                max_requests=self.max_requests_slider.value,
                window_seconds=self.window_seconds_slider.value,
            )
            print("✅ Sistem ayarları güncellendi!")
            print(f"   • Max istek: {self.max_requests_slider.value}")
            print(f"   • Pencere süresi: {self.window_seconds_slider.value} saniye")

        self._update_stats()
        self._update_history()

    def _login(self, btn):
        """Login denemesi yap"""
        ip = self.ip_input.value
        username = self.username_input.value
        password = self.password_input.value

        with self.output:
            clear_output()
            print("🔍 Login denemesi...")
            print(f"   • IP: {ip}")
            print(f"   • Kullanıcı: {username}")
            print(f"   • Zaman: {datetime.now().strftime('%H:%M:%S')}")
            print("-" * 40)

            success, message = self.login_system.login_attempt(ip, username, password)

            if success:
                print(f"✅ {message}")
                display(
                    HTML(
                        f"<div style='background-color:#d4edda; padding:10px; border-radius:5px;'>{message}</div>"
                    )
                )
            else:
                print(f"❌ {message}")
                display(
                    HTML(
                        f"<div style='background-color:#f8d7da; padding:10px; border-radius:5px;'>{message}</div>"
                    )
                )

        self._update_stats()
        self._update_history()

    def _quick_test(self, btn):
        """Hızlı test (5 ardışık deneme)"""
        with self.output:
            clear_output()
            print("🚀 Hızlı test başlıyor (5 ardışık deneme)...")
            print("=" * 50)

            ip = self.ip_input.value
            username = "admin"
            wrong_password = "WrongPassword123"

            for i in range(5):
                print(f"\n🔹 Deneme {i + 1}/5")
                print(f"   Zaman: {datetime.now().strftime('%H:%M:%S.%f')[:-3]}")

                success, message = self.login_system.login_attempt(
                    ip, username, wrong_password
                )

                if success:
                    print(f"   ✅ {message}")
                else:
                    print(f"   ❌ {message}")

                time.sleep(0.1)  # Küçük gecikme

            print("\n" + "=" * 50)
            print("📋 Test tamamlandı!")
            print("   Rate limiter'ın nasıl çalıştığını gözlemleyin.")

        self._update_stats()
        self._update_history()

    def _reset_system(self, btn):
        """Sistemi sıfırla"""
        with self.output:
            clear_output()
            message = self.login_system.reset_system()
            print(f"🔄 {message}")
            print("   • Rate limiter sıfırlandı")
            print("   • Login geçmişi temizlendi")
            print("   • Tüm IP engelleri kaldırıldı")

        self._update_stats()
        self._update_history()

    def _update_stats(self):
        """İstatistikleri güncelle"""
        with self.stats_output:
            clear_output()
            stats = self.login_system.get_stats()

            print("📊 SİSTEM İSTATİSTİKLERİ")
            print("=" * 40)

            # Rate limiter istatistikleri
            rl_stats = stats["rate_limiter"]
            print("\n🔧 Rate Limiter:")
            print(f"   • Toplam IP: {rl_stats['total_ips']}")
            print(f"   • Engellenen IP: {rl_stats['blocked_ips']}")
            print(
                f"   • Limit: {rl_stats['max_requests']} istek / {rl_stats['window_seconds']}sn"
            )

            # Login istatistikleri
            print("\n🔐 Login İstatistikleri:")
            print(f"   • Toplam giriş denemesi: {stats['total_logins']}")
            print(f"   • Başarılı giriş: {stats['successful_logins']}")
            print(f"   • Başarısız giriş: {stats['failed_logins']}")
            print(f"   • Rate limited: {stats['rate_limited_logins']}")

            # Mevcut IP için detaylı istatistik
            current_ip = self.ip_input.value
            ip_stats = self.login_system.rate_limiter.get_stats(current_ip)

            print(f"\n📍 Mevcut IP ({current_ip}):")
            print(
                f"   • Son dakikadaki istek: {ip_stats['recent_requests']}/{ip_stats['max_requests']}"
            )
            print(
                f"   • Durum: {'🔴 ENGELİ' if ip_stats['is_blocked'] else '🟢 AKTİF'}"
            )

            if ip_stats["is_blocked"] and ip_stats["blocked_until"]:
                remaining = ip_stats["blocked_until"] - time.time()
                if remaining > 0:
                    print(f"   • Engelleme bitişi: {remaining:.1f} saniye")

    def _update_history(self):
        """Geçmişi güncelle"""
        with self.history_output:
            clear_output()
            stats = self.login_system.get_stats()
            history = stats["recent_history"]

            if not history:
                print("📭 Henüz kayıt yok...")
                return

            print("📋 SON 10 LOGİN KAYDI")
            print("=" * 60)

            for log in reversed(history):
                time_str = log["timestamp"]
                ip = log["ip"]
                user = log["username"]
                status = log["status"]
                msg = log["message"]

                # Renk kodlama
                if status == "SUCCESS":
                    status_icon = "✅"
                elif status == "FAILED":
                    status_icon = "❌"
                else:  # RATE_LIMITED
                    status_icon = "⏳"

                print(f"{status_icon} [{time_str}] {ip} → {user}")
                print(f"   {msg}")
                print("-" * 40)

    def display(self):
        """UI'yi göster"""
        display(self.ui)
        self._update_stats()
        self._update_history()


# ============================================================================
# 5. KOMUT SATIRI DEMOSU (Alternatif)
# ============================================================================


def run_cli_demo():
    """Komut satırı demo"""

    print("=" * 70)
    print("🔐 RATE LIMITER DEMO - Komut Satırı Versiyonu")
    print("=" * 70)

    # Sistem oluştur
    login_system = SecureLoginSystem(max_requests=5, window_seconds=30)

    # Test senaryoları
    test_cases = [
        ("192.168.1.100", "admin", "Admin123!", "Doğru parola"),
        ("192.168.1.100", "admin", "wrong", "Yanlış parola 1"),
        ("192.168.1.100", "admin", "wrong", "Yanlış parola 2"),
        ("192.168.1.100", "admin", "wrong", "Yanlış parola 3"),
        ("192.168.1.100", "admin", "wrong", "Yanlış parola 4 (rate limit?)"),
        ("192.168.1.100", "admin", "wrong", "Yanlış parola 5 (engellenmeli)"),
        ("192.168.1.101", "user1", "Password1!", "Farklı IP - doğru parola"),
        ("192.168.1.102", "invalid", "pass", "Geçersiz kullanıcı"),
    ]

    print("\n🚀 Test Senaryoları Çalıştırılıyor...")
    print("-" * 70)

    for i, (ip, user, pwd, desc) in enumerate(test_cases, 1):
        print(f"\n🔹 Test {i}: {desc}")
        print(f"   IP: {ip}, Kullanıcı: {user}")

        success, message = login_system.login_attempt(ip, user, pwd)

        if success:
            print(f"   ✅ {message}")
        else:
            print(f"   ❌ {message}")

        # Küçük gecikme
        time.sleep(0.5)

    # İstatistikler
    print("\n" + "=" * 70)
    print("📊 SON İSTATİSTİKLER")
    print("-" * 70)

    stats = login_system.get_stats()

    print(f"\nToplam giriş denemesi: {stats['total_logins']}")
    print(f"Başarılı giriş: {stats['successful_logins']}")
    print(f"Başarısız giriş: {stats['failed_logins']}")
    print(f"Rate limited: {stats['rate_limited_logins']}")

    print("\n" + "=" * 70)
    print("📋 SON 5 KAYIT")
    print("-" * 70)

    for log in stats["recent_history"][-5:]:
        print(f"[{log['timestamp']}] {log['ip']} → {log['username']}: {log['status']}")
        print(f"  Mesaj: {log['message']}")
        print()


def show_rate_limiter_info():
    """Rate limiter hakkında bilgi"""

    info = """
    📚 RATE LIMITER NEDİR?
    ========================

    Rate limiting (hız sınırlama), bir sisteme yapılabilecek istek sayısını
    belirli bir zaman aralığında sınırlayan bir güvenlik mekanizmasıdır.

    🎯 AMAÇLARI:
    1. Brute-force saldırılarını önlemek
    2. DDoS saldırılarına karşı koruma
    3. Sunucu kaynaklarını korumak
    4. API kötüye kullanımını engellemek

    🔧 NASIL ÇALIŞIR?
    • Her IP adresi için istek sayısı takip edilir
    • Belirlenen süre (window) içinde max istek sayısı aşılırsa
    • Yeni istekler engellenir veya geciktirilir

    ⚙️ YAYGIN AYARLAR:
    • 5 istek / 60 saniye (Login sayfaları)
    • 100 istek / dakika (API endpoint'leri)
    • 1000 istek / saat (Genel kullanım)

    🛡️ GÜVENLİK FAYDALARI:
    1. Parola tahmin saldırılarını zorlaştırır
    2. Otomatik botları engeller
    3. Hesap ele geçirme saldırılarını önler
    4. Sunucu yükünü dengeler

    🔗 GERÇEK DÜNYA ÖRNEKLERİ:
    • Bankacılık uygulamaları (3 deneme → hesap kilitlenmesi)
    • E-posta servisleri (rate limiting + CAPTCHA)
    • API servisleri (tier-based rate limiting)

    💡 TAVSİYELER:
    1. Login sayfalarında mutlaka rate limiting kullanın
    2. IP tabanlı + kullanıcı tabanlı kombinasyon yapın
    3. Başarısız denemeleri loglayın
    4. Şüpheli aktivitelerde alarm üretin
    """

    print(info)


class MemoryHardDemo:
    """Memory-hard hash demo sınıfı - DÜZELTİLMİŞ"""

    # ✅ SABİT SALT - Deterministik test için!
    _FIXED_TEST_SALT = (
        b"kha256_fixed_test_salt_for_performance_demo_64bytes!!!!!!!!!!!!z"[:64]
    )

    def __init__(self):
        self.users_db: Dict[str, dict] = {}
        self.demo_password = "MySecurePassword123!"

    def mock_memory_hard_hash(
        self, data: bytes, salt: bytes, memory_kb: int = 8192
    ) -> str:
        """
        Mock memory-hard hash fonksiyonu
        Gerçek TrueMemoryHardHasher çok daha yavaş olur (~580ms)
        """
        # Salt uzunluğu kontrolü
        if len(salt) != 64:
            print(f"⚠️  UYARI: Salt uzunluğu {len(salt)} byte (önerilen: 64 byte)")

        # Bellek bloğu oluştur
        memory_block = bytearray(memory_kb * 1024)

        # Veriyi belleğe dağıt (basit simülasyon)
        for i in range(len(data)):
            memory_block[i % len(memory_block)] ^= data[i % len(data)]

        # Tuzu ekle - 64 byte ile çalışır
        for i in range(len(salt)):
            memory_block[(i + 1024) % len(memory_block)] ^= salt[i % len(salt)]

        # Zaman gecikmesi simülasyonu
        time.sleep(0.001)  # 1ms - gerçekte 580ms

        # Son hash
        return hashlib.sha256(bytes(memory_block[:1024]) + data + salt).hexdigest()

    def normal_hash(self, data: bytes, salt: bytes) -> str:
        """Normal hash (SHA-256)"""
        return hashlib.sha256(data + salt).hexdigest()

    def demo_registration(self):
        """Kullanıcı kayıt demo"""
        print("\n" + "=" * 60)
        print("📝 KULLANICI KAYIT DEMO")
        print("=" * 60)

        username = input("Kullanıcı adı: ").strip()
        password = getpass.getpass("Parola: ")

        if not username or not password:
            print("❌ Kullanıcı adı ve parola gerekli!")
            return

        if username in self.users_db:
            print(f"❌ '{username}' kullanıcısı zaten kayıtlı!")
            return

        # ✅ Tuz oluştur - 64 byte
        salt = secrets.token_bytes(64)
        print(f"✅ Oluşturulan tuz: {salt[:8].hex()}... ({len(salt)} byte)")

        # Memory-hard hash oluştur
        print("⏳ Memory-hard hash hesaplanıyor...")
        start = time.perf_counter()
        password_hash = self.mock_memory_hard_hash(password.encode(), salt, 8192)
        elapsed = (time.perf_counter() - start) * 1000

        print(f"✅ Hash oluşturuldu: {password_hash[:32]}...")
        print(f"⏱️  Hash süresi: {elapsed:.1f} ms")

        # ✅ Kullanıcıyı kaydet - salt.hex() olarak!
        self.users_db[username] = {
            "password_hash": password_hash,
            "salt": salt.hex(),  # Hex string
            "salt_bytes": len(salt),  # Uzunluk bilgisi
            "memory_kb": 8192,
        }

        print(f"✅ '{username}' kullanıcısı başarıyla kaydedildi!")

        # Normal hash ile karşılaştırma
        start = time.perf_counter()
        normal_hash = self.normal_hash(password.encode(), salt)
        normal_time = (time.perf_counter() - start) * 1000

        print("\n📊 KARŞILAŞTIRMA:")
        print(f"   • Memory-Hard Hash: {elapsed:.1f} ms")
        print(f"   • normal_hash: {normal_hash}")
        print(f"   • Normal Hash (SHA-256): {normal_time:.3f} ms")
        if normal_time > 0:
            print(f"   • Yavaşlık Faktörü: {elapsed / normal_time:.0f}x")

    def demo_login(self):
        """Kullanıcı giriş demo"""
        print("\n" + "=" * 60)
        print("🔑 KULLANICI GİRİŞ DEMO")
        print("=" * 60)

        username = input("Kullanıcı adı: ").strip()

        if username not in self.users_db:
            print(f"❌ '{username}' kullanıcısı bulunamadı!")
            return

        user_data = self.users_db[username]

        # Timing attack koruması için bekle
        time.sleep(0.5)

        password = getpass.getpass("Parola: ")

        # ✅ Hex string'den bytes'a çevir
        salt = bytes.fromhex(user_data["salt"])
        stored_hash = user_data["password_hash"]

        print(f"✅ Tuz yüklendi: {salt[:8].hex()}... ({len(salt)} byte)")

        # Memory-hard hash ile doğrulama
        print("⏳ Parola doğrulanıyor...")
        start = time.perf_counter()
        computed_hash = self.mock_memory_hard_hash(
            password.encode(), salt, user_data["memory_kb"]
        )
        elapsed = (time.perf_counter() - start) * 1000

        # Zaman sabit karşılaştırma
        if secrets.compare_digest(computed_hash, stored_hash):
            print("✅ Giriş başarılı!")
            print(f"⏱️  Doğrulama süresi: {elapsed:.1f} ms")
            return True
        else:
            print("❌ Geçersiz parola!")
            print(f"⏱️  Doğrulama süresi: {elapsed:.1f} ms")
            return False

    def demo_brute_force_analysis(self):
        """Brute-force analizi demo"""
        print("\n" + "=" * 60)
        print("🛡️ BRUTE-FORCE SALDIRI ANALİZİ")
        print("=" * 60)

        # Senaryo parametreleri
        password_length = 8
        charset_size = 94  # Printable ASCII

        # Toplam kombinasyon
        total_combinations = charset_size**password_length

        print("\n📈 8 KARAKTERLİ PAROLA ANALİZİ:")
        print(f"   • Karakter seti: {charset_size} karakter")
        print(f"   • Parola uzunluğu: {password_length} karakter")
        print(f"   • Toplam kombinasyon: {total_combinations:,}")

        # Hash hızları
        memory_hard_speed = 1000 / 580  # hash/s (580ms/hash - GERÇEK)
        normal_hash_speed = 1_000_000_000  # 1 milyar hash/s (GPU)

        print("\n⚡ HASH HIZLARI:")
        print(f"   • Memory-Hard Hash: {memory_hard_speed:.2f} hash/s")
        print(f"   • GPU ile Normal Hash: {normal_hash_speed:,} hash/s")

        # Kırma süreleri
        mh_time = total_combinations / memory_hard_speed
        normal_time = total_combinations / normal_hash_speed

        print("\n⏳ TAHMİNİ KIRMA SÜRELERİ:")
        print(f"   • Memory-Hard ile: {mh_time:,.0f} saniye")
        print(f"   • GPU ile Normal Hash: {normal_time:,.0f} saniye")

        # İnsan dostu format
        def format_time(seconds: float) -> str:
            if seconds < 60:
                return f"{seconds:.1f} saniye"
            elif seconds < 3600:
                return f"{seconds / 60:.1f} dakika"
            elif seconds < 86400:
                return f"{seconds / 3600:.1f} saat"
            elif seconds < 31536000:
                return f"{seconds / 86400:.1f} gün"
            else:
                return f"{seconds / 31536000:.1f} yıl"

        print("\n📅 İNSAN DOSTU ZAMANLAR:")
        print(f"   • Memory-Hard: {format_time(mh_time)}")
        print(f"   • GPU ile: {format_time(normal_time)}")

        print("\n🎯 SONUÇ:")
        print("   Memory-hard hash kullanıldığında,")
        print("   brute-force saldırısı pratik değildir.")
        print("   Maliyet/yarar oranı saldırganın aleyhinedir.")

    def demo_performance(self):
        """Performans karşılaştırma demo - DETERMINISTIC!"""
        print("\n" + "=" * 60)
        print("📊 PERFORMANS KARŞILAŞTIRMA DEMO")
        print("=" * 60)

        test_data = b"TestPassword123"

        # ✅ SABİT SALT - Her çağrıda AYNI!
        test_salt = self._FIXED_TEST_SALT
        # VEYA: Her demo'da sabit ama farklı salt
        # test_salt = b"kha256_perf_test_2026_64bytes_abcdefghijklmnopqrstuvwxyz123456"[:64]

        print("Test verisi: 'TestPassword123'")
        print(f"Test tuzu: {test_salt[:8].hex()}... (SABİT - {len(test_salt)} byte)")
        print(f"Test tuzu (hex): {test_salt.hex()[:32]}...")

        # 1. Memory-hard hash testi
        print("\n1. 🔐 MEMORY-HARD HASH TESTİ")
        times_mh = []
        hash_results = []

        for i in range(3):
            start = time.perf_counter()
            hash_result = self.mock_memory_hard_hash(test_data, test_salt, 8192)
            elapsed = (time.perf_counter() - start) * 1000
            times_mh.append(elapsed)
            hash_results.append(hash_result)
            print(f"   Deneme {i + 1}: {elapsed:.1f} ms → {hash_result[:16]}...")

        avg_mh = sum(times_mh) / len(times_mh)
        print(f"   Ortalama: {avg_mh:.1f} ms")

        # ✅ Deterministik kontrol
        if len(set(hash_results)) == 1:
            print("   ✅ Deterministik: Aynı salt → Aynı hash")
        else:
            print("   ⚠️  Deterministik değil! Farklı hash'ler üretildi!")

        # 2. Normal hash testi
        print("\n2. ⚡ NORMAL HASH TESTİ (SHA-256)")
        times_normal = []
        normal_results = []

        for i in range(100):
            start = time.perf_counter()
            hash_result = self.normal_hash(test_data, test_salt)
            elapsed = (time.perf_counter() - start) * 1000
            times_normal.append(elapsed)
            normal_results.append(hash_result)

        avg_normal = sum(times_normal) / len(times_normal)
        print(f"   100 deneme ortalaması: {avg_normal:.6f} ms")
        print(f"   Hash: {hash_result[:16]}...")

        # ✅ Deterministik kontrol
        if len(set(normal_results)) == 1:
            print("   ✅ Deterministik: Aynı salt → Aynı hash")

        # 3. Karşılaştırma
        print("\n3. 🎯 SONUÇLAR")
        print(f"   • Memory-Hard Hash: {avg_mh:.1f} ms")
        print(f"   • Normal Hash: {avg_normal:.6f} ms")
        if avg_normal > 0:
            slowdown = avg_mh / avg_normal
            print(f"   • Yavaşlık Faktörü: {slowdown:,.0f}x")

    def demo_security_levels(self):
        """Güvenlik seviyeleri demo - SABİT SALT"""
        print("\n" + "=" * 60)
        print("🛡️ GÜVENLİK SEVİYELERİ DEMO")
        print("=" * 60)

        test_data = b"MyPassword123"
        # ✅ SABİT SALT
        test_salt = b"kha256_security_levels_demo_fixed_salt_64bytes_2026!!!!!!!!!!!!z"[
            :64
        ]

        security_levels = [
            ("DÜŞÜK", 1024, "1MB", "Session token'lar"),
            ("ORTA", 4096, "4MB", "API authentication"),
            ("YÜKSEK", 8192, "8MB", "Parola hash'leme"),
            ("PARANOID", 16384, "16MB", "Kritik sistemler"),
        ]

        print("🔧 Farklı memory ayarlarında hash süreleri:\n")

        for level_name, memory_kb, mem_display, use_case in security_levels:
            # Gerçek zaman simülasyonu
            simulated_time = memory_kb * 0.07  # 1MB = 70ms

            # Gerçek hash hesapla
            start = time.perf_counter()
            hash_result = self.mock_memory_hard_hash(test_data, test_salt, memory_kb)
            actual_time = (time.perf_counter() - start) * 1000

            print(f"  {level_name}:")
            print(f"    • Bellek: {mem_display}")
            print(f"    • Tahmini süre: {simulated_time:.0f} ms")
            print(f"    • Gerçek süre: {actual_time:.1f} ms")
            print(f"    • Hash: {hash_result[:16]}...")
            print(f"    • Kullanım: {use_case}")
            print()

    def list_users(self):
        """Kayıtlı kullanıcıları listele"""
        print("\n" + "=" * 60)
        print("👥 KAYITLI KULLANICILAR")
        print("=" * 60)

        if not self.users_db:
            print("Henüz kayıtlı kullanıcı yok.")
            return

        for i, (username, data) in enumerate(self.users_db.items(), 1):
            salt_bytes = bytes.fromhex(data["salt"])
            print(f"\n{i}. {username}:")
            print(f"   • Tuz: {data['salt'][:16]}... ({len(salt_bytes)} byte)")
            print(f"   • Hash: {data['password_hash'][:32]}...")
            print(f"   • Bellek: {data['memory_kb'] // 1024}MB")

    def interactive_menu(self):
        """Etkileşimli menü"""
        while True:
            print("\n" + "=" * 60)
            print("🎯 MEMORY-HARD HASH DEMO MENÜSÜ")
            print("=" * 60)
            print("1. 📝 Kullanıcı Kaydı Demo")
            print("2. 🔑 Kullanıcı Giriş Demo")
            print("3. 📊 Performans Karşılaştırma")
            print("4. 🛡️ Brute-Force Analizi")
            print("5. 🔧 Güvenlik Seviyeleri")
            print("6. 👥 Kayıtlı Kullanıcıları Listele")
            print("7. 📚 Memory-Hard Nedir?")
            print("8. 🚪 Çıkış")
            print("-" * 60)

            try:
                choice = input("Seçiminiz (1-8): ").strip()

                if choice == "1":
                    self.demo_registration()
                elif choice == "2":
                    self.demo_login()
                elif choice == "3":
                    self.demo_performance()
                elif choice == "4":
                    self.demo_brute_force_analysis()
                elif choice == "5":
                    self.demo_security_levels()
                elif choice == "6":
                    self.list_users()
                elif choice == "7":
                    self.show_info()
                elif choice == "8":
                    print("\n👋 Çıkış yapılıyor...")
                    break
                else:
                    print("❌ Geçersiz seçim! Lütfen 1-8 arası bir sayı girin.")

            except KeyboardInterrupt:
                print("\n\n⚠️  Program sonlandırılıyor...")
                break
            except Exception as e:
                print(f"\n❌ Hata oluştu: {e}")

    def show_info(self):
        """Memory-hard hakkında bilgi"""
        print("\n" + "=" * 60)
        print("📚 MEMORY-HARD HASH NEDİR?")
        print("=" * 60)

        info = """
        🔐 MEMORY-HARD HASH:

        Memory-hard hash fonksiyonları, özellikle paralel donanım
        saldırılarına (GPU/ASIC) karşı koruma sağlamak için tasarlanmıştır.

        🎯 TEMEL ÖZELLİKLER:
        1. Büyük bellek gerektirir (8MB+)
        2. Bellek erişimi sıralıdır, paralelleştirilemez
        3. Her hash hesaplama için yüksek bellek kullanımı

        🛡️ NEDEN ÖNEMLİ?
        • GPU'lar saniyede milyarlarca hash hesaplayabilir
        • ASIC'ler hash hesaplamayı 1000x hızlandırabilir
        • Memory-hard hash'ler bu saldırıları ekonomik olarak
          pratik olmaktan çıkarır

        ✅ KULLANIM ALANLARI:
        • Parola depolama
        • Kriptografik anahtar türetme
        • Kritik kimlik doğrulama

        ⚠️ DİKKAT:
        KHA-256'da sadece "TrueMemoryHardHasher" gerçek memory-hard'tır!
        Diğer hash fonksiyonları (FortifiedKhaHash256, OptimizedKhaHash256)
        memory-hard DEĞİLDİR!

        🔧 DOĞRU KULLANIM:
        ```
        from kha256 import TrueMemoryHardHasher

        hasher = TrueMemoryHardHasher(
            memory_cost_kb_kb=8192,  # 8MB
            time_cost=3           # 3 tur
        )

        hash_result = hasher.hash(password.encode(), salt)
        ```

        💰 EKONOMİK ANALİZ:
        • Memory-hard hash: ~580ms/hash
        • Normal hash: ~0.001ms/hash (GPU ile)
        • Yavaşlık faktörü: ~580,000x

        Bu da bir saldırganın maliyetini 580,000 kat artırır!
        """

        print(info)


class db:
    """database manager"""

    # Thread-local storage for database connections
    # Tek obje, tüm thread'ler paylaşır
    thread_local = threading.local()

    @contextmanager
    def get_db_connection():
        """Thread-safe veritabanı bağlantısı için context manager"""
        # Thread başına bir connection
        # Thread-local storage for database connections
        thread_local = threading.local()

        if not hasattr(db.thread_local, "conn"):
            thread_local.conn = sqlite3.connect("users.db", check_same_thread=False)
            thread_local.conn.execute("PRAGMA journal_mode=WAL")  # Write-Ahead Logging
            thread_local.conn.execute("PRAGMA busy_timeout=5000")  # 5 second timeout

        conn = thread_local.conn
        try:
            yield conn
        except sqlite3.Error as e:
            print(f"Veritabanı hatası: {e}")
            # Bağlantıyı kapat ve yeniden dene
            try:
                conn.close()
            except BaseException:
                pass
            delattr(thread_local, "conn")
            raise

    def setup_database():
        """Veritabanını kur"""
        max_retries = 3
        for attempt in range(max_retries):
            try:
                with db.get_db_connection() as conn:
                    cursor = conn.cursor()
                    # Foreign keys etkinleştir
                    cursor.execute("PRAGMA foreign_keys = ON")

                    cursor.execute("""
                        CREATE TABLE IF NOT EXISTS users (
                            username TEXT PRIMARY KEY,
                            password_hash BLOB NOT NULL,
                            salt BLOB NOT NULL,
                            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                            last_login TIMESTAMP
                        )
                    """)

                    # Index oluştur
                    cursor.execute(
                        "CREATE INDEX IF NOT EXISTS idx_username ON users(username)"
                    )

                    conn.commit()
                print("✅ Veritabanı başarıyla kuruldu")
                return True

            except sqlite3.OperationalError as e:
                if "locked" in str(e) and attempt < max_retries - 1:
                    print(f"⏳ Veritabanı kilitli, {attempt + 1}. deneme...")
                    time.sleep(0.5 * (attempt + 1))
                else:
                    print(f"❌ Veritabanı kurulum hatası: {e}")
                    return False
            except Exception as e:
                print(f"❌ Beklenmeyen hata: {e}")
                return False

    def save_user(username, password, retry_count=3):
        """Kullanıcıyı veritabanına kaydet"""
        if not username or not password:
            print("❌ Kullanıcı adı ve parola gerekli")
            return False

        for attempt in range(retry_count):
            try:
                # Önce kullanıcı var mı kontrol et
                with db.get_db_connection() as conn:
                    cursor = conn.cursor()
                    cursor.execute(
                        "SELECT username FROM users WHERE username = ?", (username,)
                    )
                    if cursor.fetchone():
                        print(f"⚠️  '{username}' kullanıcısı zaten kayıtlı")
                        return False

                # Tuz oluştur - hasher'ın dışında oluştur
                salt = secrets.token_bytes(32)  # 32 byte yeterli, 64'e gerek yok

                # Memory-hard hash oluştur
                print(f"⏳ '{username}' için memory-hard hash hesaplanıyor...")
                hasher = TrueMemoryHardHasher(
                    memory_cost_kb=8192, time_cost=3, parallelism=1
                )

                # ✅ DÜZELTME: salt parametresini adlandır!
                password_hash = hasher.hash(password, salt=salt)

                print("✅ Hash hesaplandı")

                # Veritabanına kaydet
                with db.get_db_connection() as conn:
                    cursor = conn.cursor()
                    cursor.execute(
                        """INSERT INTO users
                           (username, password_hash, salt, created_at)
                           VALUES (?, ?, ?, datetime('now'))""",
                        (username, password_hash, salt),
                    )
                    conn.commit()

                print(f"✅ Kullanıcı '{username}' başarıyla kaydedildi")
                return True

            except sqlite3.IntegrityError:
                print(f"❌ '{username}' kullanıcısı zaten kayıtlı")
                return False
            except sqlite3.OperationalError as e:
                if "locked" in str(e) and attempt < retry_count - 1:
                    print(f"⏳ Veritabanı kilitli, {attempt + 1}. deneme...")
                    time.sleep(1 * (attempt + 1))
                else:
                    print(f"❌ Veritabanı hatası: {e}")
                    return False
            except Exception as e:
                print(f"❌ Kayıt hatası: {e}")
                import traceback

                traceback.print_exc()
                return False

        return False

    def verify_user(username, password, retry_count=3):
        """Kullanıcıyı doğrula"""
        if not username or not password:
            print("❌ Kullanıcı adı ve parola gerekli")
            return False

        for attempt in range(retry_count):
            try:
                with db.get_db_connection() as conn:
                    cursor = conn.cursor()
                    cursor.execute(
                        "SELECT password_hash, salt FROM users WHERE username = ?",
                        (username,),
                    )
                    result = cursor.fetchone()

                    if not result:
                        # Timing attack koruması için gecikme
                        time.sleep(0.5)
                        print(f"❌ Kullanıcı '{username}' bulunamadı")
                        return False

                    stored_hash, salt = result

                    # stored_hash string olarak geliyor, salt bytes olarak
                    print(f"⏳ '{username}' için hash doğrulanıyor...")

                    # ✅ DÜZELTME: verify metodunu kullan!
                    hasher = TrueMemoryHardHasher(
                        memory_cost_kb=8192, time_cost=3, parallelism=1
                    )

                    # Ya verify metodunu kullan:
                    is_valid = hasher.verify(password, stored_hash, salt)

                    # Ya da hash metoduna salt'ı açıkça ver:
                    # computed_hash = hasher.hash(password, salt=salt)  # salt
                    # parametresini adlandır!

                    if is_valid:  # veya computed_hash == stored_hash
                        # Başarılı giriş tarihini güncelle
                        cursor.execute(
                            "UPDATE users SET last_login = datetime('now') WHERE username = ?",
                            (username,),
                        )
                        conn.commit()
                        print(f"✅ Kullanıcı '{username}' başarıyla doğrulandı")
                        return True
                    else:
                        print(f"❌ Kullanıcı '{username}' için geçersiz parola")
                        return False

            except sqlite3.OperationalError as e:
                if "locked" in str(e) and attempt < retry_count - 1:
                    print(f"⏳ Veritabanı kilitli, {attempt + 1}. deneme...")
                    time.sleep(1 * (attempt + 1))
                else:
                    print(f"❌ Veritabanı hatası: {e}")
                    return False
            except Exception as e:
                print(f"❌ Doğrulama hatası: {e}")
                import traceback

                traceback.print_exc()
                return False

        return False

    def list_users():
        """Kayıtlı kullanıcıları listele"""
        try:
            with db.get_db_connection() as conn:
                cursor = conn.cursor()
                cursor.execute("""
                    SELECT username, created_at, last_login
                    FROM users
                    ORDER BY created_at DESC
                """)
                users = cursor.fetchall()

                if not users:
                    print("📭 Henüz kayıtlı kullanıcı yok")
                    return []

                print("\n📋 KAYITLI KULLANICILAR")
                print("=" * 60)
                for username, created_at, last_login in users:
                    print(f"\n👤 {username}:")
                    print(f"   📅 Oluşturulma: {created_at}")
                    print(
                        f"   🔐 Son giriş: {last_login if last_login else 'Henüz giriş yapılmadı'}"
                    )

                return users

        except Exception as e:
            print(f"❌ Listeleme hatası: {e}")
            return []

    def delete_user(username):
        """Kullanıcıyı sil"""
        try:
            with db.get_db_connection() as conn:
                cursor = conn.cursor()
                cursor.execute("DELETE FROM users WHERE username = ?", (username,))
                conn.commit()

                if cursor.rowcount > 0:
                    print(f"✅ '{username}' kullanıcısı silindi")
                    return True
                else:
                    print(f"❌ '{username}' kullanıcısı bulunamadı")
                    return False

        except Exception as e:
            print(f"❌ Silme hatası: {e}")
            return False

    def close_all_connections():
        """Tüm veritabanı bağlantılarını kapat"""
        try:
            if hasattr(db.thread_local, "conn"):
                thread_local.conn.close()
                delattr(thread_local, "conn")
                print("✅ Veritabanı bağlantıları kapatıldı")
        except BaseException:
            pass

    # Jupyter için interaktif fonksiyon - Jupyter widget'larını import edelim
    import ipywidgets as widgets
    from IPython.display import HTML, clear_output, display

    def interactive_demo():
        """Jupyter'da interaktif demo"""

        print("🎮 İNTERAKTİF MEMORY-HARD HASH DEMO")
        print("=" * 60)

        # Önce veritabanını kur
        db.setup_database()

        # Widget'ları oluştur
        username_input = db.widgets.Text(
            placeholder="Kullanıcı adı",
            description="Kullanıcı:",
            style={"description_width": "initial"},
        )

        password_input = db.widgets.Password(
            placeholder="Parola",
            description="Parola:",
            style={"description_width": "initial"},
        )

        output_area = db.widgets.Output(
            layout={
                "border": "1px solid #ddd",
                "padding": "10px",
                "min_height": "200px",
            }
        )

        def on_register_click(b):
            with output_area:
                db.clear_output()
                username = username_input.value
                password = password_input.value

                if not username or not password:
                    print("❌ Kullanıcı adı ve parola gerekli")
                    return

                print(f"📝 '{username}' kaydediliyor...")
                if db.save_user(username, password):
                    print(f"✅ '{username}' başarıyla kaydedildi")
                else:
                    print(f"❌ '{username}' kaydı başarısız")

        def on_login_click(b):
            with output_area:
                db.clear_output()
                username = username_input.value
                password = password_input.value

                if not username or not password:
                    print("❌ Kullanıcı adı ve parola gerekli")
                    return

                print(f"🔐 '{username}' doğrulanıyor...")
                if db.verify_user(username, password):
                    print(f"✅ '{username}' başarıyla doğrulandı")
                else:
                    print(f"❌ '{username}' doğrulama başarısız")

        def on_list_click(b):
            with output_area:
                db.clear_output()
                db.list_users()

        def on_clear_click(b):
            with output_area:
                db.clear_output()
                print("🧹 Çıktı temizlendi")

        register_btn = db.widgets.Button(
            description="Kayıt Ol",
            button_style="primary",
            icon="user-plus",
            layout=db.widgets.Layout(width="100px"),
        )

        login_btn = db.widgets.Button(
            description="Giriş Yap",
            button_style="success",
            icon="sign-in-alt",
            layout=db.widgets.Layout(width="100px"),
        )

        list_btn = db.widgets.Button(
            description="Listele",
            button_style="info",
            icon="list",
            layout=db.widgets.Layout(width="100px"),
        )

        clear_btn = db.widgets.Button(
            description="Temizle",
            button_style="warning",
            icon="trash",
            layout=db.widgets.Layout(width="100px"),
        )

        register_btn.on_click(on_register_click)
        login_btn.on_click(on_login_click)
        list_btn.on_click(on_list_click)
        clear_btn.on_click(on_clear_click)

        # Layout
        buttons = db.widgets.HBox([register_btn, login_btn, list_btn, clear_btn])

        form = db.widgets.VBox(
            [
                db.widgets.HTML(
                    "<h3 style='color: #2c3e50;'>👤 Kullanıcı İşlemleri</h3>"
                ),
                username_input,
                password_input,
                buttons,
                db.widgets.HTML("<h4 style='color: #3498db;'>📊 Çıktı:</h4>"),
                output_area,
            ],
            layout=db.widgets.Layout(width="80%", margin="20px"),
        )

        display(form)


def performance_comparison():
    """Memory-hard vs normal hash performans karşılaştırması"""

    password = "TestPassword123"
    salt = secrets.token_bytes(32)  # 32 byte - standart

    # 1. Memory-hard hasher (gerçek koruma)
    memory_hard = TrueMemoryHardHasher(memory_cost_kb=1024, time_cost=3)

    # 2. Normal hasher (hızlı ama daha az güvenli)
    config = FortifiedConfig()
    normal_hasher = FortifiedKhaHash256(config)

    # Test
    start = time.perf_counter()
    memory_hard.hash(password.encode(), salt)
    memory_hard_time = (time.perf_counter() - start) * 1000

    start = time.perf_counter()
    normal_hasher.hash(password.encode(), salt)
    normal_time = (time.perf_counter() - start) * 1000

    print(f"🔐 Memory-Hard Hash: {memory_hard_time:.1f} ms")
    print(f"⚡ Normal Hash: {normal_time:.1f} ms")
    print(f"📈 Yavaşlık Faktörü: {memory_hard_time / normal_time:.0f}x")

    return memory_hard_time, normal_time


def economic_analysis(memory_mb=8, time_ms=580):
    """
    Memory-hard hash'lerin ekonomik analizi
    GPU/ASIC saldırılarına karşı maliyet etkinliği
    """

    # Varsayımlar (Assumptions)
    gpu_hash_rate = 1_000_000_000  # 1 milyar hash/saniye (1 billion hashes/sec)
    electricity_cost = 0.15  # $/kWh
    gpu_power = 300  # Watt

    # Memory-hard için (For memory-hard)
    memory_hard_rate = 1000 / time_ms  # hash/saniye (hashes/sec)

    # Maliyet karşılaştırması (Cost comparison)
    gpu_daily_hashes = gpu_hash_rate * 86400
    memory_hard_daily_hashes = memory_hard_rate * 86400

    gpu_daily_cost = (gpu_power * 24 / 1000) * electricity_cost

    print("💰 EKONOMİK ANALİZ (ECONOMIC ANALYSIS)")
    print("=" * 50)
    print(f"🔧 GPU Hash Rate: {gpu_hash_rate:,} hash/s")
    print(f"🔐 Memory-Hard Rate: {memory_hard_rate:.1f} hash/s")
    print(f"📊 GPU Günlük Hash: {gpu_daily_hashes:,}")
    print(f"📈 Memory-Hard Günlük Hash: {memory_hard_daily_hashes:,.0f}")
    print(f"💡 GPU Günlük Maliyet: ${gpu_daily_cost:.2f}")
    print(f"🎯 Etkinlik Oranı: {gpu_daily_hashes / memory_hard_daily_hashes:,.0f}x")

    # Sonuç (Result)
    print("\n📢 SONUÇ (CONCLUSION):")
    print("Bir GPU ile memory-hard hash kırmak, normal hash'e göre")
    print(f"{gpu_daily_hashes / memory_hard_daily_hashes:,.0f} kat daha az verimlidir!")
    print("Bu da saldırıyı ekonomik olarak pratik olmaktan çıkarır.")


def secure_password_hashing(password, salt=None):
    """Güvenli parola hash'leme için minimum ayarlar"""

    if salt is None:
        salt = secrets.token_bytes(32)  # 256-bit - NIST/OWASP uyumlu

    # NIST SP 800-63B uyumlu ayarlar
    hasher = TrueMemoryHardHasher(
        memory_cost_kb=2048,  # 16MB (önerilen minimum)
        time_cost=4,  # 3 iterasyon
    )

    return hasher.hash(password.encode(), salt), salt


# Renkli çıktı için ANSI kodları


class Colors:
    GREEN = "\033[92m"
    YELLOW = "\033[93m"
    RED = "\033[91m"
    BLUE = "\033[94m"
    MAGENTA = "\033[95m"
    CYAN = "\033[96m"
    WHITE = "\033[97m"
    RESET = "\033[0m"
    BOLD = "\033[1m"
    UNDERLINE = "\033[4m"


def print_header(text: str, width: int = 70):
    print(f"\n{Colors.BOLD}{Colors.CYAN}{'=' * width}{Colors.RESET}")
    print(f"{Colors.BOLD}{Colors.CYAN}{text:^{width}}{Colors.RESET}")
    print(f"{Colors.BOLD}{Colors.CYAN}{'=' * width}{Colors.RESET}")


def print_subheader(text: str):
    print(f"\n{Colors.BOLD}{Colors.BLUE}{'─' * 50}{Colors.RESET}")
    print(f"{Colors.BOLD}{Colors.BLUE}▶ {text}{Colors.RESET}")


def print_success(text: str):
    print(f"{Colors.GREEN}✓ {text}{Colors.RESET}")


def print_error(text: str):
    print(f"{Colors.RED}✗ {text}{Colors.RESET}")


def print_warning(text: str):
    print(f"{Colors.YELLOW}⚠ {text}{Colors.RESET}")


def print_info(text: str):
    print(f"{Colors.BLUE}ℹ {text}{Colors.RESET}")


# ============================================================================
# YARDIMCI FONKSİYONLAR
# ============================================================================


def safe_hash_password(password: str, salt: bytes = None) -> str:
    """Güvenli hash_password wrapper - her çağrıda farklı salt kullan"""
    import secrets

    if isinstance(password, str):
        password = password.encode("utf-8")

    # Salt verilmemişse her seferinde yeni salt oluştur
    # NIST SP 800-63B, OWASP: 16-32 bayt önerilir, 32 bayt (256 bit) yeterlidir
    if salt is None:
        salt = secrets.token_bytes(32)  # 256-bit - endüstri standardı

    try:
        result = hash_password(password, salt)
        return result
    except Exception as e:
        # Farklı API'leri dene
        try:
            # String parametreli versiyon
            result = hash_password(password.decode("utf-8"), salt)
            return result
        except TypeError:
            # Salt opsiyonel versiyon
            try:
                result = hash_password(password.decode("utf-8"))
                return result
            except BaseException:
                raise e


def safe_quick_hash(data) -> str:
    """Güvenli quick_hash wrapper"""
    if isinstance(data, str):
        data = data.encode("utf-8")
    elif isinstance(data, bytearray):
        data = bytes(data)
    return quick_hash(data)


def calculate_bit_difference(hash1: str, hash2: str) -> int:
    """İki hex hash arasındaki farklı bit sayısını hesapla"""

    # Tip kontrolü
    if not isinstance(hash1, str) or not isinstance(hash2, str):
        raise TypeError(f"String bekleniyor, {type(hash1)} ve {type(hash2)} alındı")

    # Hex formatı kontrolü
    hash1 = hash1.lower().replace("0x", "")
    hash2 = hash2.lower().replace("0x", "")

    # Uzunlukları eşitle
    max_len = max(len(hash1), len(hash2))
    hash1 = hash1.zfill(max_len)
    hash2 = hash2.zfill(max_len)

    diff_count = 0
    for i in range(max_len):
        try:
            h1 = int(hash1[i], 16)
            h2 = int(hash2[i], 16)
            xor_result = h1 ^ h2
            diff_count += bin(xor_result).count("1")
        except (ValueError, IndexError):
            continue

    return diff_count


def cal_bit_difference(hash1: str, hash2: str) -> int:
    """İki hex hash arasındaki farklı bit sayısını hesapla"""
    diff_count = 0
    for i in range(min(len(hash1), len(hash2))):
        try:
            h1 = int(hash1[i], 16)
            h2 = int(hash2[i], 16)
            xor_result = h1 ^ h2
            diff_count += bin(xor_result).count("1")
        except ValueError:
            continue
    return diff_count


def avalanche_test(hasher, base_data: bytes, salt: bytes, num_tests: int = 100):
    """Avalanche etkisi testi - tek bit değişiminin etkisi"""

    hasher = FortifiedKhaHash256()
    base_hash = hasher.hash(base_data, salt)
    total_diff = 0

    for i in range(num_tests):
        # Tek bir byte'da tek bit değiştir
        modified = bytearray(base_data)
        byte_idx = i % len(modified)
        bit_idx = i % 8
        modified[byte_idx] ^= 1 << bit_idx

        new_hash = hasher.hash(bytes(modified), salt)
        diff = calculate_bit_difference(base_hash, new_hash)
        total_diff += diff

    avg_diff = total_diff / num_tests
    print(f"Ortalama bit farkı: {avg_diff:.2f}")
    print("İdeal avalanche: 128 bit (256-bit hash için)")
    print(f"Başarım: {avg_diff / 128:.2%}")


def detailed_avalanche_test(
    hasher, base_data: bytes, salt: bytes, num_tests: int = 1000
):
    """Detaylı avalanche analizi - bit_diff'leri toplar"""

    base_hash = hasher.hash(base_data, salt)
    bit_diffs = []  # 👈 BURADA TOPLANIYOR
    positions = []

    for i in range(num_tests):
        # Farklı pozisyonlarda bit değiştir
        modified = bytearray(base_data)
        byte_idx = i % len(modified)
        bit_idx = i % 8
        modified[byte_idx] ^= 1 << bit_idx

        new_hash = hasher.hash(bytes(modified), salt)
        diff = calculate_bit_difference(base_hash, new_hash)

        bit_diffs.append(diff)  # 👈 DİFF'İ LİSTEYE EKLE
        positions.append((byte_idx, bit_idx))

    # İstatistikler
    avg_diff = np.mean(bit_diffs)
    std_diff = np.std(bit_diffs)
    min_diff = np.min(bit_diffs)
    max_diff = np.max(bit_diffs)

    print("🔬 DETAYLI AVALANCHE ANALİZİ")
    print("=" * 50)
    print(f"📊 Test sayısı: {num_tests}")
    print(f"🎯 Ortalama bit farkı: {avg_diff:.4f}")
    print("📈 İdeal: 128.0000")
    print(
        f"📉 Sapma: {abs(avg_diff - 128):.4f} bit (%{abs(avg_diff - 128) / 128 * 100:.3f})"
    )
    print(f"⚖️ Standart sapma: {std_diff:.4f}")
    print(f"🔻 Minimum: {min_diff}")
    print(f"🔺 Maximum: {max_diff}")
    print(f"📋 Aralık: {min_diff}-{max_diff}")

    # Normal dağılım kontrolü (ideal ± 12 bit)
    in_range = sum(116 <= d <= 140 for d in bit_diffs)
    print(f"✅ Normal aralıkta (116-140): {in_range / num_tests:.1%}")

    # bit_diffs listesini döndür
    return bit_diffs, avg_diff, std_diff


def comprehensive_avalanche_test(hasher, base_data: bytes, salt: bytes):
    """Tüm bit pozisyonlarını test et - DÜZELTİLMİŞ HEATMAP"""

    base_hash = hasher.hash(base_data, salt)
    bit_diffs = []

    total_bits = len(base_data) * 8
    print(f"Toplam bit pozisyonu: {total_bits}")

    # Tüm pozisyonları test et
    for byte_idx in range(len(base_data)):
        for bit_idx in range(8):
            modified = bytearray(base_data)
            modified[byte_idx] ^= 1 << bit_idx

            new_hash = hasher.hash(bytes(modified), salt)
            diff = calculate_bit_difference(base_hash, new_hash)
            bit_diffs.append(diff)

    # İstatistikler
    avg = np.mean(bit_diffs)
    std = np.std(bit_diffs)

    print("🔍 POZİSYON BAZLI AVALANCHE ANALİZİ")
    print("=" * 50)
    print(f"Toplam bit pozisyonu: {total_bits}")
    print(f"Test edilen pozisyon: {len(bit_diffs)}")
    print(f"Ortalama: {avg:.2f}")
    print(f"Standart sapma: {std:.2f}")
    print(f"Min: {np.min(bit_diffs)}")
    print(f"Max: {np.max(bit_diffs)}")

    # HEATMAP - DÜZGÜN BOYUT KONTROLÜ
    try:
        # Byte x bit matrisi oluştur
        heatmap_data = np.array(bit_diffs).reshape(len(base_data), 8)

        plt.figure(figsize=(12, 6))

        # Heatmap
        plt.subplot(1, 2, 1)
        im = plt.imshow(
            heatmap_data,
            cmap="RdYlGn",
            aspect="auto",
            vmin=100,
            vmax=156,
            interpolation="nearest",
        )
        plt.colorbar(im, label="Farklı bit sayısı")
        plt.xlabel("Bit pozisyonu (0-7)")
        plt.ylabel("Byte pozisyonu")
        plt.title(f"Avalanche Dağılımı Heatmap\nOrtalama: {avg:.2f}, Std: {std:.2f}")

        # Değerleri hücrelere yaz
        for i in range(len(base_data)):
            for j in range(8):
                plt.text(
                    j,
                    i,
                    f"{int(heatmap_data[i, j])}",
                    ha="center",
                    va="center",
                    color="black",
                    fontsize=8,
                )

        # Histogram
        plt.subplot(1, 2, 2)
        plt.hist(bit_diffs, bins=20, alpha=0.7, color="blue", edgecolor="black")
        plt.axvline(128, color="red", linestyle="--", label="İdeal (128)")
        plt.axvline(avg, color="green", linestyle="-", label=f"Ortalama ({avg:.1f})")
        plt.xlabel("Farklı Bit Sayısı")
        plt.ylabel("Frekans")
        plt.title("Avalanche Dağılımı Histogram")
        plt.legend()
        plt.grid(alpha=0.3)

        plt.tight_layout()
        plt.show()

    except ValueError as e:
        print(f"⚠️ Heatmap oluşturulamadı: {e}")
        print(f"bit_diffs uzunluğu: {len(bit_diffs)}")
        print(f"Beklenen: {len(base_data)} x 8 = {len(base_data) * 8}")

        # Alternatif grafik
        plt.figure(figsize=(12, 4))
        plt.plot(bit_diffs, "o-", alpha=0.5, markersize=3)
        plt.axhline(128, color="red", linestyle="--", label="İdeal (128)")
        plt.axhline(avg, color="green", linestyle="-", label=f"Ortalama ({avg:.1f})")
        plt.xlabel("Bit Pozisyonu")
        plt.ylabel("Farklı Bit Sayısı")
        plt.title("Pozisyon Bazlı Avalanche Değerleri")
        plt.legend()
        plt.grid(alpha=0.3)
        plt.show()

    return bit_diffs, avg, std


def safe_heatmap_plot(bit_diffs, base_data_length):
    """Güvenli heatmap oluşturma - hata kontrolü ile"""

    expected_size = base_data_length * 8

    if len(bit_diffs) != expected_size:
        print(f"⚠️ Uyarı: Beklenen {expected_size} değer, alınan {len(bit_diffs)}")
        # Eksik veya fazla değerleri düzelt
        if len(bit_diffs) > expected_size:
            bit_diffs = bit_diffs[:expected_size]
        else:
            # Eksik değerleri ortalama ile doldur
            avg = np.mean(bit_diffs)
            bit_diffs = bit_diffs + [avg] * (expected_size - len(bit_diffs))

    try:
        # Reshape işlemi
        heatmap_data = np.array(bit_diffs).reshape(base_data_length, 8)

        plt.figure(figsize=(10, 6))
        im = plt.imshow(
            heatmap_data, cmap="viridis", aspect="auto", interpolation="nearest"
        )
        plt.colorbar(im, label="Farklı Bit Sayısı")
        plt.xlabel("Bit Pozisyonu")
        plt.ylabel("Byte Pozisyonu")
        plt.title("Avalanche Etkisi - Pozisyon Bazlı")

        # Değerleri göster
        for i in range(base_data_length):
            for j in range(8):
                plt.text(
                    j,
                    i,
                    f"{int(heatmap_data[i, j])}",
                    ha="center",
                    va="center",
                    color="white" if heatmap_data[i, j] < 128 else "black",
                    fontsize=8,
                )

        plt.tight_layout()
        plt.show()
        return True

    except Exception as e:
        print(f"❌ Heatmap oluşturulamadı: {e}")
        return False


def plot_avalanche_distribution(bit_diffs):
    """Avalanche dağılımını görselleştir"""

    plt.figure(figsize=(15, 5))

    # 1. Histogram
    plt.subplot(1, 3, 1)
    plt.hist(bit_diffs, bins=30, alpha=0.7, color="blue", edgecolor="black")
    plt.axvline(128, color="red", linestyle="--", linewidth=2, label="İdeal (128)")
    plt.axvline(
        np.mean(bit_diffs),
        color="green",
        linestyle="-",
        linewidth=2,
        label=f"Ortalama ({np.mean(bit_diffs):.2f})",
    )
    plt.axvline(116, color="orange", linestyle=":", alpha=0.5, label="Alt sınır (116)")
    plt.axvline(140, color="orange", linestyle=":", alpha=0.5, label="Üst sınır (140)")
    plt.xlabel("Farklı Bit Sayısı")
    plt.ylabel("Frekans")
    plt.title("Avalanche Dağılımı (Histogram)")
    plt.legend()
    plt.grid(alpha=0.3)

    # 2. Box plot
    plt.subplot(1, 3, 2)
    bp = plt.boxplot(bit_diffs, vert=True, patch_artist=True)
    bp["boxes"][0].set_facecolor("lightblue")
    plt.axhline(128, color="red", linestyle="--", linewidth=2, label="İdeal")
    plt.axhline(
        np.mean(bit_diffs),
        color="green",
        linestyle="-",
        linewidth=2,
        label=f"Ortalama ({np.mean(bit_diffs):.2f})",
    )
    plt.ylabel("Farklı Bit Sayısı")
    plt.title("Kutu Grafiği (Box Plot)")
    plt.legend()
    plt.grid(alpha=0.3)

    # 3. Line plot (test sırasına göre)
    plt.subplot(1, 3, 3)
    plt.plot(bit_diffs, alpha=0.5, color="blue", linewidth=0.5)
    plt.axhline(128, color="red", linestyle="--", label="İdeal")
    plt.axhline(
        np.mean(bit_diffs),
        color="green",
        linestyle="-",
        label=f"Ortalama ({np.mean(bit_diffs):.2f})",
    )
    plt.fill_between(range(len(bit_diffs)), 116, 140, alpha=0.1, color="orange")
    plt.xlabel("Test Numarası")
    plt.ylabel("Farklı Bit Sayısı")
    plt.title("Test Sırasına Göre Avalanche")
    plt.legend()
    plt.grid(alpha=0.3)

    plt.tight_layout()
    plt.show()


# ============================================================================
# TEMEL FONKSİYONELLİK TESTLERİ
# ============================================================================


def test_basic_functionality() -> Dict[str, Any]:
    """Temel fonksiyonellik testleri"""
    print_header("TEMEL FONKSİYONELLİK TESTLERİ")

    results = {
        "tests_passed": 0,
        "tests_failed": 0,
        "tests_total": 0,
        "details": [],
        "start_time": time.time(),
    }

    test_cases = [
        (
            "quick_hash_string",
            lambda: safe_quick_hash("test"),
            "quick_hash string input",
        ),
        (
            "quick_hash_bytes",
            lambda: safe_quick_hash(b"test"),
            "quick_hash bytes input",
        ),
        (
            "hash_password",
            lambda: safe_hash_password("testpassword"),
            "Parola hash'leme",
        ),
        (
            "generate_fortified_hasher",
            lambda: generate_fortified_hasher(),
            "Hasher oluşturma",
        ),
        (
            "FortifiedKhaHash256",
            lambda: FortifiedKhaHash256(),
            "FortifiedKhaHash256 sınıfı",
        ),
        ("FortifiedConfig", lambda: FortifiedConfig(), "FortifiedConfig sınıfı"),
        ("empty_string", lambda: safe_quick_hash(""), "Boş string hash"),
        ("empty_bytes", lambda: safe_quick_hash(b""), "Boş bytes hash"),
    ]

    for test_name, test_func, description in test_cases:
        results["tests_total"] += 1
        try:
            result = test_func()

            if test_name in [
                "quick_hash_string",
                "quick_hash_bytes",
                "empty_string",
                "empty_bytes",
            ]:
                assert isinstance(result, str) and len(result) == 64
                details = f"{result[:16]}... ({len(result)} chars)"

            elif test_name == "hash_password":
                assert isinstance(result, str)
                details = f"{result[:30]}..."

            elif test_name == "generate_fortified_hasher":
                assert hasattr(result, "hash")
                details = "Hasher oluşturuldu"

            elif test_name in ["FortifiedKhaHash256", "FortifiedConfig"]:
                assert result is not None
                details = f"{test_name} oluşturuldu"

            else:
                assert result is not None
                details = "OK"

            results["tests_passed"] += 1
            results["details"].append(
                {"test": description, "status": "passed", "details": details}
            )
            print_success(f"{description}: Başarılı - {details}")

        except Exception as e:
            results["tests_failed"] += 1
            error_details = f"{str(e)[:100]}"
            results["details"].append(
                {"test": description, "status": "failed", "details": error_details}
            )
            print_error(f"{description} hatası: {e}")

    results["end_time"] = time.time()
    results["duration"] = results["end_time"] - results["start_time"]

    print_subheader("Temel Fonksiyonellik Test Özeti")
    print(f"Toplam Test: {results['tests_total']}")
    print(f"Başarılı: {results['tests_passed']}")
    print(f"Başarısız: {results['tests_failed']}")
    print(
        f"Başarı Oranı: {results['tests_passed'] / results['tests_total'] * 100:.1f}%"
    )

    return results


# ============================================================================
# PERFORMANS TESTLERİ
# ============================================================================


def test_performance_scenarios() -> Dict[str, Any]:
    """Performans senaryo testleri"""
    print_header("PERFORMANS SENARYO TESTLERİ")

    results = {"scenarios": [], "throughputs": [], "start_time": time.time()}

    try:
        # Senaryo 1: Küçük veriler
        print_subheader("1. Küçük Veri Senaryosu (1-64B)")
        small_sizes = [1, 4, 16, 32, 64]
        small_results = []

        for size in small_sizes:
            data = secrets.token_bytes(size)
            times = []

            for _ in range(100):  # Daha fazla iterasyon
                start = time.perf_counter_ns()
                safe_quick_hash(data)
                elapsed = (time.perf_counter_ns() - start) / 1_000_000  # ms
                times.append(elapsed)

            avg_time = statistics.median(times)
            throughput = (size / 1024) / (avg_time / 1000) if avg_time > 0 else 0

            small_results.append(
                {"size": size, "avg_time_ms": avg_time, "throughput_kbs": throughput}
            )

            print(f"  {size:3d} byte: {avg_time:6.3f}ms, {throughput:6.0f} KB/s")

        results["scenarios"].append(
            {
                "name": "Küçük Veri",
                "results": small_results,
                "avg_throughput": statistics.mean(
                    [r["throughput_kbs"] for r in small_results]
                ),
            }
        )

        # Senaryo 2: quick_hash vs hash_password karşılaştırması
        print_subheader("2. quick_hash vs hash_password Karşılaştırması")

        # quick_hash performansı
        quick_times = []
        test_data = secrets.token_bytes(1024)

        for _ in range(50):
            start = time.perf_counter_ns()
            safe_quick_hash(test_data)
            quick_times.append((time.perf_counter_ns() - start) / 1_000_000)

        avg_quick = statistics.median(quick_times)

        # hash_password performansı - DÜZELTİLDİ: Her seferinde farklı salt!
        secure_times = []
        test_password = "testpassword"

        for _ in range(5):  # Daha az iterasyon (çok yavaş)
            # Her seferinde YENİ salt oluştur
            start = time.perf_counter_ns()
            # salt parametresi yok - her seferinde yeni oluşturulur
            safe_hash_password(test_password)
            elapsed = (time.perf_counter_ns() - start) / 1_000_000
            secure_times.append(elapsed)

        avg_secure = statistics.median(secure_times) if secure_times else 0
        speed_difference = avg_secure / max(avg_quick, 0.001)

        results["scenarios"].append(
            {
                "name": "Hash Comparison",
                "results": {
                    "quick_hash_avg_ms": avg_quick,
                    "hash_password_avg_ms": avg_secure,
                    "speed_difference": speed_difference,
                },
            }
        )

        print(f"  quick_hash: {avg_quick:.3f}ms")
        print(f"  hash_password: {avg_secure:.3f}ms")
        print(f"  Fark: {speed_difference:.1f}x daha yavaş")

        results["end_time"] = time.time()
        results["duration"] = results["end_time"] - results["start_time"]

        all_throughputs = []
        for r in small_results:
            all_throughputs.append(r["throughput_kbs"])

        if all_throughputs:
            results["avg_throughput"] = statistics.mean(all_throughputs)
            results["max_throughput"] = max(all_throughputs)
            results["min_throughput"] = min(all_throughputs)

    except Exception as e:
        results["end_time"] = time.time()
        results["duration"] = results["end_time"] - results["start_time"]
        print_error(f"Performans testi hatası: {e}")
        traceback.print_exc()

    return results


# ============================================================================
# GÜVENLİK TESTLERİ
# ============================================================================


def test_security_scenarios() -> Dict[str, Any]:
    """Güvenlik senaryo testleri"""
    print_header("GÜVENLİK SENARYO TESTLERİ")

    results = {"scenarios": [], "start_time": time.time()}

    try:
        # Senaryo 1: Çakışma testi
        print_subheader("1. Çakışma Testi (100 örnek)")

        collisions_found = 0
        hash_dict = {}

        for i in range(100):
            data = secrets.token_hex(16)
            h = safe_quick_hash(data)

            if h in hash_dict:
                collisions_found += 1
            else:
                hash_dict[h] = data

        collision_safe = collisions_found == 0

        results["scenarios"].append(
            {
                "name": "Collision Test",
                "results": {
                    "samples": 100,
                    "collisions": collisions_found,
                    "collision_rate": collisions_found / 100 * 100,
                    "safe": collision_safe,
                },
            }
        )

        print("    Örnekler: 100")
        print(f"    Çakışmalar: {collisions_found}")
        print(f"    Durum: {'GÜVENLİ ✓' if collision_safe else 'ZAFİYET ✗'}")

        # Senaryo 2: Avalanche etkisi
        print_subheader("2. Avalanche Etkisi Testi")

        test_pairs = [
            ("Hello World", "Hello Xorld"),
            ("hello world", "Hello World"),
            ("Test123", "Test124"),
            ("A" * 100, "A" * 99 + "B"),
            (b"\x00\x01\x02\x03\x04", b"\x00\x01\x02\x03\x05"),
        ]

        bit_changes = []

        for original, modified in test_pairs:
            h1 = safe_quick_hash(original)
            h2 = safe_quick_hash(modified)
            diff = calculate_bit_difference(h1, h2)
            total_bits = len(h1) * 4
            percentage = (diff / total_bits) * 100
            bit_changes.append(percentage)

            print(
                f"    {str(original)[:15]}... → {diff}/{total_bits} bit (%{percentage:.1f})"
            )

        avg_avalanche = statistics.mean(bit_changes) if bit_changes else 0

        results["scenarios"].append(
            {
                "name": "Avalanche Effect",
                "results": {"average_percentage": avg_avalanche},
            }
        )

        print(f"    Ortalama Avalanche: %{avg_avalanche:.1f}")

        # Senaryo 3: Entropi testi
        print_subheader("3. Entropi Testi")

        hash_samples = [safe_quick_hash(secrets.token_hex(16)) for _ in range(200)]
        byte_distribution = [0] * 256

        for h in hash_samples:
            for i in range(0, 64, 2):
                try:
                    byte_val = int(h[i : i + 2], 16)
                    byte_distribution[byte_val] += 1
                except ValueError:
                    pass

        total_bytes = sum(byte_distribution)
        entropy = 0
        if total_bytes > 0:
            for count in byte_distribution:
                if count > 0:
                    p = count / total_bytes
                    entropy -= p * math.log2(p)

        entropy_percentage = (entropy / 8) * 100
        good_entropy = entropy_percentage >= 95

        results["scenarios"].append(
            {
                "name": "Entropy Test",
                "results": {
                    "entropy_bits": entropy,
                    "entropy_percentage": entropy_percentage,
                    "good_entropy": good_entropy,
                },
            }
        )

        print(f"    Entropy: {entropy:.2f} bits ({entropy_percentage:.1f}%)")
        print(f"    Durum: {'İYİ ✓' if good_entropy else 'ZAYIF ✗'}")

        # Senaryo 4: Salt güvenliği - DÜZELTİLDİ: Her seferinde farklı salt
        print_subheader("4. Salt Güvenlik Testi")

        test_password = "MySecurePassword123!"
        hashes = []

        for _ in range(3):
            # Her seferinde yeni salt ile hash'le
            # salt parametresi yok, her seferinde yeni oluşturulur
            h = safe_hash_password(test_password)
            hashes.append(h[:30] + "...")

        unique_hashes = len(set(hashes))
        secure = unique_hashes == 3

        results["scenarios"].append(
            {
                "name": "Salt Security",
                "results": {"unique_hashes": unique_hashes, "secure": secure},
            }
        )

        status = "GÜVENLİ ✓" if secure else "ZAFİYET ✗"
        color = Colors.GREEN if secure else Colors.RED
        print(f"    {color}{status}{Colors.RESET} {unique_hashes}/3 unique hash")
        for h in hashes:
            print(f"      {h}")

        results["end_time"] = time.time()
        results["duration"] = results["end_time"] - results["start_time"]

        # Güvenlik skoru
        security_tests_passed = 0
        if collision_safe:
            security_tests_passed += 1
        if avg_avalanche >= 45:
            security_tests_passed += 1
        if good_entropy:
            security_tests_passed += 1
        if secure:
            security_tests_passed += 1

        security_score = (security_tests_passed / 4) * 100
        results["overall_security_score"] = security_score

        print_subheader("Güvenlik Senaryo Özeti")
        print(f"Güvenlik Skoru: {security_score:.1f}/100")

    except Exception as e:
        results["end_time"] = time.time()
        results["duration"] = results["end_time"] - results["start_time"]
        print_error(f"Güvenlik testi hatası: {e}")
        traceback.print_exc()

    return results


# ============================================================================
# GERÇEK DÜNYA SENARYO TESTLERİ
# ============================================================================


def test_real_world_scenarios() -> Dict[str, Any]:
    """Gerçek dünya senaryo testleri"""
    print_header("GERÇEK DÜNYA SENARYO TESTLERİ")

    results = {
        "scenarios": [],
        "passed_tests": [],
        "failed_tests": [],
        "start_time": time.time(),
    }

    try:
        # Senaryo 1: Web uygulaması
        print_subheader("1. Web Uygulaması Senaryosu")

        web_scenario = {"name": "Web Application", "tests": []}

        # 1.1 Kullanıcı kaydı - DÜZELTİLDİ: Her kullanıcı için FARKLI salt!
        print_info("  a) Kullanıcı Kaydı")

        users = [
            {"username": "alice", "password": "AlicePass123!"},
            {"username": "bob", "password": "BobSecure456@"},
            {"username": "charlie", "password": "Charlie789#"},
        ]

        user_hashes = []

        for user in users:
            # Her kullanıcı için YENİ salt oluştur
            # salt parametresi yok - her seferinde yeni
            password_hash = safe_hash_password(user["password"])
            starts_with_kha = password_hash.startswith(("KHA256$", "KHA256-USB$"))
            has_salt = "$" in password_hash
            passed = starts_with_kha and has_salt

            user_hashes.append(
                {
                    "username": user["username"],
                    "password_hash": password_hash[:50] + "...",
                    "full_hash": password_hash,
                    "passed": passed,
                }
            )

            if passed:
                results["passed_tests"].append(f"User Registration: {user['username']}")
            else:
                results["failed_tests"].append(f"User Registration: {user['username']}")

        web_scenario["tests"].append(
            {
                "test": "User Registration",
                "results": user_hashes,
                "passed": all(h["passed"] for h in user_hashes),
            }
        )

        for user in user_hashes:
            status = "✓" if user["passed"] else "✗"
            color = Colors.GREEN if user["passed"] else Colors.RED
            print(
                f"    {color}{status}{Colors.RESET} {user['username']}: {user['password_hash']}"
            )

        # Hash'lerin farklı olduğunu kontrol et
        unique_hashes = len(set(u["full_hash"] for u in user_hashes))
        if unique_hashes == 3:
            print_success("    Tüm kullanıcılar farklı hash değerlerine sahip")
        else:
            print_warning(
                f"    {unique_hashes}/3 unique hash - AYNI SALT KULLANILIYOR!"
            )

        # 1.2 Oturum token'ı
        print_info("  b) Oturum Token'ı")

        session_tokens = []
        for i in range(3):
            token_data = f"session_{i}_{secrets.token_hex(16)}"
            token_hash = safe_quick_hash(token_data)
            passed = len(token_hash) == 64

            session_tokens.append(
                {
                    "token_data": token_data[:20] + "...",
                    "token_hash": token_hash[:20] + "...",
                    "passed": passed,
                }
            )

            if passed:
                results["passed_tests"].append(f"Session Token {i}")
            else:
                results["failed_tests"].append(f"Session Token {i}")

        web_scenario["tests"].append(
            {
                "test": "Session Tokens",
                "results": session_tokens,
                "passed": all(t["passed"] for t in session_tokens),
            }
        )

        for token in session_tokens:
            status = "✓" if token["passed"] else "✗"
            color = Colors.GREEN if token["passed"] else Colors.RED
            print(
                f"    {color}{status}{Colors.RESET} Token: {token['token_data']} → {token['token_hash']}"
            )

        results["scenarios"].append(web_scenario)

        # Senaryo 2: Uyumluluk testi
        print_subheader("2. Uyumluluk Testi")

        compatibility_scenario = {"name": "Compatibility", "tests": []}

        print_info("  a) Python hashlib ile Karşılaştırma")

        test_strings = ["hello", "world", "test123", "password"]
        comparison_results = []

        for test_str in test_strings:
            hashlib_hash = hashlib.sha256(test_str.encode()).hexdigest()
            kha_hash = safe_quick_hash(test_str)

            comparison_results.append(
                {
                    "input": test_str,
                    "hashlib_length": len(hashlib_hash),
                    "kha_length": len(kha_hash),
                    "same_hash": hashlib_hash == kha_hash,
                }
            )

            if len(kha_hash) == 64:
                results["passed_tests"].append(f"Hashlib comparison: {test_str}")
            else:
                results["failed_tests"].append(f"Hashlib comparison: {test_str}")

        compatibility_scenario["tests"].append(
            {
                "test": "Hashlib Compatibility",
                "results": comparison_results,
                "passed": all(r["kha_length"] == 64 for r in comparison_results),
            }
        )

        for result in comparison_results:
            status = "✓" if result["kha_length"] == 64 else "✗"
            color = Colors.GREEN if result["kha_length"] == 64 else Colors.RED
            print(
                f"    {color}{status}{Colors.RESET} '{result['input']}': "
                f"hashlib={result['hashlib_length']}, KHA={result['kha_length']}"
            )

        results["scenarios"].append(compatibility_scenario)

        results["end_time"] = time.time()
        results["duration"] = results["end_time"] - results["start_time"]

        total_tests = len(results["passed_tests"]) + len(results["failed_tests"])
        passed_tests = len(results["passed_tests"])
        success_rate = (passed_tests / total_tests * 100) if total_tests > 0 else 0
        results["overall_success_rate"] = success_rate

        print_subheader("Gerçek Dünya Senaryo Özeti")
        print(f"Toplam Test: {total_tests}")
        print(f"Başarılı Test: {passed_tests}")
        print(f"Başarısız Test: {len(results['failed_tests'])}")
        print(f"Başarı Oranı: %{success_rate:.1f}")

    except Exception as e:
        results["end_time"] = time.time()
        results["duration"] = results["end_time"] - results["start_time"]
        print_error(f"Gerçek dünya testi hatası: {e}")
        traceback.print_exc()

    return results


# ============================================================================
# EDGE CASE TESTLERİ
# ============================================================================


def test_edge_cases() -> Dict[str, Any]:
    """Edge case testleri"""
    print_header("EDGE CASE TESTLERİ")

    results = {
        "tests_passed": 0,
        "tests_failed": 0,
        "tests_total": 0,
        "details": [],
        "start_time": time.time(),
    }

    edge_cases = [
        ("Boş string", lambda: safe_quick_hash(""), "Boş string"),
        ("Boş bytes", lambda: safe_quick_hash(b""), "Boş bytes"),
        ("Çok büyük veri", lambda: safe_quick_hash(b"x" * 32768), "32KB veri"),
        ("Unicode", lambda: safe_quick_hash("😀🎉🚀测试"), "Unicode karakterler"),
        (
            "Binary null",
            lambda: safe_quick_hash(b"\x00\x01\x02\x03\x00"),
            "Null byte'lar",
        ),
        ("Özel karakterler", lambda: safe_quick_hash("\n\t\r\b\f"), "Özel karakterler"),
        ("Büyük sayı", lambda: safe_quick_hash(str(2**128)), "Büyük sayı"),
        (
            "Tekrarlayan pattern",
            lambda: safe_quick_hash("AB" * 100),
            "Tekrarlayan pattern",
        ),
        (
            "Bytearray",
            lambda: safe_quick_hash(bytearray(range(100))),
            "Bytearray input",
        ),
    ]

    for test_name, test_func, description in edge_cases:
        results["tests_total"] += 1
        try:
            result = test_func()
            assert isinstance(result, str) and len(result) == 64

            results["tests_passed"] += 1
            results["details"].append(
                {
                    "test": description,
                    "status": "passed",
                    "details": f"{result[:16]}...",
                }
            )
            print_success(f"{description}: Başarılı")

        except Exception as e:
            results["tests_failed"] += 1
            results["details"].append(
                {"test": description, "status": "failed", "details": str(e)[:100]}
            )
            print_error(f"{description} hatası: {e}")

    results["end_time"] = time.time()
    results["duration"] = results["end_time"] - results["start_time"]

    print_subheader("Edge Case Test Özeti")
    print(f"Toplam Test: {results['tests_total']}")
    print(f"Başarılı: {results['tests_passed']}")
    print(f"Başarısız: {results['tests_failed']}")
    print(
        f"Başarı Oranı: {results['tests_passed'] / results['tests_total'] * 100:.1f}%"
    )

    return results


# ============================================================================
# ANA TEST FONKSİYONU
# ============================================================================


def run_comprehensive_test_suite() -> Dict[str, Any]:
    """Kapsamlı test paketini çalıştır"""

    print_header("KHA-256 KAPSAMLI TAM TEST SÜİTİ v3.3", 80)
    print(
        f"{Colors.BOLD}Başlangıç zamanı: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}{Colors.RESET}"
    )

    all_results = {
        "test_suite_version": "3.3",
        "start_time": time.time(),
        "modules_tested": [],
    }

    try:
        # 1. Temel fonksiyonellik testleri
        func_results = test_basic_functionality()
        all_results["functionality_tests"] = func_results
        all_results["modules_tested"].append("functionality_tests")

        # 2. Performans testleri
        perf_results = test_performance_scenarios()
        all_results["performance_tests"] = perf_results
        all_results["modules_tested"].append("performance_tests")

        # 3. Güvenlik testleri
        sec_results = test_security_scenarios()
        all_results["security_tests"] = sec_results
        all_results["modules_tested"].append("security_tests")

        # 4. Gerçek dünya testleri
        real_results = test_real_world_scenarios()
        all_results["real_world_tests"] = real_results
        all_results["modules_tested"].append("real_world_tests")

        # 5. Edge case testleri
        edge_results = test_edge_cases()
        all_results["edge_case_tests"] = edge_results
        all_results["modules_tested"].append("edge_case_tests")

        # Genel istatistikler
        all_results["end_time"] = time.time()
        all_results["total_duration"] = (
            all_results["end_time"] - all_results["start_time"]
        )

        # Toplam başarı oranı
        total_tests = 0
        passed_tests = 0

        for module in ["functionality_tests", "edge_case_tests"]:
            if module in all_results:
                total_tests += all_results[module].get("tests_total", 0)
                passed_tests += all_results[module].get("tests_passed", 0)

        success_rate = (passed_tests / total_tests * 100) if total_tests > 0 else 0

        all_results["overall_results"] = {
            "total_modules": len(all_results["modules_tested"]),
            "total_tests": total_tests,
            "passed_tests": passed_tests,
            "success_rate": success_rate,
            "total_duration_seconds": all_results["total_duration"],
        }

        # FİNAL RAPOR
        print_header("FİNAL TEST SONUÇLARI", 80)

        print(
            f"\n{Colors.BOLD}{'MODÜL':<25} {'DURUM':<15} {'AÇIKLAMA':<30}{Colors.RESET}"
        )
        print(f"{'-' * 80}")

        for module_name in all_results["modules_tested"]:
            module_data = all_results.get(module_name, {})

            if module_name in ["functionality_tests", "edge_case_tests"]:
                total = module_data.get("tests_total", 0)
                passed = module_data.get("tests_passed", 0)
                if total > 0:
                    success_percent = (passed / total) * 100
                    if success_percent >= 90:
                        status = "EXCELLENT"
                        color = Colors.GREEN
                    elif success_percent >= 80:
                        status = "GOOD"
                        color = Colors.YELLOW
                    else:
                        status = "NEEDS WORK"
                        color = Colors.RED
                    desc = f"{passed}/{total} ({success_percent:.0f}%)"
                else:
                    status = "UNKNOWN"
                    color = Colors.YELLOW
                    desc = "No tests"

            elif module_name == "performance_tests":
                avg_tp = module_data.get("avg_throughput", 0)
                if avg_tp > 10000:
                    status = "EXCELLENT"
                    color = Colors.GREEN
                elif avg_tp > 1000:
                    status = "GOOD"
                    color = Colors.YELLOW
                else:
                    status = "NEEDS WORK"
                    color = Colors.RED
                desc = f"{avg_tp:.0f} KB/s"

            elif module_name == "security_tests":
                sec_score = module_data.get("overall_security_score", 0)
                if sec_score >= 90:
                    status = "EXCELLENT"
                    color = Colors.GREEN
                elif sec_score >= 75:
                    status = "GOOD"
                    color = Colors.YELLOW
                else:
                    status = "NEEDS WORK"
                    color = Colors.RED
                desc = f"{sec_score:.0f}/100"

            elif module_name == "real_world_tests":
                success_rate_rw = module_data.get("overall_success_rate", 0)
                if success_rate_rw >= 90:
                    status = "EXCELLENT"
                    color = Colors.GREEN
                elif success_rate_rw >= 80:
                    status = "GOOD"
                    color = Colors.YELLOW
                else:
                    status = "NEEDS WORK"
                    color = Colors.RED
                desc = f"{success_rate_rw:.1f}%"

            else:
                status = "UNKNOWN"
                color = Colors.YELLOW
                desc = "No evaluation"

            print(f"{module_name:<25} {color}{status:<15}{Colors.RESET} {desc:<30}")

        print(f"\n{Colors.BOLD}GENEL DEĞERLENDİRME:{Colors.RESET}")
        print(f"Toplam Süre: {all_results['total_duration']:.2f} saniye")
        print(f"Test Edilen Modül: {len(all_results['modules_tested'])}")
        print(f"Toplam Test: {total_tests}")
        print(f"Başarılı Test: {passed_tests}")
        print(f"Başarı Oranı: {success_rate:.1f}%")

        if success_rate >= 90:
            print(
                f"\n{Colors.GREEN}{Colors.BOLD}✓ TÜM TESTLER BAŞARIYLA GEÇTİ{Colors.RESET}"
            )
        elif success_rate >= 75:
            print(
                f"\n{Colors.YELLOW}{Colors.BOLD}⚠ ÇOĞU TEST GEÇTİ, BAZI İYİLEŞTİRMELER GEREKEBİLİR{Colors.RESET}"
            )
        else:
            print(
                f"\n{Colors.RED}{Colors.BOLD}✗ TESTLERDE ÖNEMLİ BAŞARISIZLIKLAR VAR{Colors.RESET}"
            )

        # Sonuçları kaydet
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        filename = f"kha256_test_results_v3_{timestamp}.json"

        try:
            with open(filename, "w", encoding="utf-8") as f:
                json.dump(all_results, f, indent=2, ensure_ascii=False, default=str)
            print(
                f"\n{Colors.GREEN}✓ Test sonuçları '{filename}' dosyasına kaydedildi{Colors.RESET}"
            )
        except Exception as e:
            print_error(f"Sonuçlar kaydedilemedi: {e}")

        return all_results

    except KeyboardInterrupt:
        print_warning("\nTest kullanıcı tarafından durduruldu.")
        return {"error": "Interrupted by user"}
    except Exception as e:
        print_error(f"Test paketi çalıştırılırken hata oluştu: {e}")
        traceback.print_exc()
        return {"error": str(e)}


class Kha256SecureStorage:
    def __init__(self):
        self.config = FortifiedConfig(
            iterations=3, shuffle_layers=2, salt_length=32, double_hashing=True
        )
        self.hasher = FortifiedKhaHash256(self.config)

    def protect(self, data: bytes) -> bytes:
        """Veriyi salt + len + data + tag ile korur"""
        salt = secrets.token_bytes(32)
        tag_input = data + salt
        tag_str = self.hasher.hash(tag_input, salt)
        tag_bytes = bytes.fromhex(tag_str[:64])  # 32-byte tag

        data_len = struct.pack(">I", len(data))
        return salt + data_len + data + tag_bytes

    def verify(self, stored: bytes) -> tuple[bool, bytes]:
        """Bütünlüğü doğrular, veri döner"""
        salt = stored[:32]
        data_len = struct.unpack(">I", stored[32:36])[0]
        data_start = 36
        data = stored[data_start : data_start + data_len]
        tag = stored[-(32):]

        computed_tag_str = self.hasher.hash(data + salt, salt)
        computed_tag = bytes.fromhex(computed_tag_str[:64])

        return computed_tag == tag, data


def run_comprehensive_test():
    """Kapsamlı güvenlik testi"""
    print("=" * 80)
    print("KHA - KAPSAMLI GÜVENLİK TESTİ")
    print("=" * 80)

    # Hasher oluştur
    hasher = generate_fortified_hasher()

    # Güvenlik raporu
    security_report = hasher.get_security_report()
    print("\nGÜVENLİK RAPORU:")
    print("-" * 40)
    print(f"  Algoritma: {security_report['algorithm']}")
    print(f"  Versiyon: {security_report['version']}")
    print(f"  Güvenlik Seviyesi: {security_report['security_level']}")
    print(f"  Kuantum Direnci: {security_report['features']['quantum_resistance']}")
    print(f"  Bellek Sertleştirme: {security_report['features']['memory_hardening']}")
    print(
        f"  Yan Kanal Koruma: {security_report['features']['side_channel_resistance']}"
    )

    print("\n1. TEMEL FONKSİYON TESTİ")
    print("-" * 40)

    test_cases = [
        ("", "Boş string"),
        ("a", "Tek karakter"),
        ("Merhaba Dünya!", "Basit metin"),
        ("K" * 1000, "Uzun tekrar"),
        (secrets.token_bytes(128), "Rastgele veri (128 byte)"),
        ("İçerik: özel karakterler: áéíóú ñ ç ş ğ ü ö", "Unicode metin"),
    ]

    for data, desc in test_cases:
        if isinstance(data, bytes):
            preview = data[:24].hex() + "..."
        else:
            preview = data[:24] + "..." if len(data) > 24 else data
        salt = secrets.token_bytes(32)
        start = time.perf_counter()
        h = hasher.hash(data, salt)
        elapsed = (time.perf_counter() - start) * 1000

        print(f"  {desc:<30} '{preview}'")
        print(f"    → {h[:56]}... ({elapsed:.2f}ms)")

    print("\n2. AVALANCHE TESTİ (100 örnek)")
    print("-" * 40)

    avalanche_result = hasher.test_avalanche_effect(100)
    print(f"  Ortalama bit değişimi: {avalanche_result['avg_bit_change_percent']:.3f}%")
    print(f"  Standart sapma: {avalanche_result['std_deviation']:.3f}")
    print(f"  Hamming mesafesi: {avalanche_result['avg_hamming_distance']:.1f}")
    print(f"  İdeal aralıkta: {avalanche_result['in_ideal_range']}")
    print(f"  Durum: {avalanche_result['status']}")

    print("\n3. ÇAKIŞMA TESTİ (10000 örnek)")
    print("-" * 40)

    collision_result = hasher.test_collision_resistance(100)  # 10000
    print(f"  Çakışma sayısı: {collision_result['collisions']}")
    print(f"  Çakışma oranı: {collision_result['collision_rate_percent']:.8f}%")
    print(f"  Yakın çakışma: {collision_result['near_collisions']}")
    print(f"  Durum: {collision_result['status']}")

    print("\n4. UNIFORMLUK TESTİ (10000 örnek)")
    print("-" * 40)

    uniformity_result = hasher.test_uniformity(100)  # 10000
    print(f"  Chi-square (bit): {uniformity_result['chi_square_bit']:.1f}")
    print(f"  Chi-square (byte): {uniformity_result['chi_square_byte']:.1f}")
    print(f"  Ortalama run uzunluğu: {uniformity_result['avg_run_length']:.3f}")
    print(f"  Bit uniform mu: {uniformity_result['is_uniform_bit']}")
    print(f"  Byte uniform mu: {uniformity_result['is_uniform_byte']}")
    print(f"  Durum: {uniformity_result['status']}")

    print("\nPERFORMANS ÖZETİ")
    print("-" * 40)

    stats = hasher.get_stats()
    print(f"  Toplam hash: {stats['hash_count']}")
    print(f"  Ortalama süre: {stats.get('avg_time_ms', 0):.2f}ms")
    print(f"  Toplam operasyon: {stats.get('total_operations', 0)}")  # 0
    print(f"  KHA başarı oranı: {stats.get('kha_success_rate', 0):.1f}%")  # 0
    print(f"  Güvenlik kontrolleri: {stats.get('security_checks', 0)}")  # 0

    print("\n" + "=" * 80)
    print("SONUÇ: KHA-256 FORTIFIED")
    print("=" * 80)

    # Final evaluation
    avalanche_ok = avalanche_result["status"] in ["EXCELLENT", "GOOD"]
    collision_ok = collision_result["status"] in ["EXCELLENT", "GOOD"]
    uniformity_ok = uniformity_result["status"] in ["EXCELLENT", "GOOD"]

    if avalanche_ok and collision_ok and uniformity_ok:
        print("✓ TÜM TESTLER BAŞARILI! - ÜRETİME HAZIR!")
        print("✓ Yüksek güvenlik seviyesi sağlandı")
        print("✓ Kuantum ve yan kanal saldırılarına karşı korumalı")
    elif avalanche_ok and collision_ok:
        print("✓ İYİ - Çakışma ve avalanche testleri başarılı")
    else:
        print("⚠ İYİLEŞTİRME GEREKLİ - Bazı testler başarısız")

    return hasher


# ============================================================================
# YARDIMCI FONKSİYONLAR
# ============================================================================


def xor_bytes(a: bytes, b: bytes) -> bytes:
    return bytes(x ^ y for x, y in zip(a, b))


def bytes_to_int(b: bytes) -> int:
    return int.from_bytes(b, "big")


def int_to_bytes(i: int, length: int = 4) -> bytes:
    return i.to_bytes(length, "big")


def secure_compare(a: bytes, b: bytes) -> bool:
    if len(a) != len(b):
        return False
    result = 0
    for x, y in zip(a, b):
        result |= x ^ y
    return result == 0


def simple_hmac(key: bytes, message: bytes) -> bytes:
    block_size = 64
    if len(key) > block_size:
        key = hashlib.sha256(key).digest()
    if len(key) < block_size:
        key = key + b"\x00" * (block_size - len(key))

    o_key_pad = xor_bytes(key, b"\x5c" * block_size)
    i_key_pad = xor_bytes(key, b"\x36" * block_size)

    inner = hashlib.sha256(i_key_pad + message).digest()
    return hashlib.sha256(o_key_pad + inner).digest().hex()


# ============================================================================
# TEST 1: KHAUtils
# ============================================================================


def test_khautils():
    """KHAUtils testi"""
    print_subheader("KHAUtils - Yardımcı Fonksiyonlar")

    try:
        from kha256 import KHAUtils

        tests_passed = 0

        # Test 1: secure_random
        rand1 = KHAUtils.secure_random(32)
        rand2 = KHAUtils.secure_random(32)
        if len(rand1) == 32 and rand1 != rand2:
            print_success("  ✅ secure_random: 32 byte, unique")
            tests_passed += 1
        else:
            print_error("  ❌ secure_random hatalı")

        # Test 2: rotl8
        test_byte = 0b10110010
        rotated = KHAUtils.rotl8(test_byte, 3)
        expected = ((test_byte << 3) | (test_byte >> 5)) & 0xFF
        if rotated == expected:
            print_success("  ✅ rotl8: doğru çalışıyor")
            tests_passed += 1
        else:
            print_error(f"  ❌ rotl8 hatalı: {rotated} != {expected}")

        # Test 3: rotl64 / rotr64
        test_val = 0x123456789ABCDEF0
        rotl = KHAUtils.rotl64(test_val, 13)
        rotr = KHAUtils.rotr64(rotl, 13)
        if rotr == test_val:
            print_success("  ✅ rotl64/rotr64: tersinir")
            tests_passed += 1
        else:
            print_error("  ❌ rotl64/rotr64 hatalı")

        # Test 4: bytes_to_words64
        test_bytes = b"12345678" * 3
        words = KHAUtils.bytes_to_words64(test_bytes)
        if len(words) == 3 and all(isinstance(w, int) for w in words):
            print_success("  ✅ bytes_to_words64: doğru çalışıyor")
            tests_passed += 1
        else:
            print_error("  ❌ bytes_to_words64 hatalı")

        # Test 5: bytes_to_words32
        words32 = KHAUtils.bytes_to_words32(test_bytes)
        if len(words32) == 6 and all(isinstance(w, int) for w in words32):
            print_success("  ✅ bytes_to_words32: doğru çalışıyor")
            tests_passed += 1
        else:
            print_error("  ❌ bytes_to_words32 hatalı")

        # Test 6: bit_diff
        h1 = "0000000000000000000000000000000000000000000000000000000000000000"
        h2 = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
        diff = KHAUtils.bit_diff(h1, h2)
        if diff == 256:
            print_success("  ✅ bit_diff: doğru çalışıyor")
            tests_passed += 1
        else:
            print_error(f"  ❌ bit_diff hatalı: {diff} != 256")

        # Test 7: Manuel XOR
        b1 = b"test1234"
        b2 = b"abcd1234"
        xored = xor_bytes(b1, b2)
        if len(xored) == 8:
            print_success("  ✅ xor_bytes (manuel): çalışıyor")
            tests_passed += 1

        # Test 8: Manuel byte/int dönüşümü
        test_int = 123456789
        test_bytes = int_to_bytes(test_int, 8)
        recovered = bytes_to_int(test_bytes)
        if recovered == test_int:
            print_success("  ✅ byte/int dönüşümü (manuel): çalışıyor")
            tests_passed += 1

        return tests_passed >= 6

    except Exception as e:
        print_error(f"  ❌ KHAUtils test hatası: {e}")
        return False


# ============================================================================
# TEST 2: CoreHash
# ============================================================================


def test_core_hash():
    """CoreHash testi"""
    print_subheader("CoreHash - Çekirdek Hash")

    try:
        from kha256 import CoreHash

        data = secrets.token_bytes(64)
        salt = secrets.token_bytes(32)

        # CoreHash
        start = time.perf_counter()
        hash1 = CoreHash.hash(data, salt)
        time1 = (time.perf_counter() - start) * 1000

        # Deterministic test
        hash2 = CoreHash.hash(data, salt)

        if isinstance(hash1, bytes) and len(hash1) == 32:
            print_success(f"  ✅ CoreHash: 32 byte output ({time1:.2f}ms)")
        else:
            print_error(f"  ❌ CoreHash output: {type(hash1)}")

        if hash1 == hash2:
            print_success("  ✅ CoreHash: deterministic")
        else:
            print_error("  ❌ CoreHash: deterministic DEĞİL")

        return True

    except Exception as e:
        print_error(f"  ❌ CoreHash test hatası: {e}")
        return False


# ============================================================================
# TEST 3: DeterministicHash / DeterministicEngine
# ============================================================================


def test_deterministic_hash():
    """DeterministicHash / DeterministicEngine testi"""
    print_subheader("DeterministicHash / DeterministicEngine")

    try:
        from kha256 import DeterministicEngine, DeterministicHash

        data = b"test deterministic data"
        salt = secrets.token_bytes(32)

        # DeterministicHash
        start = time.perf_counter()
        hash1 = DeterministicHash.hash(data + salt)
        time1 = (time.perf_counter() - start) * 1000

        # Deterministic test
        hash2 = DeterministicHash.hash(data + salt)

        # Unique test
        salt2 = secrets.token_bytes(32)
        hash3 = DeterministicHash.hash(data + salt2)

        if isinstance(hash1, bytes) and len(hash1) == 32:
            print_success(f"  ✅ DeterministicHash: 32 byte ({time1:.2f}ms)")
        else:
            print_error(f"  ❌ DeterministicHash output: {type(hash1)}")

        if hash1 == hash2:
            print_success("  ✅ DeterministicHash: deterministic")
        else:
            print_error("  ❌ DeterministicHash: deterministic DEĞİL")

        if hash1 != hash3:
            print_success("  ✅ DeterministicHash: farklı salt → farklı hash")
        else:
            print_error("  ❌ DeterministicHash: salt etkisiz")

        # DeterministicEngine
        try:
            hash4 = DeterministicEngine.hash(data + salt)
            if isinstance(hash4, bytes) and len(hash4) == 32:
                print_success("  ✅ DeterministicEngine çalışıyor")
                if hash4 == hash1:
                    print_success("    ✅ DeterministicEngine ile uyumlu")
                else:
                    print_warning("    ⚠️ DeterministicEngine farklı output")
        except AttributeError:
            print_info("  ℹ️ DeterministicEngine test edilemedi")
        except Exception as e:
            print_warning(f"  ⚠️ DeterministicEngine hatası: {e}")

        return True

    except Exception as e:
        print_error(f"  ❌ DeterministicHash test hatası: {e}")
        return False


# ============================================================================
# TEST 4: MemoryHardHash
# ============================================================================


def test_memory_hard_hash():
    """MemoryHardHash testi"""
    print_subheader("MemoryHardHash")

    try:
        from kha256 import MemoryHardHash

        data = secrets.token_bytes(32)
        salt = secrets.token_bytes(32)

        times = {}
        for mb in [1, 2, 4]:
            start = time.perf_counter()
            hash_val = MemoryHardHash(mb).hash(data, salt)
            duration = (time.perf_counter() - start) * 1000
            times[mb] = duration
            print(f"  📊 {mb}MB: {duration:.2f}ms - {hash_val[:16]}...")

        scale_2_1 = times[2] / times[1] if times[1] > 0 else 0
        scale_4_2 = times[4] / times[2] if times[2] > 0 else 0

        print(f"  📈 Scaling: 2/1={scale_2_1:.2f}x, 4/2={scale_4_2:.2f}x")

        if 1.5 <= scale_2_1 <= 2.5 and 1.5 <= scale_4_2 <= 2.5:
            print_success("  ✅ MEMORY-HARD DOĞRULANDI!")
        else:
            print_warning("  ⚠️ Scaling beklendiği gibi değil")

        return True

    except Exception as e:
        print_error(f"  ❌ MemoryHardHash test hatası: {e}")
        return False


# ============================================================================
# TEST 5: MemoryHardEngine (YENİ!)
# ============================================================================


def test_memory_hard_engine():
    """MemoryHardEngine testi - YENİ EKLENDİ"""
    print_subheader("MemoryHardEngine - Memory-Hard Engine")

    try:
        from kha256 import MemoryHardEngine

        data = secrets.token_bytes(32)
        salt = secrets.token_bytes(32)

        # Test 1: Temel çalışma
        engine = MemoryHardEngine(memory_mb=2)
        start = time.perf_counter()
        hash1 = engine.hash(data, salt)
        time1 = (time.perf_counter() - start) * 1000

        # Output kontrolü
        if isinstance(hash1, str) and len(hash1) == 64:
            print_success(f"  ✅ MemoryHardEngine: 64 char hex ({time1:.2f}ms)")
            print(f"     Hash: {hash1[:32]}...")
        else:
            print_error(f"  ❌ MemoryHardEngine output: {type(hash1)}")

        # Test 2: Deterministic
        hash2 = engine.hash(data, salt)
        if hash1 == hash2:
            print_success("  ✅ MemoryHardEngine: deterministic")
        else:
            print_error("  ❌ MemoryHardEngine: deterministic DEĞİL")

        # Test 3: Farklı memory miktarı
        engine2 = MemoryHardEngine(memory_mb=4)
        start = time.perf_counter()
        engine2.hash(data, salt)
        time2 = (time.perf_counter() - start) * 1000

        print(f"  📊 2MB: {time1:.2f}ms, 4MB: {time2:.2f}ms")

        # Test 4: Properties
        if hasattr(engine, "memory_mb"):
            print_success(f"  ✅ memory_mb property: {engine.memory_mb}")

        if hasattr(engine, "metrics"):
            print_success(f"  ✅ metrics: {engine.metrics}")

        return True

    except ImportError:
        print_warning("  ⚠️ MemoryHardEngine bulunamadı (kha256'da yok)")
        return False
    except Exception as e:
        print_error(f"  ❌ MemoryHardEngine test hatası: {e}")
        return False


# ============================================================================
# TEST 6: KHA256
# ============================================================================


def test_kha256_main():
    """KHA256 ana sınıf testi"""
    print_subheader("KHA256 - Ana Hash Sınıfı")

    try:
        from kha256 import KHA256

        hasher = KHA256()
        data = b"test data"
        salt = secrets.token_bytes(32)

        # Test 1: Normal mod
        start = time.perf_counter()
        hash1 = hasher.hash(data, salt)
        time1 = (time.perf_counter() - start) * 1000

        hash2 = hasher.hash(data, salt)

        if len(hash1) == 64 and isinstance(hash1, str):
            print_success(f"  ✅ Normal mod: 64 char hex ({time1:.2f}ms)")
        else:
            print_error(f"  ❌ Normal mod output: {type(hash1)}")

        if hash1 == hash2:
            print_success("  ✅ Normal mod: deterministic")
        else:
            print_error("  ❌ Normal mod: deterministic DEĞİL")

        # Test 2: Deterministic mod
        try:
            hash_det = hasher.hash(data, salt, deterministic=True)
            if len(hash_det) == 64:
                print_success("  ✅ Deterministic mod çalışıyor")
        except Exception as e:
            print_warning(f"  ⚠️ Deterministic mod: {e}")

        # Test 3: Memory-hard mod
        try:
            hash_mh = hasher.hash(data, salt, memory_hard=True, memory_mb=2)
            if len(hash_mh) == 64:
                print_success("  ✅ Memory-hard mod çalışıyor")
        except Exception as e:
            print_warning(f"  ⚠️ Memory-hard mod: {e}")

        return True

    except Exception as e:
        print_error(f"  ❌ KHA256 test hatası: {e}")
        return False


# ============================================================================
# TEST 7: KHA256b
# ============================================================================


def test_kha256b():
    """KHA256b - Perfect Avalanche sınıfı"""
    print_subheader("KHA256b - Perfect Avalanche Sınıfı")

    try:
        from kha256 import KHA256b

        hasher = KHA256b()
        data = b"test data"
        salt = secrets.token_bytes(32)

        # Version/certificate
        if hasattr(hasher, "version"):
            print(f"  📋 Version: {hasher.version}")
        if hasattr(hasher, "certificate"):
            print(f"  📋 Certificate: {hasher.certificate}")
        if hasattr(hasher, "avalanche_score"):
            print(f"  📋 Avalanche score: {hasher.avalanche_score}")

        # Test 1: Normal mod
        start = time.perf_counter()
        hash1 = hasher.hash(data, salt)
        time1 = (time.perf_counter() - start) * 1000

        hash2 = hasher.hash(data, salt)

        if len(hash1) == 64:
            print_success(f"  ✅ Normal mod: {time1:.2f}ms")

        if hash1 == hash2:
            print_success("  ✅ Normal mod: deterministic")

        # Test 2: Deterministic mod
        try:
            hash_det = hasher.hash(data, salt, deterministic=True)
            if len(hash_det) == 64:
                print_success("  ✅ Deterministic mod çalışıyor")
        except Exception as e:
            print_warning(f"  ⚠️ Deterministic mod: {e}")

        # Test 3: Memory-hard mod
        try:
            hash_mh = hasher.hash(data, salt, memory_hard=True, memory_mb=2)
            if len(hash_mh) == 64:
                print_success("  ✅ Memory-hard mod çalışıyor")
        except Exception as e:
            print_warning(f"  ⚠️ Memory-hard mod: {e}")

        # Test 4: Metrics
        if hasattr(hasher, "metrics"):
            metrics = hasher.metrics
            print_success(f"  ✅ Metrics: {metrics}")

        return True

    except Exception as e:
        print_error(f"  ❌ KHA256b test hatası: {e}")
        return False


# ============================================================================
# TEST 8: StreamingKHA256
# ============================================================================


def test2_streaming():
    """StreamingKHA256 testi"""
    print_subheader("StreamingKHA256 - Streaming Mode")

    try:
        from kha256 import KHA256, StreamingKHA256

        test_data = secrets.token_bytes(1024)
        salt = secrets.token_bytes(32)

        # Streaming ile hash
        stream = StreamingKHA256(salt)
        stream.update(test_data)
        hash1 = stream.hexdigest()

        # Tek seferde hash
        hasher = KHA256()
        hash2 = hasher.hash(test_data, salt)

        print(f"  🔑 Streaming: {hash1[:32]}...")
        print(f"  🔑 Normal:    {hash2[:32]}...")

        if len(hash1) == 64 and isinstance(hash1, str):
            print_success("  ✅ Streaming çalışıyor")

            # Reset test
            stream.reset()
            stream.update(test_data)
            hash3 = stream.hexdigest()
            if hash1 == hash3:
                print_success("  ✅ Reset çalışıyor")
        else:
            print_error("  ❌ Streaming hatalı output")

        print_info("  ℹ️ Streaming vs normal: farklı olabilir")

        return True

    except Exception as e:
        print_error(f"  ❌ Streaming test hatası: {e}")
        return False


# ============================================================================
# TEST 9: Avalanche
# ============================================================================


def test2_avalanche():
    """Avalanche etkisi testi"""
    print_subheader("Avalanche Testi - Çığ Etkisi")

    try:
        from kha256 import KHA256

        hasher = KHA256()

        data1 = b"test data for avalanche"
        data2 = bytearray(data1)
        data2[0] ^= 1
        data2 = bytes(data2)

        salt = secrets.token_bytes(32)

        hash1 = hasher.hash(data1, salt)
        hash2 = hasher.hash(data2, salt)

        try:
            from kha256 import KHAUtils

            diff_count = KHAUtils.bit_diff(hash1, hash2)
        except BaseException:
            b1 = bytes.fromhex(hash1)
            b2 = bytes.fromhex(hash2)
            diff_count = sum(bin(a ^ b).count("1") for a, b in zip(b1, b2))

        diff_percent = (diff_count / 256) * 100

        print(f"  📊 Farklı bit sayısı: {diff_count}/256 ({diff_percent:.1f}%)")

        if 100 <= diff_count <= 156:
            print_success(f"  ✅ Avalanche etkisi: {diff_percent:.1f}%")
            return True
        else:
            print_warning(f"  ⚠️ Avalanche zayıf: {diff_percent:.1f}%")
            return False

    except Exception as e:
        print_error(f"  ❌ Avalanche test hatası: {e}")
        return False


# ============================================================================
# TEST 10: HMAC
# ============================================================================


def test2_hmac():
    """HMAC testi"""
    print_subheader("HMAC Testi")

    try:
        from kha256 import KHA256

        kha = KHA256()

        test_cases = [
            (b"key", b"The quick brown fox jumps over the lazy dog"),
            (b"secret", b"Hello, World!"),
            (secrets.token_bytes(32), b"KHA-256 HMAC Test"),
            (b"", b"Empty key test"),
            (b"x" * 128, b"Long key test"),
        ]

        print("  HMAC Test Sonuçları:")

        for i, (key, msg) in enumerate(test_cases, 1):
            try:
                if hasattr(kha, "hmac"):
                    hmac_result = kha.hmac(key, msg)
                else:
                    hmac_result = simple_hmac(key, msg)

                length_ok = len(hmac_result) == 64
                hex_ok = (
                    all(c in "0123456789abcdef" for c in hmac_result)
                    if isinstance(hmac_result, str)
                    else False
                )

                print(f"\n    Test {i}:")
                print(f"      HMAC: {hmac_result[:16]}...{hmac_result[-16:]}")
                print(f"      ✓ Uzunluk: {length_ok}")
                print(f"      ✓ Hex format: {hex_ok}")

            except Exception as e:
                print_warning(f"    Test {i} atlandı: {e}")

        return True

    except Exception as e:
        print_error(f"  ❌ HMAC test hatası: {e}")
        return False


# ============================================================================
# TEST 11: TrueMemoryHardHasher
# ============================================================================


def test_true_memory_hard():
    """TrueMemoryHardHasher testi"""
    print_subheader("TrueMemoryHardHasher")

    try:
        from kha256 import TrueMemoryHardHasher

        data = secrets.token_bytes(32)
        salt = secrets.token_bytes(32)

        times = {}
        for mb in [1, 2, 4]:
            hasher = TrueMemoryHardHasher(
                memory_cost_kb=mb * 1024, time_cost=3, parallelism=1
            )
            start = time.perf_counter()
            h = hasher.hash(data, salt)
            duration = (time.perf_counter() - start) * 1000
            times[mb] = duration
            print(f"  📊 {mb}MB: {duration:.2f}ms - {h[:16]}...")

        scale_2_1 = times[2] / times[1]
        scale_4_2 = times[4] / times[2]
        print(f"  📈 Scaling: 2/1={scale_2_1:.2f}x, 4/2={scale_4_2:.2f}x")

        if 1.5 <= scale_2_1 <= 2.5 and 1.5 <= scale_4_2 <= 2.5:
            print_success("  ✅ MEMORY-HARD DOĞRULANDI!")
            return True
        else:
            print_warning("  ⚠️ Scaling beklendiği gibi değil")
            return False

    except Exception as e:
        print_error(f"  ❌ TrueMemoryHardHasher test hatası: {e}")
        return False


# ============================================================================
# TEST 12: FortifiedKhaHash256
# ============================================================================


def test_fortified_memory_hard2():
    """FortifiedKhaHash256 testi"""
    print_subheader("FortifiedKhaHash256")

    try:
        from kha256 import FortifiedConfig, FortifiedKhaHash256

        data = secrets.token_bytes(32)
        salt = secrets.token_bytes(32)

        times = {}
        for mb in [1, 2, 4]:
            config = FortifiedConfig()
            config.enable_memory_hard_mode = True
            config.memory_cost_kb = mb * 1024
            config.time_cost = 3
            hasher = FortifiedKhaHash256(config=config, deterministic=False)

            start = time.perf_counter()
            h = hasher.hash(data, salt)
            duration = (time.perf_counter() - start) * 1000
            times[mb] = duration
            print(f"  📊 {mb}MB: {duration:.2f}ms - {h[:16]}...")

        scale_2_1 = times[2] / times[1]
        scale_4_2 = times[4] / times[2]
        print(f"  📈 Scaling: 2/1={scale_2_1:.2f}x, 4/2={scale_4_2:.2f}x")

        if 1.5 <= scale_2_1 <= 2.5 and 1.5 <= scale_4_2 <= 2.5:
            print_success("  ✅ MEMORY-HARD DOĞRULANDI!")
            return True
        else:
            print_warning("  ⚠️ Scaling beklendiği gibi değil")
            return False

    except Exception as e:
        print_error(f"  ❌ FortifiedKhaHash256 test hatası: {e}")
        return False

def plot_avalanche_simple(bit_diffs):
    """Basit avalanche grafiği"""
    import matplotlib.pyplot as plt
    import numpy as np
    
    plt.figure(figsize=(15, 5))
    
    # Histogram
    plt.subplot(1, 2, 1)
    plt.hist(bit_diffs, bins=25, alpha=0.7, color='steelblue', edgecolor='black')
    plt.axvline(128, color='red', linestyle='--', linewidth=2, label='İdeal (128)')
    plt.axvline(np.mean(bit_diffs), color='darkgreen', linestyle='-', linewidth=2, 
                label=f'Ortalama ({np.mean(bit_diffs):.2f})')
    plt.xlabel('Farklı Bit Sayısı')
    plt.ylabel('Frekans')
    plt.title('Avalanche Dağılımı')
    plt.legend()
    plt.grid(alpha=0.3)
    
    # Box plot
    plt.subplot(1, 2, 2)
    bp = plt.boxplot(bit_diffs, vert=True, patch_artist=True)
    bp['boxes'][0].set_facecolor('lightblue')
    plt.axhline(128, color='red', linestyle='--', label='İdeal')
    plt.axhline(np.mean(bit_diffs), color='green', linestyle='-', label='Ortalama')
    plt.ylabel('Farklı Bit Sayısı')
    plt.title('Kutu Grafiği')
    plt.legend()
    plt.grid(alpha=0.3)
    
    plt.tight_layout()
    plt.show()

def kha_rastgele_sayi(min_deger=0, max_deger=100):
    # Sistem saati + PID + dosya yolunu karıştır
    seed = str(time.time_ns()) + str(os.getpid()) + str(os.getcwd())
    
    # KHA256 ile hash (salt=None ile otomatik salt)
    hasher = KHA256()
    hash_result = hasher.hash(seed, salt=None)  # salt=None = otomatik salt
    
    # Hex string'i integer'a çevir ve mod al
    hash_int = int(hash_result, 16)
    rastgele = (hash_int % (max_deger - min_deger + 1)) + min_deger
    return rastgele

def rastgele_sayi(min_deger=0, max_deger=100):
    # Sistem saati + PID + dosya yolunu karıştır
    seed = str(time.time_ns()) + str(os.getpid()) + str(os.getcwd())
    # Hash'i mod alarak rastgele sayı üret
    hash_deger = hash(seed)
    rastgele = abs(hash_deger) % (max_deger - min_deger + 1) + min_deger
    return rastgele

def kha256_hard_random(min_deger=0, max_deger=100):
    hasher = KHA256()
    # TrueMemoryHardHasher = ASIC-resistant, en güçlü
    hash_result = hasher.hash(
        str(time.time_ns()), 
        salt=None,  # Otomatik secure_random salt
        memory_hard=True,
        memory_mb=16  # 16MB memory-hard
    )
    hash_int = int(hash_result, 16)
    return (hash_int % (max_deger - min_deger + 1)) + min_deger

def kha256_memory_hard_random(min_deger=0, max_deger=100):
    hasher = KHA256()
    seed = f"{time.time_ns()}_{os.getpid()}"
    result = hasher.hash(
        seed,
        salt=None,
        memory_hard=True,
        memory_mb=32  # 32MB ASIC koruması
    )
    return int(result, 16) % (max_deger - min_deger + 1) + min_deger


def kha256_password_random(password: str, min_deger=0, max_deger=100):
    # Saf Python salt (16+ byte gerekli)
    #salt = b"fixed_16byte_salt!!"  # Min 16 byte
    salt = os.urandom(64)
    
    # hash_password(data, salt) - KHA256 Scrypt wrapper
    hash_result = hash_password(
        data=password.encode(),
        salt=salt,
        is_usb_key=True  # 128MB memory-hard varsayılan
    )
    
    # "KHA256-USB$salt$digest" formatından sadece digest al
    _, _, digest = hash_result.split('$')
    return int(digest, 16) % (max_deger - min_deger + 1) + min_deger

def kha256_fortified_random(min_deger=0, max_deger=100):
    hasher = KHA256()
    seed = str(time.time_ns())
    
    # Zincirleme fortified hash
    result1 = hasher.hash(seed, memory_hard=True, memory_mb=64)
    result2 = hasher.hash(result1, memory_hard=True, memory_mb=64)
    
    final_int = int(result2, 16)
    return (final_int % (max_deger - min_deger + 1)) + min_deger

def true_memory_hard_random(min_deger=0, max_deger=100):
    salt = os.urandom(64)
    seed = str(time.time_ns()).encode()
    hasher = TrueMemoryHardHasher(memory_cost_kb=65556, time_cost=3)  # Direkt sınıf!
    result = hasher.hash(seed, salt)  # Kendi hash metodu
    return int(result, 16) % (max_deger - min_deger + 1) + min_deger

## 2. MemoryHardHash 
def memory_hard_hash_random(min_deger=0, max_deger=100):
    salt = os.urandom(64)
    seed = f"{time.time_ns()}_{os.getpid()}".encode()
    hasher = MemoryHardHash(memory_mb=32)
    result = hasher.hash(seed, salt)
    return int(result, 16) % (max_deger - min_deger + 1) + min_deger

def memory_hard_engine_random(min_deger=0, max_deger=100):
    seed = str(time.time_ns()).encode()
    #salt = b"fixed_engine_salt_32"  # 32 byte salt ZORUNLU
    salt = os.urandom(32)
    
    engine = MemoryHardEngine(memory_mb=128, iterations=3)
    result = engine.hash(seed, salt)  # Direkt str döner (hex)
    
    hash_int = int(result, 16)
    return hash_int % (max_deger - min_deger + 1) + min_deger

## 4. FortifiedKhaHash256 (Zincirleme)
def fortified_kha_random(min_deger=0, max_deger=100):
    salt = os.urandom(64)
    seed = str(time.time_ns()).encode()
    fortified = FortifiedKhaHash256()
    result = fortified.hash(seed, salt)  # Paranoid mod
    return int(result, 16) % (max_deger - min_deger + 1) + min_deger

## 5. KHA256b (İkinci varyant)
def kha256b_random(min_deger=0, max_deger=100):
    hasher = KHA256b()
    result = hasher.hash(str(time.time_ns()), memory_hard=True, memory_mb=16)
    return int(result, 16) % (max_deger - min_deger + 1) + min_deger

def scrypt_random(min_deger=0, max_deger=100):
    # SAF PYTHON - her çağrı benzersiz
    timestamp = str(time.time_ns())
    pid = str(os.getpid())
    seed_data = (timestamp + pid).encode()
    salt = (timestamp[::-1] + pid).encode()[:64]  # 64 byte salt
    
    result = hash_password(seed_data, salt, is_usb_key=True)
    _, _, digest = result.split('$')
    return int(digest, 16) % (max_deger - min_deger + 1) + min_deger

## Global Counter (En Güvenli)
counter = 0
def gscrypt_random(min_deger=0, max_deger=100):
    global counter
    seed = f"{time.time_ns()}_{os.getpid()}_{counter}".encode()
    salt = f"{counter}_{time.time_ns()}".encode()[:64]
    counter += 1
    
    result = hash_password(seed, salt, is_usb_key=True)
    _, _, digest = result.split('$')
    return int(digest, 16) % (max_deger - min_deger + 1) + min_deger

def bscrypt_random(min_deger=0, max_deger=100):
    # aynı girdi, aynı çıktı verir
    #salt = b"scrypt_salt_16!!"
    #password = b"a"
    # farklı girdi , farklı çıktıverir
    salt = os.urandom(64)
    password = os.urandom(16)    
    result = hash_password(password, salt)
    _, _, digest = result.split('$')
    return int(digest, 16) % (max_deger - min_deger + 1) + min_deger

def scrypt_dual_output(min_deger=0, max_deger=100):
    counter = str(time.time_ns() * 1000 + os.getpid() * 1000000)
    
    # SABİT (test/verification)
    sabit_result = hash_password(b"a", b"scrypt_salt_16!!", is_usb_key=True)
    sabit_sayi = int(sabit_result.split('$')[2], 16) % (max_deger - min_deger + 1) + min_deger
    
    # RASTGELE (gerçek kullanım)
    rastgele_password = (counter + str(os.times().elapsed)).encode()[:64]
    rastgele_salt = (str(os.getcwd()) + counter[::-1]).encode()[:64]
    rastgele_result = hash_password(rastgele_password, rastgele_salt, is_usb_key=True)
    rastgele_sayi = int(rastgele_result.split('$')[2], 16) % (max_deger - min_deger + 1) + min_deger
    
    return sabit_sayi, rastgele_sayi


def run_all_tests():
    """Bütün memory-hard sınıfı testlerini çalıştırır"""

    if is_jupyter():
        clear_output(wait=True)

    print_header("🧪 KHA-256 bütün Sınıflar Testi", 70)
    print(f"Tarih: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    print(f"Jupyter: {'✅' if is_jupyter() else '❌'}")
    print(f"{'=' * 70}")

    summary = {}

    # TEST 1: KHAUtils
    print_header("🔧 TEST 1: KHAUtils", 70)
    summary["KHAUtils"] = test_khautils()

    # TEST 2: CoreHash
    print_header("⚙️ TEST 2: CoreHash", 70)
    summary["CoreHash"] = test_core_hash()

    # TEST 3: DeterministicHash
    print_header("🎯 TEST 3: DeterministicHash", 70)
    summary["DeterministicHash"] = test_deterministic_hash()

    # TEST 4: MemoryHardHash
    print_header("🧠 TEST 4: MemoryHardHash", 70)
    summary["MemoryHardHash"] = test_memory_hard_hash()

    # TEST 5: MemoryHardEngine (YENİ!)
    print_header("⚙️🧠 TEST 5: MemoryHardEngine", 70)
    summary["MemoryHardEngine"] = test_memory_hard_engine()

    # TEST 6: KHA256
    print_header("📦 TEST 6: KHA256", 70)
    summary["KHA256"] = test_kha256_main()

    # TEST 7: KHA256b
    print_header("✨ TEST 7: KHA256b", 70)
    summary["KHA256b"] = test_kha256b()

    # TEST 8: StreamingKHA256
    print_header("🌊 TEST 8: StreamingKHA256", 70)
    summary["Streaming"] = test2_streaming()

    # TEST 9: Avalanche
    print_header("🔄 TEST 9: Avalanche", 70)
    summary["Avalanche"] = test2_avalanche()

    # TEST 10: HMAC
    print_header("🔐 TEST 10: HMAC", 70)
    summary["HMAC"] = test2_hmac()

    # TEST 11: TrueMemoryHardHasher
    print_header("🏔️ TEST 11: TrueMemoryHardHasher", 70)
    summary["TrueMemoryHardHasher"] = test_true_memory_hard()

    # TEST 12: FortifiedKhaHash256
    print_header("🛡️ TEST 12: FortifiedKhaHash256", 70)
    summary["FortifiedKhaHash256"] = test_fortified_memory_hard2()

    # ========== ÖZET RAPOR ==========
    print_header("📊 KHA-256 Test Raporu", 70)

    print(f"\n{'=' * 70}")
    print(f"{'TEST':<40} {'DURUM':<20}")
    print(f"{'=' * 70}")

    for test_name, passed in summary.items():
        status = "✅ PASS" if passed else "❌ FAIL"
        color = Colors.GREEN if passed else Colors.RED
        name_display = f"{test_name:<38}"
        print(f"  {name_display} {color}{status}{Colors.RESET}")

    print(f"{'=' * 70}")

    total_tests = len(summary)
    passed_tests = sum(1 for v in summary.values() if v)

    print(f"\n🎯 Genel Sonuç: {passed_tests}/{total_tests} testi Geçti")

    if passed_tests == total_tests:
        print(
            f"\n{Colors.GREEN}{Colors.BOLD}✅ Mükemmel! Bütün KHA-256 Sınıfları Çalışıyor!{Colors.RESET}"
        )
    elif passed_tests >= total_tests - 2:
        print(
            f"\n{Colors.YELLOW}{Colors.BOLD}⚠️ Çoğu test geçti, küçük sorunlar var{Colors.RESET}"
        )
    else:
        print(f"\n{Colors.RED}{Colors.BOLD}❌ Önemli sorunlar var{Colors.RESET}")

    print(f"{'=' * 70}")

    return summary


# ============================================================
# ÖRNEK KULLANIM
# ============================================================
if __name__ == "__main__":
    print("🔒 Keçeci Hash Algoritması (KHA-256) - FORTIFIED Version")
    print("   Salt zorunlu • USB varsayılan • 2026 güvenli\n")

    # Temel test
    kha = KHA256()
    salt = KHAUtils.secure_random(32)

    h1 = kha.hash("KHA-256 Test 1", salt)
    h2 = kha.hash("KHA-256 Test 2", salt)

    print(f"\n📝 Hash 1: {h1[:16]}...{h1[-16:]}")
    print(f"📝 Hash 2: {h2[:16]}...{h2[-16:]}")

    diff = KHAUtils.bit_diff(h1, h2)
    print(f"🔍 Bit Farkı: {diff}/256")

    # Avalanche test
    avg = test_avalanche(kha, 200)
    print(f"\n🌋 Avalanche (200 test): {avg:.2f}/128.00 (%{avg / 128:.2%})")

    # HMAC test
    test_hmac()

    # Streaming test - KESİN ÇÖZÜM
    test_streaming()

    # SABİT test salt'ı - DEMO İÇİN (32 byte - NIST uyumlu)
    fixed_salt = b"KHA_DEMO_SALT_32BYTES!!"  # 32 byte sabit salt

    if len(sys.argv) > 1 and sys.argv[1] == "--test":
        hasher = run_comprehensive_test()
    else:
        print("⚡ HIZLI DEMO:\n")

        # 1. Basit hasher
        hasher = SimpleKhaHasher()

        # Örnek 1: Basit metin - HER ZAMAN AYNI SONUÇ!
        text = "Merhaba dünya! KHA test"
        hash_result = hasher.hash(text, fixed_salt)
        print(f"📄 '{text}'")
        print(f"🔑 → {hash_result}\n")  # Her çalıştırmada aynı hash

        # Örnek 2: Şifre - HER ZAMAN AYNI SONUÇ!
        password = "ÇokGizliŞifre123!@#"
        password_hash = hash_password_str(password, fixed_salt)
        print(f"🔐 '{password}'")
        print(f"🔑 → {password_hash[:64]}...\n")  # Her çalıştırmada aynı

        # Örnek 3: Avalanche testi - AYNI SALT ŞART!
        print("🔥 AVALANCHE TEST:")
        data1, data2 = "Test123", "Test124"
        h1 = hasher.hash(data1, fixed_salt)  # Aynı salt
        h2 = hasher.hash(data2, fixed_salt)  # Aynı salt

        h1_bin = bin(int(h1.split("$")[-1], 16))[2:].zfill(256)
        h2_bin = bin(int(h2.split("$")[-1], 16))[2:].zfill(256)
        diff = sum(a != b for a, b in zip(h1_bin, h2_bin))

        print(f"  '{data1}' → {h1[:32]}...")
        print(f"  '{data2}' → {h2[:32]}...")
        print(f"  Bit farkı: {diff}/256 (%{diff / 2.56:.1f}) ✅\n")

        # Örnek 4: Performans testi
        print("⏱️  PERFORMANS TESTİ:")
        test_data = "Performans testi" * 100
        start = time.time()
        result = hasher.hash(test_data, fixed_salt)
        duration = (time.time() - start) * 1000

        print(f"  2000+ char → {duration:.1f}ms ✅")

        # Güvenlik raporu
        print("\n🛡️  GÜVENLİK RAPORU:")
        report = hasher.get_security_report()
        print(f"  Versiyon: {report['version']}")
        for key, value in report["features"].items():
            status = "✓" if value else "✗"
            print(f"  {key.replace('_', ' ').title()}: {status}")

        print("\n🚀 Kullanım: python kha.py --test")

    try:
        if not is_jupyter():
            print(
                f"{Colors.CYAN}{Colors.BOLD}KHA-256 TÜM SINIFLAR TESTİ v5.2{Colors.RESET}"
            )
            response = input("\nTüm testleri çalıştırmak için ENTER: ")
            if response.lower() == "q":
                print_info("Test iptal edildi.")
                sys.exit(0)

        results = run_all_tests()
        print_success("\n✅ Tüm testler tamamlandı!")

    except KeyboardInterrupt:
        print_warning("\nProgram durduruldu.")
        sys.exit(130)
    except Exception as e:
        print_error(f"Hata: {e}")
        traceback.print_exc()
        sys.exit(1)
