#!/usr/bin/env python3
"""
Multi-Timeframe Analyzer - Analyse sur plusieurs échelles de temps
Améliore la fiabilité des signaux en détectant la convergence sur 5m, 15m, 1h, 4h
"""

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

logger = logging.getLogger("MultiTimeframeAnalyzer")

class MultiTimeframeAnalyzer:
    """Analyse multi-timeframe pour confirmation des signaux"""
    
    TIMEFRAMES = ['5m', '15m', '1h', '4h']
    
    # Poids par timeframe (plus le TF est élevé, plus son signal est important)
    TIMEFRAME_WEIGHTS = {
        '5m': 0.15,   # Court terme
        '15m': 0.25,  # Moyen-court terme
        '1h': 0.30,   # Moyen terme
        '4h': 0.30    # Long terme
    }
    
    def __init__(self):
        self.klines_fetcher = None
        logger.info("✅ Multi-Timeframe Analyzer initialisé")
    
    def set_klines_fetcher(self, fetcher):
        """Injecte le fetcher de klines depuis le bot"""
        self.klines_fetcher = fetcher
    
    def analyze_symbol_multi_tf(self, symbol: str, analyze_func) -> Dict:
        """
        Analyse un symbole sur tous les timeframes
        
        Args:
            symbol: Symbole à analyser
            analyze_func: Fonction d'analyse à appliquer (ex: analyze_symbol)
        
        Returns:
            Dict avec scores par timeframe et score final pondéré
        """
        if not self.klines_fetcher:
            logger.warning("⚠️ Klines fetcher non configuré pour multi-TF")
            return {'success': False, 'reason': 'no_fetcher'}
        
        results = {}
        scores = []
        weights = []
        
        try:
            for tf in self.TIMEFRAMES:
                # Récupérer les klines pour ce timeframe
                klines = self.klines_fetcher(symbol, interval=tf, limit=100)
                
                if not klines or len(klines) < 50:
                    continue
                
                # Extraire prix et volumes
                prices = [float(k[4]) for k in klines]  # Close prices
                volumes = [float(k[5]) for k in klines]
                
                # Analyser avec la fonction fournie
                analysis = analyze_func(symbol, prices, volumes)
                
                if analysis and hasattr(analysis, 'score'):
                    score = analysis.score
                    results[tf] = {
                        'score': score,
                        'pattern': getattr(analysis, 'pattern', ''),
                        'confidence': getattr(analysis, 'confidence', 0),
                        'status': getattr(analysis, 'status', 'watching')
                    }
                    
                    scores.append(score)
                    weights.append(self.TIMEFRAME_WEIGHTS[tf])
            
            if not scores:
                return {'success': False, 'reason': 'no_data'}
            
            # Calculer score pondéré
            weighted_score = sum(s * w for s, w in zip(scores, weights)) / sum(weights)
            
            # Détecter alignement multi-timeframe
            alignment_bonus = self._calculate_alignment_bonus(results)
            
            # Score final avec bonus d'alignement
            final_score = min(100, weighted_score + alignment_bonus)
            
            return {
                'success': True,
                'timeframes': results,
                'weighted_score': weighted_score,
                'alignment_bonus': alignment_bonus,
                'final_score': final_score,
                'aligned_count': sum(1 for r in results.values() if r['score'] > 60),
                'total_timeframes': len(results)
            }
            
        except Exception as e:
            logger.error(f"Erreur analyse multi-TF {symbol}: {e}")
            return {'success': False, 'reason': str(e)}
    
    def _calculate_alignment_bonus(self, results: Dict) -> float:
        """
        Calcule un bonus si les timeframes sont alignés
        
        Returns:
            Bonus de 0 à 25 points selon l'alignement
        """
        if len(results) < 3:
            return 0
        
        scores = [r['score'] for r in results.values()]
        
        # Tous les TF > 60 = fort alignement haussier
        if all(s > 60 for s in scores):
            return 25
        
        # Tous les TF > 50 = bon alignement
        if all(s > 50 for s in scores):
            return 15
        
        # Majorité > 60 = alignement modéré
        if sum(1 for s in scores if s > 60) >= len(scores) * 0.7:
            return 10
        
        # Divergence = pas de bonus
        return 0
    
    def get_trend_consistency(self, results: Dict) -> str:
        """
        Détermine la consistance de la tendance sur les timeframes
        
        Returns:
            'STRONG_BULLISH', 'BULLISH', 'NEUTRAL', 'BEARISH', 'STRONG_BEARISH'
        """
        if not results or 'timeframes' not in results:
            return 'NEUTRAL'
        
        tf_data = results['timeframes']
        scores = [r['score'] for r in tf_data.values()]
        
        avg_score = sum(scores) / len(scores) if scores else 50
        
        if avg_score > 70:
            return 'STRONG_BULLISH'
        elif avg_score > 55:
            return 'BULLISH'
        elif avg_score < 30:
            return 'STRONG_BEARISH'
        elif avg_score < 45:
            return 'BEARISH'
        else:
            return 'NEUTRAL'
    
    def should_enter_position(self, multi_tf_result: Dict) -> Tuple[bool, str]:
        """
        Décide si on doit entrer en position basé sur l'analyse multi-TF
        
        Returns:
            (should_enter: bool, reason: str)
        """
        if not multi_tf_result.get('success'):
            return False, "Données multi-TF insuffisantes"
        
        final_score = multi_tf_result.get('final_score', 0)
        aligned_count = multi_tf_result.get('aligned_count', 0)
        total_tf = multi_tf_result.get('total_timeframes', 0)
        
        # Minimum 3 timeframes analysés
        if total_tf < 3:
            return False, "Pas assez de timeframes"
        
        # Score final doit être élevé
        if final_score < 55:
            return False, f"Score multi-TF trop faible ({final_score:.1f})"
        
        # Au moins 70% des TF doivent être alignés
        alignment_ratio = aligned_count / total_tf if total_tf > 0 else 0
        if alignment_ratio < 0.7:
            return False, f"Alignement insuffisant ({alignment_ratio*100:.0f}%)"
        
        trend = self.get_trend_consistency(multi_tf_result)
        if trend in ['BEARISH', 'STRONG_BEARISH']:
            return False, f"Tendance baissière sur multi-TF ({trend})"
        
        return True, f"Signal confirmé sur {aligned_count}/{total_tf} TF (score={final_score:.1f})"


# Instance globale
_multi_tf_analyzer = None

def get_multi_tf_analyzer() -> MultiTimeframeAnalyzer:
    """Retourne l'instance globale de l'analyseur multi-timeframe"""
    global _multi_tf_analyzer
    if _multi_tf_analyzer is None:
        _multi_tf_analyzer = MultiTimeframeAnalyzer()
    return _multi_tf_analyzer
