import asyncio
import time
import hashlib
from typing import Dict, List, Optional, Set
from datetime import datetime, timedelta
from decimal import Decimal
from dataclasses import dataclass
from loguru import logger

from ..models.order import P2POrder, OrderBookSnapshot
from ..models.transaction import (
    DVariable, MerchantLiquidityStats, TransactionType, IndividualTransaction
)


@dataclass
class LiquidityEvent:
    """Simple liquidity event for analysis"""
    timestamp: datetime
    event_type: str
    description: str
    merchant_id: str
    merchant_name: str
    asset: str
    fiat: str
    trade_type: str
    current_volume: float
    previous_volume: float
    volume_change: float
    price: float


class TransactionAnalyzer:
    """
    Анализатор P2P транзакций на основе алгоритма D1/Dn
    """
    
    def __init__(self, d_variable_timeout: int = 300):
        self.d_variable_timeout = d_variable_timeout
        self.d_variables: Dict[str, DVariable] = {}
        self.merchant_stats: Dict[str, MerchantLiquidityStats] = {}
        self.last_snapshots: Dict[str, OrderBookSnapshot] = {}
        self.last_cleanup = time.time()
        # Для дедупликации транзакций
        self.processed_transactions: Set[str] = set()
        
    def analyze_orderbook_changes(
        self, 
        current_snapshot: OrderBookSnapshot,
        previous_snapshot: Optional[OrderBookSnapshot] = None
    ) -> List[LiquidityEvent]:
        """Анализ изменений в ордербуке и выявление событий ликвидности"""
        
        events = []
        snapshot_key = f"{current_snapshot.asset}_{current_snapshot.trade_type}_{current_snapshot.fiat}"
        
        # Используем предыдущий snapshot из кэша, если не передан
        if previous_snapshot is None:
            previous_snapshot = self.last_snapshots.get(snapshot_key)
        
        # Сохраняем текущий snapshot
        self.last_snapshots[snapshot_key] = current_snapshot
        
        if previous_snapshot is None:
            logger.info(f"🔍 First snapshot for {snapshot_key}, initializing tracking")
            return events
        
        # Анализируем изменения по мерчантам
        events.extend(self._analyze_merchant_changes(current_snapshot, previous_snapshot))
        
        # Очистка истёкших D-переменных
        self._cleanup_expired_d_variables()
        
        return events
    
    def _analyze_merchant_changes(
        self, 
        current: OrderBookSnapshot, 
        previous: OrderBookSnapshot
    ) -> List[LiquidityEvent]:
        """Анализ изменений объёмов по мерчантам"""
        
        events = []
        
        # Создаём словари для быстрого поиска
        current_orders = {order.merchant_id: order for order in current.orders}
        previous_orders = {order.merchant_id: order for order in previous.orders}
        
        # Анализируем всех мерчантов из текущего снапшота
        for merchant_id, current_order in current_orders.items():
            previous_order = previous_orders.get(merchant_id)
            
            if previous_order is None:
                # Новый мерчант появился
                events.append(self._create_event(
                    current_order, 
                    None,
                    "NEW_MERCHANT",
                    f"🆕 New merchant appeared: {current_order.merchant_name}"
                ))
                continue
            
            # Сравниваем объёмы
            volume_change = current_order.available_amount - previous_order.available_amount
            
            if abs(volume_change) < 0.01:  # Игнорируем минимальные изменения
                continue
            
            if volume_change < 0:
                # Объём уменьшился - создаём D1 переменную
                event = self._handle_volume_decrease(current_order, previous_order, abs(volume_change))
                if event:
                    events.append(event)
            
            elif volume_change > 0:
                # Объём увеличился - ищем соответствующую D1 переменную
                event = self._handle_volume_increase(current_order, previous_order, volume_change)
                if event:
                    events.append(event)
        
        # Проверяем исчезнувших мерчантов
        disappeared_merchants = set(previous_orders.keys()) - set(current_orders.keys())
        for merchant_id in disappeared_merchants:
            previous_order = previous_orders[merchant_id]
            events.append(self._create_event(
                None,
                previous_order, 
                "MERCHANT_DISAPPEARED",
                f"👻 Merchant disappeared: {previous_order.merchant_name}"
            ))
        
        return events
    
    def _handle_volume_decrease(self, current_order: P2POrder, previous_order: P2POrder, decrease_amount: float) -> Optional[LiquidityEvent]:
        """Обработка уменьшения объёма - создание D1 переменной"""
        
        # Создаем уникальный хеш для дедупликации
        transaction_hash = self._create_transaction_hash(
            current_order.merchant_id,
            current_order.asset,
            decrease_amount,
            previous_order.available_amount,
            current_order.available_amount,
            "real_trade"
        )
        
        # Проверяем, не обрабатывали ли мы уже эту транзакцию
        if transaction_hash in self.processed_transactions:
            logger.debug(f"🔄 Duplicate transaction detected for {current_order.merchant_name}, skipping")
            return None
            
        # Добавляем в обработанные
        self.processed_transactions.add(transaction_hash)
        
        # Создаём D1 переменную с уникальным ID
        d_var_id = f"{current_order.merchant_id}_{current_order.asset}_{transaction_hash[:8]}"
        d_variable = DVariable(
            id=d_var_id,
            merchant_id=current_order.merchant_id,
            asset=current_order.asset,
            fiat=current_order.fiat,
            trade_type=current_order.trade_type,
            amount=decrease_amount,
            timestamp=datetime.now(),
            expires_at=datetime.now() + timedelta(seconds=max(self.d_variable_timeout, 3600)),
            price_at_d1=current_order.price,
            merchant_name=current_order.merchant_name
        )
        
        self.d_variables[d_var_id] = d_variable
        
        # Создаём индивидуальную транзакцию
        transaction = IndividualTransaction(
            merchant_id=current_order.merchant_id,
            merchant_name=current_order.merchant_name,
            asset=current_order.asset,
            fiat=current_order.fiat,
            transaction_type="real_trade",
            amount=decrease_amount,
            price=current_order.price,
            volume_before=previous_order.available_amount,
            volume_after=current_order.available_amount,
            trade_type=current_order.trade_type
        )
        
        # Обновляем статистику мерчанта
        self._update_merchant_stats_with_transaction(
            current_order.merchant_id,
            current_order.merchant_name,
            current_order.asset,
            "real_trade",
            transaction
        )
        
        logger.debug(f"📉 Created D1 variable: {d_var_id} for {decrease_amount} {current_order.asset}")
        
        return self._create_event(
            current_order,
            previous_order,
            "VOLUME_DECREASE",
            f"📉 Volume decreased: {current_order.merchant_name} -{decrease_amount:.2f} {current_order.asset}"
        )
    
    def _handle_volume_increase(self, current_order: P2POrder, previous_order: P2POrder, increase_amount: float) -> Optional[LiquidityEvent]:
        """Обработка увеличения объёма - поиск соответствующей D1 переменной"""
        
        # Создаем уникальный хеш для дедупликации
        transaction_hash = self._create_transaction_hash(
            current_order.merchant_id,
            current_order.asset,
            increase_amount,
            previous_order.available_amount,
            current_order.available_amount,
            "volume_increase"
        )
        
        # Проверяем, не обрабатывали ли мы уже эту транзакцию
        if transaction_hash in self.processed_transactions:
            logger.debug(f"🔄 Duplicate volume increase detected for {current_order.merchant_name}, skipping")
            return None
            
        # Добавляем в обработанные
        self.processed_transactions.add(transaction_hash)
        
        # Ищем соответствующую D1 переменную с относительным допуском 2%
        matching_d_var = None
        for d_var_id, d_var in self.d_variables.items():
            if d_var.merchant_id != current_order.merchant_id or d_var.asset != current_order.asset:
                continue
            try:
                diff = abs(float(d_var.amount) - float(increase_amount))
                tolerance = max(0.02, 0.02 * max(float(d_var.amount), float(increase_amount)))
            except Exception:
                diff = 999999.0
                tolerance = 0.0
            if diff <= tolerance:
                matching_d_var = d_var
                break
        
        if matching_d_var:
            # Найдена соответствующая D1 переменная - это реальная сделка
            logger.debug(f"✅ Real trade confirmed: {increase_amount} {current_order.asset}")
            
            # Удаляем D1 переменную
            del self.d_variables[matching_d_var.id]
            
            # Создаём индивидуальную транзакцию для подтверждённой сделки
            transaction = IndividualTransaction(
                merchant_id=current_order.merchant_id,
                merchant_name=current_order.merchant_name,
                asset=current_order.asset,
                fiat=current_order.fiat,
                transaction_type="real_trade",
                amount=increase_amount,
                price=current_order.price,
                volume_before=previous_order.available_amount,
                volume_after=current_order.available_amount,
                trade_type=current_order.trade_type
            )
            
            # Обновляем статистику мерчанта
            self._update_merchant_stats_with_transaction(
                current_order.merchant_id,
                current_order.merchant_name,
                current_order.asset,
                "real_trade",
                transaction
            )
            
            return self._create_event(
                current_order,
                previous_order,
                "REAL_TRADE",
                f"✅ Real trade confirmed: {current_order.merchant_name} {increase_amount:.2f} {current_order.asset}"
            )
        else:
            # Не найдена соответствующая D1 переменная - это пополнение мерчанта
            logger.debug(f"💰 Merchant top-up detected: {increase_amount} {current_order.asset}")
            
            # Создаём индивидуальную транзакцию для пополнения
            transaction = IndividualTransaction(
                merchant_id=current_order.merchant_id,
                merchant_name=current_order.merchant_name,
                asset=current_order.asset,
                fiat=current_order.fiat,
                transaction_type="top_up",
                amount=increase_amount,
                price=current_order.price,
                volume_before=previous_order.available_amount,
                volume_after=current_order.available_amount,
                trade_type=current_order.trade_type
            )
            
            # Обновляем статистику мерчанта
            self._update_merchant_stats_with_transaction(
                current_order.merchant_id,
                current_order.merchant_name,
                current_order.asset,
                "top_up",
                transaction
            )
            
            logger.debug(f"💰 Merchant top-up detected: {increase_amount} {current_order.asset}")
            
            return self._create_event(
                current_order,
                previous_order,
                "MERCHANT_TOPUP",
                f"💰 Merchant top-up: {current_order.merchant_name} +{increase_amount:.2f} {current_order.asset}"
            )
    
    def _create_transaction_hash(self, merchant_id: str, asset: str, amount: float, 
                                volume_before: float, volume_after: float, transaction_type: str) -> str:
        """Создание уникального хеша для дедупликации транзакций"""
        # Создаем строку из ключевых параметров транзакции
        hash_string = f"{merchant_id}_{asset}_{amount:.6f}_{volume_before:.6f}_{volume_after:.6f}_{transaction_type}"
        # Возвращаем первые 16 символов SHA256 хеша
        return hashlib.sha256(hash_string.encode()).hexdigest()[:16]
    
    def _cleanup_expired_d_variables(self):
        """Удаление истёкших D-переменных (старше 30 минут)"""
        current_time = datetime.now()
        expired_vars = []
        
        for d_var_id, d_var in self.d_variables.items():
            if current_time > d_var.expires_at:
                expired_vars.append(d_var_id)
        
        for d_var_id in expired_vars:
            logger.debug(f"🗑️ Removing expired D-variable: {d_var_id}")
            del self.d_variables[d_var_id]
    
    def _update_merchant_stats(self, merchant_id: str, asset: str, event_type: str, amount: float):
        """Обновление статистики мерчанта (устаревший метод для совместимости)"""
        
        if merchant_id not in self.merchant_stats:
            self.merchant_stats[merchant_id] = MerchantLiquidityStats(merchant_id=merchant_id, asset=asset)
        
        stats = self.merchant_stats[merchant_id]
        
        if event_type == "real_trade":
            stats.real_trades_count += 1
            stats.real_trades_volume += amount
        elif event_type == "order_cancellation":
            stats.order_cancellations_count += 1
            stats.order_cancellations_volume += amount
        elif event_type == "top_up":
            stats.top_ups_count += 1
            stats.top_ups_volume += amount
        
        stats.last_activity = datetime.now()
    
    def _update_merchant_stats_with_transaction(self, merchant_id: str, merchant_name: str, asset: str, event_type: str, transaction: IndividualTransaction):
        """Обновление статистики мерчанта с индивидуальной транзакцией"""
        
        if merchant_id not in self.merchant_stats:
            self.merchant_stats[merchant_id] = MerchantLiquidityStats(
                merchant_id=merchant_id,
                merchant_name=merchant_name,
                asset=asset
            )
        
        stats = self.merchant_stats[merchant_id]
        
        # Обновляем имя мерчанта, если оно не было установлено
        if not stats.merchant_name and merchant_name:
            stats.merchant_name = merchant_name
        
        if event_type == "real_trade":
            stats.add_confirmed_trade(transaction)
        elif event_type == "order_cancellation":
            stats.add_cancelled_order(transaction)
        elif event_type == "top_up":
            stats.add_merchant_topup(transaction)
    
    def _create_event(
        self, 
        current_order: Optional[P2POrder], 
        previous_order: Optional[P2POrder],
        event_type: str, 
        description: str
    ) -> LiquidityEvent:
        """Создание события ликвидности"""
        
        return LiquidityEvent(
            timestamp=datetime.now(),
            event_type=event_type,
            description=description,
            merchant_id=current_order.merchant_id if current_order else (previous_order.merchant_id if previous_order else "unknown"),
            merchant_name=current_order.merchant_name if current_order else (previous_order.merchant_name if previous_order else "unknown"),
            asset=current_order.asset if current_order else (previous_order.asset if previous_order else "unknown"),
            fiat=current_order.fiat if current_order else (previous_order.fiat if previous_order else "unknown"),
            trade_type=current_order.trade_type if current_order else (previous_order.trade_type if previous_order else "unknown"),
            current_volume=current_order.available_amount if current_order else 0.0,
            previous_volume=previous_order.available_amount if previous_order else 0.0,
            volume_change=(current_order.available_amount if current_order else 0.0) - (previous_order.available_amount if previous_order else 0.0),
            price=current_order.price if current_order else (previous_order.price if previous_order else 0.0)
        )
    
    def get_merchant_stats(self, merchant_id: str) -> Optional[MerchantLiquidityStats]:
        """Получение статистики мерчанта"""
        return self.merchant_stats.get(merchant_id)
    
    def get_all_merchant_stats(self) -> Dict[str, MerchantLiquidityStats]:
        """Получение статистики всех мерчантов"""
        return self.merchant_stats.copy()
    
    def get_active_d_variables(self) -> Dict[str, DVariable]:
        """Получение активных D-переменных"""
        return self.d_variables.copy()
    
    def export_stats_to_json(self) -> dict:
        """Экспорт статистики в JSON формат с индивидуальными транзакциями"""
        
        return {
            "timestamp": datetime.now().isoformat(),
            "total_merchants": len(self.merchant_stats),
            "active_d_variables": len(self.d_variables),
            "merchant_stats": {
                merchant_id: {
                    "merchant_name": stats.merchant_name,
                    "asset": stats.asset,
                    "real_trades_count": stats.real_trades_count,
                    "real_trades_volume": stats.real_trades_volume,
                    "order_cancellations_count": stats.order_cancellations_count,
                    "order_cancellations_volume": stats.order_cancellations_volume,
                    "top_ups_count": stats.top_ups_count,
                    "top_ups_volume": stats.top_ups_volume,
                    "last_activity": stats.last_activity.isoformat() if stats.last_activity else None,
                    # Индивидуальные транзакции
                    "individual_trades": [tx.to_dict() for tx in stats.individual_trades],
                    "individual_top_ups": [tx.to_dict() for tx in stats.individual_top_ups],
                    "individual_cancellations": [tx.to_dict() for tx in stats.individual_cancellations]
                }
                for merchant_id, stats in self.merchant_stats.items()
            },
            "d_variables": {
                key: {
                    "merchant_id": d_var.merchant_id,
                    "asset": d_var.asset,
                    "amount": float(d_var.amount),
                    "timestamp": d_var.timestamp.isoformat(),
                    "age_seconds": (datetime.now() - d_var.timestamp).total_seconds(),
                    "is_matched": d_var.is_matched,
                    "matched_with": d_var.matched_with
                }
                for key, d_var in self.d_variables.items()
            }
        } 