"""
Ultra-efficient CPU memory manager optimized for mining operations.
Uses flip-flop based timing and minimal memory footprint.
"""
from typing import Dict, Any, Optional, Tuple
import mmap
import threading
import time
import logging
from electron_speed import max_switch_freq, GATE_DELAY
from disk_storage import DiskStorageManager  # Import disk storage

class CPUMiningMemory:
    """Memory manager optimized for mining operations at electron speed"""
    
    # Constants based on flip-flop physics
    SWITCH_FREQ = 9.80e14  # Hz
    GATE_DELAY = 1.02e-15  # seconds
    
    def __init__(self, cpu_id: int):
        self.cpu_id = cpu_id
        
        # Initialize thread lock first
        self.lock = threading.Lock()
        
        # Memory configuration
        self.block_size = 256  # SHA-256 block size for streaming
        self.blocks_per_thread = 2  # Double-buffering for each thread
        
        # Ultra-short block lifetime based on gate delay
        self.block_lifetime = self.GATE_DELAY * 4  # 4 gate delays
        
        # Create base storage directory first
        import os
        os.makedirs("storage", exist_ok=True)
        os.makedirs(f"storage/cpu_{cpu_id}", exist_ok=True)
        
        # Initialize disk storage
        self.disk_storage = DiskStorageManager(
            storage_path=f"storage/cpu_{cpu_id}",
            compression="lzma",  # Maximum compression
            max_size_gb=0.1)  # 100MB per CPU max
            
        # Initialize tracking dicts
        self.active_blocks = {}  # {block_id: (mmap_block, creation_time)}
        self.thread_blocks = {}
        self.thread_blocks = {}  # {thread_id: [block_ids]}
        self.allocated_bytes = 0
        self.lock = threading.Lock()
        
        # Performance tracking
        self.operations = 0
        self.last_cleanup = time.time()
        
        # Start cleanup thread
        self.running = True
        self.cleanup_thread = threading.Thread(target=self._cleanup_loop, daemon=True)
        self.cleanup_thread.start()
        
        logging.info(f"Initialized CPU {cpu_id} mining memory manager at {self.SWITCH_FREQ/1e9:.2f} GHz")
        
    def allocate_thread_blocks(self, thread_id: int) -> list[Tuple[str, Dict]]:
        """Allocate streaming blocks for a thread using disk storage"""
        with self.lock:
            blocks = []
            block_ids = []
            
            # Create streaming blocks using disk storage
            for i in range(self.blocks_per_thread):
                block_id = f"thread_{thread_id}_block_{i}"
                
                # Create a stream block in disk storage
                block_data = {
                    'thread_id': thread_id,
                    'block_index': i,
                    'created_at': time.time(),
                    'size': self.block_size,
                    'status': 'active'
                }
                
                # Store block metadata in disk
                self.disk_storage.store_block(block_id, block_data)
                blocks.append((block_id, block_data))
                block_ids.append(block_id)
                
            self.thread_blocks[thread_id] = block_ids
            return blocks
            
    def free_thread_blocks(self, thread_id: int):
        """Free all blocks associated with a thread from disk storage"""
        with self.lock:
            if thread_id in self.thread_blocks:
                for block_id in self.thread_blocks[thread_id]:
                    # Remove block from disk storage
                    self.disk_storage.delete_block(block_id)
                # Clear thread tracking
                del self.thread_blocks[thread_id]
                
    def allocate_task_memory(self, task_id: str, size: int) -> str:
        """Allocate streaming memory for a specific task"""
        block_id = f"task_{task_id}_{int(time.time() * 1e9)}"
        
        # Store task data using disk storage
        block_data = {
            'task_id': task_id,
            'created_at': time.time(),
            'size': size,
            'status': 'active'
        }
        
        self.disk_storage.store_block(block_id, block_data)
        return block_id
        
    def free_task_memory(self, block_id: str):
        """Free task-specific memory"""
        # Remove from disk storage immediately
        self.disk_storage.delete_block(block_id)
                
    def _force_cleanup(self):
        """Immediate cleanup of old blocks"""
        current_time = time.time()
        with self.lock:
            for block_id, (block, creation_time) in list(self.active_blocks.items()):
                if current_time - creation_time > self.block_lifetime:
                    block.close()
                    del self.active_blocks[block_id]
                    # Remove from thread tracking
                    for thread_blocks in self.thread_blocks.values():
                        if block_id in thread_blocks:
                            thread_blocks.remove(block_id)
                    self.allocated_bytes -= self.block_size
                    
    def _cleanup_loop(self):
        """Background cleanup thread running at electron speed"""
        while self.running:
            self._force_cleanup()
            # Sleep for one gate delay
            time.sleep(max(self.GATE_DELAY, 1e-9))  # Minimum 1ns sleep
            
    def get_stats(self) -> Dict[str, Any]:
        """Get current memory usage statistics"""
        with self.lock:
            return {
                "cpu_id": self.cpu_id,
                "active_blocks": len(self.active_blocks),
                "active_threads": len(self.thread_blocks),
                "allocated_bytes": self.allocated_bytes,
                "utilization": len(self.active_blocks) / self.max_blocks,
                "operations_count": self.operations,
                "switch_frequency": self.SWITCH_FREQ,
                "block_lifetime": self.block_lifetime
            }
            
    def __del__(self):
        """Cleanup on deletion"""
        self.running = False
        if hasattr(self, 'cleanup_thread'):
            self.cleanup_thread.join(timeout=1.0)
        with self.lock:
            for block, _ in self.active_blocks.values():
                block.close()
