"""
Trading Engine Simplifié - Ouverture de positions uniquement
"""

import logging
import time
from datetime import datetime
from threading import Thread, Lock
from typing import Dict, List, Optional
import os
import sys

# Imports locaux
import sys
import os
sys.path.append('..')
from hyperliquid_client import HyperliquidClient
# Position class définie localement (plus besoin de real_time_trading_engine)
class Position:
    def __init__(self, symbol="", side="", size=0.0, entry_price=0.0, leverage=1, **kwargs):
        self.symbol = symbol
        self.side = side
        self.size = size
        self.entry_price = entry_price
        self.leverage = leverage
        for k, v in kwargs.items():
            setattr(self, k, v)

import trading_config
from .position_utils import PositionRetriever
try:
    from .trend_detector import TrendDetector
    from .cooldown_manager import CooldownManager
    from .correlation_manager import BTCCorrelationManager
except ImportError:
    # Fallback si les modules ne sont pas disponibles
    TrendDetector = None
    CooldownManager = None
    BTCCorrelationManager = None

class SimpleTradingEngine:
    """Trading engine simplifié qui ne gère que l'ouverture de positions"""

    def __init__(self, trading_mode='SIMULATION', initial_balance=1000, hyperliquid_client=None):
        self.trading_mode = trading_mode
        self.initial_balance = initial_balance
        self.current_balance = initial_balance
        self.hyperliquid_client = hyperliquid_client

        # Configuration trading
        self.max_positions = getattr(trading_config, 'MAX_SIMULTANEOUS_POSITIONS', 5)
        self.position_size_pct = 0.1  # 10% par défaut (non utilisé avec fixed sizing)
        self.max_leverage = getattr(trading_config, 'MAX_LEVERAGE', 20)
        self.hyperliquid_fee_rate = getattr(trading_config, 'HYPERLIQUID_FEE_RATE', 0.0005)

        # Storage
        self.positions = {}
        self.lock = Lock()
        self.running = False

        # Logger
        self.setup_logger()

        # Signals provider
        self.signals_provider = None

        # Trend detector pour filtrer les signaux
        self.trend_detector = TrendDetector(hyperliquid_client) if hyperliquid_client and TrendDetector else None

        # Cooldown manager pour éviter le overtrading
        self.cooldown_manager = CooldownManager(default_cooldown_minutes=15) if CooldownManager else None

        # Gestionnaire de corrélation BTC pour pondération
        self.correlation_manager = BTCCorrelationManager(hyperliquid_client) if BTCCorrelationManager and hyperliquid_client else None
        if self.correlation_manager:
            self.correlation_manager.start_background_updates()

        self.logger.info(f"=== SIMPLE TRADING ENGINE STARTED ===")
        self.logger.info(f"Mode: {trading_mode}")
        self.logger.info(f"Max positions: {self.max_positions}")
        self.logger.info(f"Trend detection: {'ENABLED' if self.trend_detector else 'DISABLED'}")
        self.logger.info(f"Cooldown protection: ENABLED (15min défaut)")
        self.logger.info(f"BTC Correlation weighting: {'ENABLED' if self.correlation_manager else 'DISABLED'}")

    def setup_logger(self):
        """Configure le système de logging"""
        self.logger = logging.getLogger("SimpleTradingEngine")
        self.logger.setLevel(logging.INFO)

        # Créer le dossier logs si nécessaire
        logs_dir = "logs"
        if not os.path.exists(logs_dir):
            os.makedirs(logs_dir)

        # Handler fichier
        file_handler = logging.FileHandler(os.path.join(logs_dir, "simple_trading.log"))
        file_handler.setLevel(logging.INFO)

        # Handler console
        console_handler = logging.StreamHandler()
        console_handler.setLevel(logging.INFO)

        # Format
        formatter = logging.Formatter('%(asctime)s - [%(name)s] - %(message)s')
        file_handler.setFormatter(formatter)
        console_handler.setFormatter(formatter)

        if not self.logger.handlers:
            self.logger.addHandler(file_handler)
            self.logger.addHandler(console_handler)

    def set_signals_provider(self, provider_func):
        """Injecter le fournisseur de signaux"""
        self.signals_provider = provider_func

    def get_signals(self):
        """Récupère les signaux depuis le provider"""
        if not self.signals_provider:
            self.logger.warning("⚠️  Aucun signals_provider configuré!")
            return []
        self.logger.info(f"✅ Appel signals_provider: {self.signals_provider}")
        return self.signals_provider()

    def get_current_positions_count(self) -> int:
        """Récupère le nombre de positions actuelles (méthode centralisée)"""
        if self.trading_mode in ['LIVE_MANUAL', 'LIVE_AUTO'] and self.hyperliquid_client:
            return PositionRetriever.get_positions_count(self.hyperliquid_client)
        else:
            return len(self.positions)

    def get_current_positions_list(self) -> List[Dict]:
        """Récupère la liste des positions actuelles (méthode centralisée)"""
        if self.trading_mode in ['LIVE_MANUAL', 'LIVE_AUTO'] and self.hyperliquid_client:
            return PositionRetriever.get_hyperliquid_positions(self.hyperliquid_client)
        else:
            return list(self.positions.values())

    def get_current_positions_dict(self) -> Dict:
        """Retourne un dictionnaire des positions actuelles pour analyse de corrélation"""
        positions_dict = {}
        positions_list = self.get_current_positions_list()

        for pos in positions_list:
            if hasattr(pos, 'symbol'):  # Objet Position
                positions_dict[pos.symbol] = {
                    'size': pos.size,
                    'entry_price': pos.entry_price,
                    'leverage': pos.leverage,
                    'side': pos.side
                }
            else:  # Dict Hyperliquid
                symbol = pos.get('symbol', '').replace('-PERP', '')
                positions_dict[symbol] = {
                    'size': pos.get('size', 0),
                    'entry_price': pos.get('entry_price', 0),
                    'leverage': pos.get('leverage', {}).get('value', 1),
                    'side': pos.get('side', '')
                }

        return positions_dict

    def should_open_position(self, signal: Dict) -> bool:
        """Vérifie si on peut ouvrir une position basée sur le signal"""
        symbol = signal.get('symbol')
        side = signal.get('side')

        # Vérifier le nombre max de positions (même logique que ProfitTaker)
        current_positions_count = self.get_current_positions_count()

        if current_positions_count >= self.max_positions:
            self.logger.info(f"REJECT {symbol}: Max positions atteint ({current_positions_count}/{self.max_positions})")
            return False

        # Vérifier la qualité du signal
        min_quality_score = getattr(trading_config, 'MIN_SIGNAL_QUALITY_SCORE', 0.65)
        quality_score = signal.get('quality_score', 0)

        if quality_score < min_quality_score:
            self.logger.info(f"REJECT {symbol}: Score qualité insuffisant ({quality_score:.2f} < {min_quality_score})")
            return False

        # Vérifier qu'on n'a pas déjà une position sur ce symbole (même logique que ProfitTaker)
        current_positions = self.get_current_positions_list()
        for pos in current_positions:
            # Gérer à la fois les objets Position (simulation) et les dicts (live)
            if hasattr(pos, 'symbol'):  # Objet Position
                pos_symbol = pos.symbol
            else:  # Dict Hyperliquid
                pos_symbol = pos.get('symbol', '').replace('-PERP', '')

            if pos_symbol == symbol:
                self.logger.info(f"REJECT {symbol}: Position déjà ouverte")
                return False

        # NOUVEAU: Vérifier la tendance avant d'ouvrir
        if self.trend_detector and self.trading_mode in ['LIVE_MANUAL', 'LIVE_AUTO']:
            try:
                allowed, reason = self.trend_detector.should_allow_signal(signal)
                if not allowed:
                    self.logger.warning(f"REJECT {symbol} {side}: Bloqué par détection de tendance - {reason}")
                    return False
                else:
                    self.logger.info(f"ACCEPT {symbol} {side}: Tendance validée - {reason}")
            except Exception as e:
                self.logger.error(f"Erreur détection tendance pour {symbol}: {e}")
                # En cas d'erreur, on laisse passer par défaut
                pass

        # NOUVEAU: Vérifier le cooldown avant d'ouvrir
        if self.cooldown_manager:
            try:
                allowed, reason = self.cooldown_manager.can_open_position(symbol)
                if not allowed:
                    self.logger.warning(f"REJECT {symbol} {side}: Bloqué par cooldown - {reason}")
                    return False
                else:
                    self.logger.debug(f"Cooldown OK pour {symbol}: {reason}")
            except Exception as e:
                self.logger.error(f"Erreur vérification cooldown pour {symbol}: {e}")
                # En cas d'erreur, on laisse passer
                pass

        # NOUVEAU: Vérifier la corrélation BTC et l'exposition du portefeuille
        if self.correlation_manager:
            try:
                current_positions = self.get_current_positions_dict()
                allowed, reason = self.correlation_manager.should_open_position(symbol, current_positions)
                if not allowed:
                    self.logger.warning(f"REJECT {symbol} {side}: Bloqué par corrélation - {reason}")
                    return False
                else:
                    self.logger.info(f"Corrélation OK pour {symbol}: {reason}")
            except Exception as e:
                self.logger.error(f"Erreur vérification corrélation pour {symbol}: {e}")
                # En cas d'erreur, on laisse passer
                pass

        return True

    def log_detailed_positions(self, positions):
        """Affiche le détail des positions actives (compatible simulation/live)"""
        if not positions:
            self.logger.info("Status: 0/5 positions actives")
            return

        self.logger.info("=" * 80)
        self.logger.info("POSITIONS ACTIVES:")
        self.logger.info("=" * 80)

        for pos in positions:
            try:
                # Gérer à la fois les objets Position (simulation) et les dicts (live)
                if hasattr(pos, 'symbol'):  # Objet Position (simulation)
                    symbol = pos.symbol
                    side = pos.side.upper()
                    entry_price = float(pos.entry_price)
                    size = abs(float(pos.size))
                    leverage = int(pos.leverage)
                    current_price = float(pos.current_price)

                    # Calculer PnL simple pour simulation
                    if side == 'LONG':
                        pnl_percent = ((current_price - entry_price) / entry_price) * 100 * leverage
                    else:  # SHORT
                        pnl_percent = ((entry_price - current_price) / entry_price) * 100 * leverage

                else:  # Dict Hyperliquid (live)
                    symbol = pos.get('symbol', '').replace('-PERP', '')
                    side = pos.get('side', '').upper()
                    entry_price = float(pos.get('entry_price', 0))
                    size = abs(float(pos.get('size', 0)))
                    leverage = int(pos.get('leverage', {}).get('value', 1))

                    # Récupérer le prix actuel
                    try:
                        current_price = self.hyperliquid_client.get_current_market_price(symbol)
                        if not current_price:
                            current_price = entry_price
                    except:
                        current_price = entry_price

                    # Calculer le PnL en utilisant la méthode centralisée
                    if entry_price > 0 and current_price > 0 and self.hyperliquid_client:
                        pnl_data = self.hyperliquid_client.calculate_position_pnl(
                            entry_price=entry_price,
                            current_price=current_price,
                            size=size,
                            side=side,
                            leverage=leverage
                        )
                        pnl_percent = pnl_data['pnl_percent_leveraged']
                    else:
                        pnl_percent = 0

                # Formatage selon la taille du prix
                if entry_price >= 1000:
                    entry_str = f"${entry_price:,.2f}"
                    current_str = f"${current_price:,.2f}"
                else:
                    entry_str = f"${entry_price:.5f}"
                    current_str = f"${current_price:.5f}"

                pnl_sign = "+" if pnl_percent >= 0 else ""
                pnl_color = "UP" if pnl_percent >= 0 else "DOWN"

                self.logger.info(f"{symbol:<6} {side:<5} | Entry: {entry_str:<12} -> Current: {current_str:<12} | "
                               f"Size: {size:<8.3f} | Leverage: {leverage:>2}x | "
                               f"PnL: {pnl_sign}{pnl_percent:>6.2f}% {pnl_color}")

            except Exception as e:
                self.logger.warning(f"Erreur affichage position {pos}: {e}")

        self.logger.info("=" * 80)

    def open_position(self, signal: Dict) -> Optional[Position]:
        """Ouvre une nouvelle position basée sur un signal"""
        try:
            symbol = signal.get('symbol')
            side = signal.get('side')

            # Vérifier que les champs obligatoires sont présents
            if not symbol or not side:
                self.logger.error(f"Signal invalide - manque symbol ou side: {signal}")
                return None

            leverage = min(signal.get('leverage', 1), self.max_leverage)

            # Calculer la taille de position
            if self.trading_mode in ['LIVE_MANUAL', 'LIVE_AUTO'] and self.hyperliquid_client:
                account_state = self.hyperliquid_client.get_account_state()
                if account_state and 'marginSummary' in account_state:
                    available_balance = float(account_state['marginSummary'].get('accountValue', 0))
                else:
                    available_balance = self.current_balance
            else:
                available_balance = self.current_balance

            # NOUVEAU: Calcul de position sizing avec système fixe ou pourcentage
            try:
                from trading_config import USE_FIXED_POSITION_SIZE, FIXED_POSITION_SIZE_USD, MIN_POSITION_SIZE_USD
                use_fixed = USE_FIXED_POSITION_SIZE
                fixed_size = FIXED_POSITION_SIZE_USD
                min_size = MIN_POSITION_SIZE_USD
            except:
                use_fixed = False
                fixed_size = None
                min_size = 20.0

            if use_fixed:
                if fixed_size is None:
                    # Calcul automatique: balance / max_positions
                    base_position_value = available_balance / self.max_positions
                    self.logger.info(f"Position sizing fixe auto: ${base_position_value:.2f} (balance ${available_balance:.2f} / {self.max_positions} positions)")
                else:
                    base_position_value = fixed_size
                    self.logger.info(f"Position sizing fixe configuré: ${base_position_value:.2f}")

                # Vérifier le minimum
                if base_position_value < min_size:
                    base_position_value = min_size
                    self.logger.warning(f"Position ajustée au minimum: ${min_size:.2f}")
            else:
                # Ancien système en pourcentage
                base_position_value = available_balance * self.position_size_pct
                self.logger.info(f"Position sizing en %: ${base_position_value:.2f} ({self.position_size_pct*100}% de ${available_balance:.2f})")

            # Appliquer la pondération de corrélation si disponible
            if self.correlation_manager:
                try:
                    # Obtenir la taille de position pondérée selon la corrélation
                    position_value = self.correlation_manager.get_position_weight(symbol, base_position_value)
                    self.logger.info(f"Position ajustée pour {symbol}: ${base_position_value:.2f} -> ${position_value:.2f} (corrélation)")
                except Exception as e:
                    self.logger.error(f"Erreur calcul pondération corrélation: {e}")
                    position_value = base_position_value
            else:
                position_value = base_position_value

            entry_price = signal.get('current_price', 0)

            if entry_price <= 0:
                self.logger.error(f"Prix invalide pour {symbol}: {entry_price}")
                return None

            size = (position_value * leverage) / entry_price
            margin_used = position_value

            # NOUVEAU: Vérifier que la valeur finale de la position n'est pas trop petite
            final_position_value = size * entry_price
            min_position_value = 30.0  # Minimum $30 USD (évite positions trop petites qui ferment aussitôt)

            if final_position_value < min_position_value:
                self.logger.warning(f"🚫 REJECT {symbol}: Position trop petite ${final_position_value:.2f} < ${min_position_value} (Size={size:.3f} @ ${entry_price:.4f})")
                return None

            # Calculer stop loss et take profit simples
            risk_pct = 0.15 / leverage
            if side == 'LONG':
                stop_loss = entry_price * (1 - risk_pct)
                take_profit = entry_price * (1 + risk_pct * 2)
            else:
                stop_loss = entry_price * (1 + risk_pct)
                take_profit = entry_price * (1 - risk_pct * 2)

            # Créer l'objet Position
            position = Position(
                id=f"{symbol}_{side}_{int(time.time())}",
                symbol=symbol,
                side=side,
                size=size,
                entry_price=entry_price,
                current_price=entry_price,
                leverage=leverage,
                stop_loss=stop_loss,
                take_profit=take_profit,
                timestamp=datetime.now().isoformat(),
                status='OPEN',
                margin_used=margin_used,
                trading_mode=self.trading_mode
            )

            # Placer l'ordre sur Hyperliquid si mode LIVE
            if self.trading_mode in ['LIVE_MANUAL', 'LIVE_AUTO'] and self.hyperliquid_client:
                is_buy = (side == 'LONG')
                result = self.hyperliquid_client.place_market_order(
                    symbol=symbol,
                    is_buy=is_buy,
                    size=size,
                    leverage=leverage,
                    order_tag=f"OPEN_{side}_{symbol}"
                )

                if not result.get('success'):
                    self.logger.error(f"Échec ouverture {symbol}: {result.get('error')}")
                    return None

            # Enregistrer la position
            with self.lock:
                self.positions[position.id] = position
                if self.trading_mode == 'SIMULATION':
                    self.current_balance -= margin_used

            # Enregistrer l'ouverture dans le cooldown manager
            if self.cooldown_manager:
                self.cooldown_manager.record_position_open(symbol)

            self.logger.info(f"OPENED: {symbol} {side} | Size: {size:.6f} | Entry: ${entry_price:.4f} | Leverage: {leverage}x")

            return position

        except Exception as e:
            self.logger.error(f"Erreur ouverture position: {e}")
            return None

    def trading_loop(self):
        """Boucle principale de trading - Ouverture uniquement"""
        self.logger.info("Démarrage de la boucle de trading...")

        while self.running:
            try:
                # L'état des positions est maintenant géré centralement via hyperliquid_client

                # Recherche de nouvelles opportunités (mode AUTO seulement)
                if self.trading_mode == 'LIVE_AUTO':
                    signals = self.get_signals()
                    self.logger.info(f"🔍 Récupérés {len(signals)} signaux du provider")

                    for signal in signals:
                        if self.should_open_position(signal):
                            self.open_position(signal)
                            time.sleep(1)  # Petit délai entre les ouvertures

                # Afficher le statut avec détail des positions (même logique que ProfitTaker)
                positions_count = self.get_current_positions_count()
                current_positions = self.get_current_positions_list()

                # Affichage détaillé toutes les 3 fois (1 minute)
                if hasattr(self, '_status_counter'):
                    self._status_counter += 1
                else:
                    self._status_counter = 1

                if self._status_counter % 3 == 0:  # Toutes les 3 fois = 1 minute
                    self.log_detailed_positions(current_positions)
                else:
                    self.logger.info(f"Status: {positions_count}/{self.max_positions} positions actives")

                # Pause avant la prochaine itération
                time.sleep(20)

            except Exception as e:
                self.logger.error(f"Erreur dans trading_loop: {e}")
                time.sleep(5)

    def start(self):
        """Démarre le trading engine"""
        if not self.running:
            self.running = True
            self.trading_thread = Thread(target=self.trading_loop)
            self.trading_thread.daemon = True
            self.trading_thread.start()
            self.logger.info("Trading engine démarré")

    def stop(self):
        """Arrête le trading engine"""
        self.running = False
        if hasattr(self, 'trading_thread'):
            self.trading_thread.join(timeout=5)
        self.logger.info("Trading engine arrêté")

    def get_status_dict(self):
        """Retourne le statut du trading engine pour l'API"""
        try:
            # Compter les positions avec la méthode centralisée
            active_positions = self.get_current_positions_count()

            # Métriques de base
            metrics = {
                'active_positions': active_positions,
                'max_positions': self.max_positions,
                'position_size_pct': self.position_size_pct,
                'balance': self.current_balance,
                'trading_mode': self.trading_mode,
                'running': self.running
            }

            # Statistiques des composants
            status = {
                'metrics': metrics,
                'trading_mode': self.trading_mode,
                'running': self.running,
                'engine_type': 'SimpleTradingEngine',
                'cooldown_enabled': self.cooldown_manager is not None,
                'trend_detection_enabled': self.trend_detector is not None
            }

            # Ajouter les stats du cooldown si disponible
            if self.cooldown_manager:
                cooldown_stats = self.cooldown_manager.get_cooldown_status()
                status['cooldown_stats'] = cooldown_stats

            return status

        except Exception as e:
            self.logger.error(f"Erreur get_status_dict: {e}")
            return {
                'metrics': {'active_positions': 0, 'error': str(e)},
                'trading_mode': self.trading_mode,
                'running': False,
                'engine_type': 'SimpleTradingEngine',
                'error': str(e)
            }