"""
60-second SHA-256 mining speed test using direct hashlib implementation
Optimized for maximum hash rate using virtual CPU parallel processing
"""
from virtual_cpu import (
    CPU, Memory, MEMORY_SIZE, THREADS_PER_CORE
)
import hashlib
import time
import struct
import logging
from datetime import datetime
import threading
from concurrent.futures import ThreadPoolExecutor
import numpy as np

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('mining_stats.log'),
        logging.StreamHandler()
    ]
)

def sha256d_hash(data):
    """Double SHA-256 hash"""
    return hashlib.sha256(hashlib.sha256(data).digest()).digest()

def mine_chunk(chunk_size=1000000):
    """Mine a chunk of nonces"""
    # Pre-allocate block header
    version = 2
    prev_block = b'0' * 32  # Previous block hash
    merkle_root = b'1' * 32  # Merkle root
    timestamp = int(time.time())
    bits = 0x1d00ffff
    
    # Use numpy for fast array operations
    nonces = np.arange(chunk_size, dtype=np.uint32)
    hashes = np.zeros(chunk_size, dtype=np.uint32)
    
    # Pre-build the block header template
    header_template = struct.pack('<L32s32sLL', 
                                version, prev_block, merkle_root, 
                                timestamp, bits)
    
    # Process nonces in vectorized form
    for i in range(chunk_size):
        block_header = header_template + struct.pack('<L', nonces[i])
        hashes[i] = int.from_bytes(sha256d_hash(block_header)[:4], 'little')
    
    return chunk_size

def run_mining_test(duration=60):
    """Run intensive SHA-256 mining test for specified duration"""
    logging.info(f"Starting {duration}-second intensive SHA-256 mining test")

    # Main SHA-256 compression function operations
      i in range(64):
         

def run_mining_test(duration=60):
    """Run SHA-256 mining test for specified duration"""
    logging.info(f"Initializing Virtual CPU for {duration}-second SHA-256 mining test")
    cpu = CPU(num_cores=4, clock_freq=2000)
    
    # Create mining program
    mining_program = create_mining_instruction_block()
    
    # Test variables
    start_time = time.time()
    end_time = start_time + duration
    hashes_completed = 0
    nonce = 0
    last_log_time = start_time
    last_hashes = 0
    
    # Prepare initial block header (80 bytes)
    version = 2
    prev_block = bytes.fromhex('0000000000000000000000000000000000000000000000000000000000000000')
    merkle_root = bytes.fromhex('4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b')
    timestamp = int(time.time())
    bits = 0x1d00ffff
    
    logging.info(f"Starting mining with {len(cpu.cores)} cores and {THREADS_PER_CORE} threads per core")
    logging.info(f"Initial clock frequency: {cpu.clock.current_frequency} Hz")
    
    try:
        while time.time() < end_time:
            # Update block header with new nonce
            block_header = struct.pack('<L32s32sLLL', 
                                     version, prev_block, merkle_root, 
                                     timestamp, bits, nonce)
            
            # Load block header into CPU memory
            for i in range(0, len(block_header), 4):
                value = int.from_bytes(block_header[i:i+4], 'little')
                cpu.memory.write(i, value)
            
            # Execute mining program
            for instruction in mining_program:
                clk = cpu.clock.tick()
                if clk > VTH:
                    pc = cpu.control_unit.program_counter.read()
                    cpu.memory.write(pc, instruction)
                    cpu.execute_instruction(clk)
            
            # Read final hash from registers
            final_hash = 0
            for i in range(8):
                hash_part = cpu.registers[i].read()
                final_hash = (final_hash << 32) | hash_part
            
            # Verify hash with real SHA-256
            real_hash = hashlib.sha256(hashlib.sha256(block_header).digest()).hexdigest()
            
            hashes_completed += 1
            nonce += 1
            
            # Log progress every second
            current_time = time.time()
            if current_time - last_log_time >= 1.0:
                elapsed = current_time - last_log_time
                recent_hashes = hashes_completed - last_hashes
                current_hashrate = recent_hashes / elapsed
                
                logging.info(f"Hash rate: {current_hashrate:.2f} H/s | "
                           f"Total hashes: {hashes_completed} | "
                           f"Latest hash: {real_hash}")
                
                last_log_time = current_time
                last_hashes = hashes_completed
    
    except KeyboardInterrupt:
        logging.info("\nMining test interrupted by user")
    
    finally:
        total_time = time.time() - start_time
        avg_hashrate = hashes_completed / total_time
        
        logging.info("\n=== Mining Test Results ===")
        logging.info(f"Test duration: {total_time:.2f} seconds")
        logging.info(f"Total hashes completed: {hashes_completed}")
        logging.info(f"Average hash rate: {avg_hashrate:.2f} H/s")
        logging.info(f"Instructions executed: {cpu.performance_counters['instructions_executed']}")
        logging.info(f"Cache hit rate: {cpu.get_cache_hit_rate():.2f}%")
        logging.info(f"Final clock frequency: {cpu.clock.current_frequency} Hz")
        logging.info(f"Active cores: {len([c for c in cpu.cores if any(ctx['active'] for ctx in c.thread_contexts)])} of {len(cpu.cores)}")
        
        # Save detailed stats
        with open('mining_stats_detailed.log', 'w') as f:
            f.write(f"=== Detailed Mining Statistics ===\n")
            f.write(f"Test timestamp: {datetime.now()}\n")
            f.write(f"CPU Configuration:\n")
            f.write(f"- Cores: {len(cpu.cores)}\n")
            f.write(f"- Threads per core: {THREADS_PER_CORE}\n")
            f.write(f"- Word size: {WORD_SIZE} bits\n")
            f.write(f"- Memory size: {MEMORY_SIZE} bytes\n")
            f.write(f"\nPerformance Metrics:\n")
            f.write(f"- Total hashes: {hashes_completed}\n")
            f.write(f"- Average hash rate: {avg_hashrate:.2f} H/s\n")
            f.write(f"- Instructions per hash: {cpu.performance_counters['instructions_executed'] / hashes_completed:.2f}\n")
            f.write(f"- Cache hit rate: {cpu.get_cache_hit_rate():.2f}%\n")
            f.write(f"- Final clock frequency: {cpu.clock.current_frequency} Hz\n")

if __name__ == "__main__":
    run_mining_test(60)  # Run for 60 seconds