"""
CPU mining optimization and thread management
"""
from typing import List, Dict, Any, Optional
from dataclasses import dataclass
import threading
import multiprocessing
import time
from queue import Queue
import psutil

@dataclass
class CPUStats:
    core_id: int
    usage_percent: float
    temperature: Optional[float]
    frequency: float
    
@dataclass
class ThreadConfig:
    core_id: int
    priority: int
    affinity: List[int]
    
class CPUThreadManager:
    def __init__(self, num_threads: int = None):
        self.num_threads = num_threads or multiprocessing.cpu_count()
        self.threads: List[threading.Thread] = []
        self.thread_configs: Dict[int, ThreadConfig] = {}
        self.work_queues: List[Queue] = []
        self.results_queue = Queue()
        self.active = True
        
        # Initialize queues
        for _ in range(self.num_threads):
            self.work_queues.append(Queue())
            
        # Start worker threads
        for i in range(self.num_threads):
            thread = threading.Thread(
                target=self._cpu_worker,
                args=(i,)
            )
            thread.daemon = True
            thread.start()
            self.threads.append(thread)
            
    def set_thread_config(self, thread_id: int, config: ThreadConfig):
        """Configure thread settings"""
        self.thread_configs[thread_id] = config
        
    def add_work(self, work_unit: Any, thread_id: Optional[int] = None):
        """Add work to thread queue"""
        if thread_id is not None:
            self.work_queues[thread_id].put(work_unit)
        else:
            # Find least loaded queue
            queue_id = min(
                range(len(self.work_queues)),
                key=lambda i: self.work_queues[i].qsize()
            )
            self.work_queues[queue_id].put(work_unit)
            
    def _cpu_worker(self, thread_id: int):
        """Worker thread function"""
        while self.active:
            try:
                work = self.work_queues[thread_id].get(timeout=1)
                result = self._process_work_unit(thread_id, work)
                if result:
                    self.results_queue.put(result)
            except Queue.Empty:
                continue
                
    def _process_work_unit(self, thread_id: int, work: Any) -> Optional[Dict]:
        """Process a work unit"""
        # Apply thread configuration
        if thread_id in self.thread_configs:
            config = self.thread_configs[thread_id]
            self._apply_thread_config(threading.current_thread(), config)
            
        # Simulate work
        time.sleep(0.1)  # Placeholder
        return None
        
    def _apply_thread_config(self, thread: threading.Thread, config: ThreadConfig):
        """Apply configuration to thread"""
        try:
            # Set thread affinity
            if hasattr(psutil.Process(), 'cpu_affinity'):
                psutil.Process().cpu_affinity(config.affinity)
                
            # Set priority
            if hasattr(psutil.Process(), 'nice'):
                psutil.Process().nice(config.priority)
        except Exception as e:
            print(f"Failed to apply thread config: {e}")
            
class CPUOptimizer:
    def __init__(self):
        self.thread_manager = CPUThreadManager()
        self.cpu_stats = {}
        self.last_optimization = 0
        
    def optimize_mining(self) -> Dict[str, Any]:
        """Optimize CPU mining performance"""
        current_time = time.time()
        if current_time - self.last_optimization < 10:  # Rate limit
            return self._get_current_stats()
            
        self.last_optimization = current_time
        
        # Update CPU stats
        self._update_cpu_stats()
        
        # Optimize thread configuration
        self._optimize_thread_config()
        
        return self._get_current_stats()
        
    def _update_cpu_stats(self):
        """Update CPU core statistics"""
        for i in range(self.thread_manager.num_threads):
            self.cpu_stats[i] = CPUStats(
                core_id=i,
                usage_percent=psutil.cpu_percent(percpu=True)[i],
                temperature=self._get_core_temp(i),
                frequency=psutil.cpu_freq().current
            )
            
    def _get_core_temp(self, core_id: int) -> Optional[float]:
        """Get CPU core temperature if available"""
        try:
            temps = psutil.sensors_temperatures()
            if 'coretemp' in temps:
                return temps['coretemp'][core_id].current
        except Exception:
            pass
        return None
        
    def _optimize_thread_config(self):
        """Optimize thread configuration based on CPU stats"""
        for thread_id, stats in self.cpu_stats.items():
            # Create optimized configuration
            config = ThreadConfig(
                core_id=stats.core_id,
                priority=self._calculate_priority(stats),
                affinity=self._calculate_affinity(stats)
            )
            self.thread_manager.set_thread_config(thread_id, config)
            
    def _calculate_priority(self, stats: CPUStats) -> int:
        """Calculate optimal thread priority"""
        if stats.usage_percent < 50:
            return 0  # Normal priority
        elif stats.temperature and stats.temperature > 80:
            return -10  # Lower priority if hot
        else:
            return 10  # Higher priority if cool and busy
            
    def _calculate_affinity(self, stats: CPUStats) -> List[int]:
        """Calculate optimal core affinity"""
        if stats.temperature and stats.temperature > 85:
            # Spread load if too hot
            return list(range(self.thread_manager.num_threads))
        else:
            # Pin to specific core
            return [stats.core_id]
            
    def _get_current_stats(self) -> Dict[str, Any]:
        """Get current optimization statistics"""
        return {
            'cpu_stats': {
                core_id: {
                    'usage': stats.usage_percent,
                    'temp': stats.temperature,
                    'freq': stats.frequency
                }
                for core_id, stats in self.cpu_stats.items()
            },
            'thread_configs': {
                thread_id: {
                    'priority': config.priority,
                    'affinity': config.affinity
                }
                for thread_id, config in
                self.thread_manager.thread_configs.items()
            },
            'queue_stats': {
                i: q.qsize()
                for i, q in enumerate(self.thread_manager.work_queues)
            }
        }
