#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Module d'analyse technique approfondie pour crypto-monnaies
Analyse RSI, Bollinger Bands, EMA, Momentum et positions actives
"""

import requests
import numpy as np
from datetime import datetime, timedelta
import json
from typing import Dict, List, Optional, Tuple
import logging

logger = logging.getLogger(__name__)


class TechnicalAnalyzer:
    """Analyseur technique pour crypto-monnaies"""
    
    def __init__(self):
        self.binance_api = "https://api.binance.com/api/v3"
        
    def analyze_symbol(self, symbol: str) -> Dict:
        """
        Analyse technique complète d'un symbole
        
        Args:
            symbol: Symbole crypto (ex: BTCUSDT)
            
        Returns:
            Dict avec toutes les données d'analyse
        """
        try:
            # Normaliser le symbole
            if not symbol.endswith('USDT'):
                symbol = f"{symbol}USDT"
            symbol = symbol.upper()
            
            # Récupérer données de marché
            klines_1h = self._get_klines(symbol, '1h', 100)
            klines_5m = self._get_klines(symbol, '5m', 100)
            
            if not klines_1h or not klines_5m:
                return {
                    'success': False,
                    'error': f'Impossible de récupérer les données pour {symbol}'
                }
            
            # Calculer indicateurs
            current_price = float(klines_1h[-1][4])  # Close
            rsi = self._calculate_rsi([float(k[4]) for k in klines_5m], period=14)
            bb_data = self._calculate_bollinger_bands([float(k[4]) for k in klines_1h], period=20)
            ema_data = self._calculate_ema([float(k[4]) for k in klines_1h])
            momentum = self._calculate_momentum([float(k[4]) for k in klines_5m], period=3)
            
            # Données 24h
            high_24h = max([float(k[2]) for k in klines_1h[-24:]])
            low_24h = min([float(k[3]) for k in klines_1h[-24:]])
            range_24h = high_24h - low_24h
            position_in_range = ((current_price - low_24h) / range_24h * 100) if range_24h > 0 else 50
            
            # Vérifier position active
            position_data = self._get_position_data(symbol)
            
            # Générer prédiction
            prediction = self._generate_prediction(
                symbol, current_price, rsi, bb_data, ema_data, momentum,
                high_24h, low_24h, position_data
            )
            
            return {
                'success': True,
                'symbol': symbol,
                'timestamp': datetime.now().isoformat(),
                'price': {
                    'current': round(current_price, 8),
                    'high_24h': round(high_24h, 8),
                    'low_24h': round(low_24h, 8),
                    'position_in_range': round(position_in_range, 2)
                },
                'indicators': {
                    'rsi': round(rsi, 2),
                    'rsi_zone': self._get_rsi_zone(rsi),
                    'bb_upper': round(bb_data['upper'], 8),
                    'bb_middle': round(bb_data['middle'], 8),
                    'bb_lower': round(bb_data['lower'], 8),
                    'bb_position': round(bb_data['position'], 4),
                    'bb_zone': self._get_bb_zone(bb_data['position']),
                    'ema9': round(ema_data['ema9'], 8),
                    'ema21': round(ema_data['ema21'], 8),
                    'ema_diff_pct': round(ema_data['diff_pct'], 4),
                    'ema_trend': ema_data['trend'],
                    'momentum': round(momentum, 6),
                    'momentum_pct': round(momentum * 100, 4)
                },
                'position': position_data,
                'prediction': prediction
            }
            
        except Exception as e:
            logger.error(f"Erreur analyse {symbol}: {e}")
            return {
                'success': False,
                'error': str(e)
            }
    
    def _get_klines(self, symbol: str, interval: str, limit: int) -> List:
        """Récupère les klines (chandeliers) de Binance"""
        try:
            url = f"{self.binance_api}/klines"
            params = {
                'symbol': symbol,
                'interval': interval,
                'limit': limit
            }
            response = requests.get(url, params=params, timeout=10)
            response.raise_for_status()
            return response.json()
        except Exception as e:
            logger.error(f"Erreur récupération klines {symbol}: {e}")
            return []
    
    def _calculate_rsi(self, prices: List[float], period: int = 14) -> float:
        """Calcule le RSI (Relative Strength Index)"""
        if len(prices) < period + 1:
            return 50.0
        
        deltas = np.diff(prices)
        gains = np.where(deltas > 0, deltas, 0)
        losses = np.where(deltas < 0, -deltas, 0)
        
        avg_gain = np.mean(gains[-period:])
        avg_loss = np.mean(losses[-period:])
        
        if avg_loss == 0:
            return 100.0
        
        rs = avg_gain / avg_loss
        rsi = 100 - (100 / (1 + rs))
        
        return rsi
    
    def _calculate_bollinger_bands(self, prices: List[float], period: int = 20, std_dev: float = 2.0) -> Dict:
        """Calcule les Bollinger Bands"""
        if len(prices) < period:
            return {'upper': 0, 'middle': 0, 'lower': 0, 'position': 0.5}
        
        recent_prices = prices[-period:]
        middle = np.mean(recent_prices)
        std = np.std(recent_prices)
        
        upper = middle + (std_dev * std)
        lower = middle - (std_dev * std)
        
        current_price = prices[-1]
        
        # Position dans les bandes (0 = lower, 0.5 = middle, 1 = upper)
        if upper != lower:
            position = (current_price - lower) / (upper - lower)
        else:
            position = 0.5
        
        return {
            'upper': upper,
            'middle': middle,
            'lower': lower,
            'position': position
        }
    
    def _calculate_ema(self, prices: List[float]) -> Dict:
        """Calcule EMA 9 et EMA 21"""
        if len(prices) < 21:
            return {'ema9': 0, 'ema21': 0, 'diff_pct': 0, 'trend': 'NEUTRE'}
        
        def ema(data, period):
            return np.mean(data[-period:])  # Simplified EMA for speed
        
        ema9 = ema(prices, 9)
        ema21 = ema(prices, 21)
        
        diff_pct = ((ema9 - ema21) / ema21 * 100) if ema21 != 0 else 0
        
        if diff_pct > 0.1:
            trend = 'HAUSSIER'
        elif diff_pct < -0.1:
            trend = 'BAISSIER'
        else:
            trend = 'NEUTRE'
        
        return {
            'ema9': ema9,
            'ema21': ema21,
            'diff_pct': diff_pct,
            'trend': trend
        }
    
    def _calculate_momentum(self, prices: List[float], period: int = 3) -> float:
        """Calcule le momentum sur N périodes"""
        if len(prices) < period + 1:
            return 0.0
        
        momentum = (prices[-1] - prices[-(period+1)]) / prices[-(period+1)]
        return momentum
    
    def _get_rsi_zone(self, rsi: float) -> str:
        """Détermine la zone RSI"""
        if rsi < 30:
            return 'SURVENTE'
        elif rsi < 45:
            return 'ZONE ACHAT'
        elif rsi < 55:
            return 'NEUTRE'
        elif rsi < 70:
            return 'ZONE VENTE'
        else:
            return 'SURACHAT'
    
    def _get_bb_zone(self, bb_position: float) -> str:
        """Détermine la zone BB"""
        if bb_position < 0.0:
            return 'SOUS BANDE BASSE'
        elif bb_position < 0.4:
            return 'ZONE ACHAT'
        elif bb_position < 0.6:
            return 'ZONE NEUTRE'
        elif bb_position < 1.0:
            return 'ZONE VENTE'
        else:
            return 'AU-DESSUS BANDE HAUTE'
    
    def _get_position_data(self, symbol: str) -> Optional[Dict]:
        """Récupère les données de position active si elle existe"""
        try:
            # Lire le fichier positions.json (positions actives)
            import os
            positions_file = os.path.join(os.path.dirname(__file__), 'positions.json')
            
            if not os.path.exists(positions_file):
                return {'active': False}
            
            with open(positions_file, 'r') as f:
                positions = json.load(f)
            
            # Normaliser le symbole
            if not symbol.endswith('USDT'):
                symbol = f"{symbol}USDT"
            symbol = symbol.upper()
            
            # Chercher la position pour ce symbole
            if symbol in positions:
                pos = positions[symbol]
                entry_price = pos.get('entry_price', 0)
                entry_time = pos.get('timestamp', '')
                
                # Calculer durée
                duration_minutes = 0
                try:
                    if entry_time:
                        entry_dt = datetime.fromisoformat(entry_time.replace('Z', '+00:00'))
                        duration_minutes = int((datetime.now() - entry_dt.replace(tzinfo=None)).total_seconds() / 60)
                except:
                    pass
                
                return {
                    'active': True,
                    'entry_price': entry_price,
                    'entry_time': entry_time,
                    'duration_minutes': duration_minutes,
                    'quantity': pos.get('quantity', 0),
                    'stop_loss': pos.get('stop_loss', 0),
                    'take_profit': pos.get('take_profit', 0)
                }
            
            return {'active': False}
            
        except Exception as e:
            logger.error(f"Erreur lecture position {symbol}: {e}")
            return {'active': False}
    
    def _generate_prediction(self, symbol: str, current_price: float, rsi: float, 
                           bb_data: Dict, ema_data: Dict, momentum: float,
                           high_24h: float, low_24h: float, position_data: Dict) -> Dict:
        """Génère une prédiction basée sur l'analyse technique"""
        
        # Calcul P&L si position active
        pnl_pct = 0
        if position_data.get('active'):
            entry_price = position_data['entry_price']
            pnl_pct = ((current_price - entry_price) / entry_price * 100) if entry_price > 0 else 0
        
        # Scoring des indicateurs
        score = 0
        signals = []
        
        # RSI
        if rsi < 30:
            score += 30
            signals.append({
                'type': 'BULLISH',
                'indicator': 'RSI',
                'value': rsi,
                'message': f'RSI survente ({rsi:.1f}) - Fort potentiel de rebond (75% historique)'
            })
        elif rsi < 45:
            score += 15
            signals.append({
                'type': 'BULLISH',
                'indicator': 'RSI',
                'value': rsi,
                'message': f'RSI zone achat ({rsi:.1f}) - Bon moment pour entrer'
            })
        elif rsi > 70:
            score -= 30
            signals.append({
                'type': 'BEARISH',
                'indicator': 'RSI',
                'value': rsi,
                'message': f'RSI surachat ({rsi:.1f}) - Risque de correction'
            })
        elif rsi > 55:
            score -= 15
            signals.append({
                'type': 'BEARISH',
                'indicator': 'RSI',
                'value': rsi,
                'message': f'RSI zone vente ({rsi:.1f}) - Prudence'
            })
        
        # Bollinger Bands
        bb_pos = bb_data['position']
        if bb_pos < 0:
            score += 25
            signals.append({
                'type': 'BULLISH',
                'indicator': 'BB',
                'value': bb_pos,
                'message': f'Prix sous bande basse ({bb_pos:.2f}) - Retour à la moyenne probable'
            })
        elif bb_pos < 0.4:
            score += 10
            signals.append({
                'type': 'BULLISH',
                'indicator': 'BB',
                'value': bb_pos,
                'message': f'Prix dans zone achat BB ({bb_pos:.2f})'
            })
        elif bb_pos > 1.0:
            score -= 25
            signals.append({
                'type': 'BEARISH',
                'indicator': 'BB',
                'value': bb_pos,
                'message': f'Prix au-dessus bande haute ({bb_pos:.2f}) - Retour à la moyenne probable'
            })
        elif bb_pos > 0.6:
            score -= 10
            signals.append({
                'type': 'BEARISH',
                'indicator': 'BB',
                'value': bb_pos,
                'message': f'Prix dans zone vente BB ({bb_pos:.2f})'
            })
        
        # EMA Trend
        if ema_data['trend'] == 'HAUSSIER':
            score += 15
            signals.append({
                'type': 'BULLISH',
                'indicator': 'EMA',
                'value': ema_data['diff_pct'],
                'message': f"Tendance haussière ({ema_data['diff_pct']:.2f}%)"
            })
        elif ema_data['trend'] == 'BAISSIER':
            score -= 15
            signals.append({
                'type': 'BEARISH',
                'indicator': 'EMA',
                'value': ema_data['diff_pct'],
                'message': f"Tendance baissière ({ema_data['diff_pct']:.2f}%)"
            })
        
        # Momentum
        momentum_pct = momentum * 100
        if momentum_pct > 0.5:
            score += 20
            signals.append({
                'type': 'BULLISH',
                'indicator': 'MOMENTUM',
                'value': momentum_pct,
                'message': f'Fort momentum positif ({momentum_pct:.2f}%)'
            })
        elif momentum_pct > 0.2:
            score += 10
            signals.append({
                'type': 'BULLISH',
                'indicator': 'MOMENTUM',
                'value': momentum_pct,
                'message': f'Momentum positif ({momentum_pct:.2f}%)'
            })
        elif momentum_pct < -0.5:
            score -= 20
            signals.append({
                'type': 'BEARISH',
                'indicator': 'MOMENTUM',
                'value': momentum_pct,
                'message': f'Fort momentum négatif ({momentum_pct:.2f}%)'
            })
        elif momentum_pct < -0.2:
            score -= 10
            signals.append({
                'type': 'BEARISH',
                'indicator': 'MOMENTUM',
                'value': momentum_pct,
                'message': f'Momentum négatif ({momentum_pct:.2f}%)'
            })
        
        # RÈGLE CRITIQUE: RSI survente + momentum négatif = DANGER
        if rsi < 30 and momentum < 0:
            score -= 50
            signals.append({
                'type': 'WARNING',
                'indicator': 'RSI+MOMENTUM',
                'value': None,
                'message': '⚠️ RSI survente + momentum négatif = CHUTE EN COURS - Ne pas acheter!'
            })
        
        # Déterminer probabilités
        if score > 50:
            probability_up = 75
            recommendation = 'ACHAT FORT'
            action = 'ACHETER' if not position_data.get('active') else 'CONSERVER'
        elif score > 20:
            probability_up = 65
            recommendation = 'ACHAT'
            action = 'ACHETER' if not position_data.get('active') else 'CONSERVER'
        elif score > -20:
            probability_up = 50
            recommendation = 'NEUTRE'
            action = 'ATTENDRE'
        elif score > -50:
            probability_up = 35
            recommendation = 'VENTE'
            action = 'VENDRE' if position_data.get('active') else 'NE PAS ACHETER'
        else:
            probability_up = 25
            recommendation = 'VENTE FORTE'
            action = 'VENDRE' if position_data.get('active') else 'ÉVITER'
        
        # Calculer objectifs
        tp_target = current_price * 1.015  # +1.5% TP conservateur
        sl_target = current_price * 0.98   # -2% SL
        
        # Estimation gain basée sur RSI et BB
        if rsi < 30 and bb_pos < 0.3:
            expected_gain_min = 0.5
            expected_gain_max = 2.0
            timeframe = '2-6 heures'
        elif rsi < 45 or bb_pos < 0.4:
            expected_gain_min = 0.3
            expected_gain_max = 1.0
            timeframe = '4-12 heures'
        else:
            expected_gain_min = 0.1
            expected_gain_max = 0.5
            timeframe = '6-24 heures'
        
        return {
            'score': score,
            'recommendation': recommendation,
            'action': action,
            'probability_up': probability_up,
            'probability_down': 100 - probability_up,
            'signals': signals,
            'targets': {
                'tp': round(tp_target, 8),
                'sl': round(sl_target, 8),
                'expected_gain_min': expected_gain_min,
                'expected_gain_max': expected_gain_max,
                'timeframe': timeframe
            },
            'position_pnl': round(pnl_pct, 2) if position_data.get('active') else None
        }


if __name__ == '__main__':
    # Test
    analyzer = TechnicalAnalyzer()
    result = analyzer.analyze_symbol('BTCUSDT')
    
    if result['success']:
        print(json.dumps(result, indent=2))
    else:
        print(f"Erreur: {result.get('error')}")
