"""
Blockchain transaction selection and fee optimization
"""
from typing import List, Dict, Any, Optional
from dataclasses import dataclass
import time
import heapq

@dataclass
class Transaction:
    txid: str
    fee: float
    size: int
    timestamp: float
    
@dataclass
class BlockConfig:
    max_size: int = 1000000  # 1MB default
    min_fee_rate: float = 1.0  # sat/byte
    max_ancestors: int = 25
    
class TransactionSelector:
    def __init__(self):
        self.mempool: Dict[str, Transaction] = {}
        self.block_config = BlockConfig()
        self.fee_estimator = FeeEstimator()
        
    def add_transaction(self, tx: Transaction):
        """Add transaction to mempool"""
        self.mempool[tx.txid] = tx
        self.fee_estimator.update(tx.fee / tx.size)
        
    def remove_transaction(self, txid: str):
        """Remove transaction from mempool"""
        if txid in self.mempool:
            tx = self.mempool[txid]
            self.fee_estimator.remove(tx.fee / tx.size)
            del self.mempool[txid]
            
    def select_transactions(self) -> List[Transaction]:
        """Select optimal set of transactions for next block"""
        selected = []
        size = 0
        min_fee_rate = self.block_config.min_fee_rate
        
        # Sort by fee rate
        sorted_txs = sorted(
            self.mempool.values(),
            key=lambda x: x.fee / x.size,
            reverse=True
        )
        
        for tx in sorted_txs:
            if size + tx.size <= self.block_config.max_size:
                fee_rate = tx.fee / tx.size
                if fee_rate >= min_fee_rate:
                    selected.append(tx)
                    size += tx.size
                    
        return selected
        
class FeeEstimator:
    def __init__(self, window_size: int = 1000):
        self.window_size = window_size
        self.fee_rates = []
        self.last_estimate = 0.0
        
    def update(self, fee_rate: float):
        """Update fee rate history"""
        self.fee_rates.append(fee_rate)
        if len(self.fee_rates) > self.window_size:
            self.fee_rates.pop(0)
            
        # Update last estimate
        if self.fee_rates:
            self.last_estimate = sum(self.fee_rates) / len(self.fee_rates)
            
    def remove(self, fee_rate: float):
        """Remove fee rate from history"""
        if fee_rate in self.fee_rates:
            self.fee_rates.remove(fee_rate)
            
        # Update last estimate
        if self.fee_rates:
            self.last_estimate = sum(self.fee_rates) / len(self.fee_rates)
        else:
            self.last_estimate = 0.0
            
    def estimate_fee(self, target_blocks: int = 1) -> float:
        """Estimate required fee rate for inclusion within target blocks"""
        if not self.fee_rates:
            return self.block_config.min_fee_rate
            
        sorted_rates = sorted(self.fee_rates, reverse=True)
        index = min(
            target_blocks * len(sorted_rates) // 6,
            len(sorted_rates) - 1
        )
        return max(sorted_rates[index], self.block_config.min_fee_rate)
        
class MempoolOptimizer:
    def __init__(self):
        self.selector = TransactionSelector()
        self.current_block: List[Transaction] = []
        
    def optimize_block(self) -> Dict[str, Any]:
        """Optimize current block composition"""
        self.current_block = self.selector.select_transactions()
        
        return {
            'selected_tx_count': len(self.current_block),
            'total_size': sum(tx.size for tx in self.current_block),
            'total_fees': sum(tx.fee for tx in self.current_block),
            'avg_fee_rate': self._calculate_avg_fee_rate(),
            'estimated_next_fee': self.selector.fee_estimator.estimate_fee()
        }
        
    def _calculate_avg_fee_rate(self) -> float:
        """Calculate average fee rate for selected transactions"""
        if not self.current_block:
            return 0.0
            
        total_fees = sum(tx.fee for tx in self.current_block)
        total_size = sum(tx.size for tx in self.current_block)
        
        return total_fees / total_size if total_size > 0 else 0.0
        
    def get_block_template(self) -> Dict[str, Any]:
        """Get block template with selected transactions"""
        return {
            'transactions': [
                {
                    'txid': tx.txid,
                    'fee': tx.fee,
                    'size': tx.size
                }
                for tx in self.current_block
            ],
            'total_size': sum(tx.size for tx in self.current_block),
            'total_fees': sum(tx.fee for tx in self.current_block),
            'timestamp': time.time()
        }
