"""
AutoMobiu - Automatic Configuration for Mobiu-Q
================================================

Version: 1.0.0
מנגנון זיהוי אוטומטי מבוסס חימום.
"""

import numpy as np
from typing import Dict, List, Optional, Union, Callable
from collections import deque
import warnings
import time

# ייבוא מקומי מאותה תיקייה
from .core import MobiuOptimizer, MobiuQCore, Demeasurement
from .core import AVAILABLE_OPTIMIZERS, DEFAULT_OPTIMIZER, METHOD_ALIASES

class AutoMobiu:
    """AutoMobiu - מנגנון זיהוי אוטומטי של פרמטרי אופטימיזציה."""
    
    def __init__(
        self,
        optimizer_or_params,
        license_key: str,
        warmup_steps: int = 5,
        auto_start: bool = True,
        verbose: bool = True,
    ):
        self.license_key = license_key
        self.warmup_steps = warmup_steps
        self.verbose = verbose
        
        # נתוני חימום
        self.energy_history = []
        self.gradient_history = []
        self.warmup_complete = False
        self.detected_params = {}
        
        # אבחנה אם זה PyTorch או Quantum
        self._is_pytorch = (
            hasattr(optimizer_or_params, 'step') and 
            hasattr(optimizer_or_params, 'param_groups')
        )
        
        if self._is_pytorch:
            self.optimizer = optimizer_or_params
            self.base_lr = optimizer_or_params.param_groups[0]['lr']
        else:
            self.params = np.asarray(optimizer_or_params)
            self.base_lr = None
        
        if auto_start and not self._is_pytorch:
            warnings.warn(
                "AutoMobiu: Quantum mode דורש חימום ידני. השתמש ב-run_warmup()",
                UserWarning
            )
    
    def run_warmup(self, energy_fn: Callable, gradient_fn: Optional[Callable] = None) -> Dict:
        """הרץ חימום ונתח את הבעיה."""
        if self.verbose:
            print(f"🔥 AutoMobiu: מריץ חימום ({self.warmup_steps} צעדים)...")
        
        params = self.params.copy()
        
        # שלב חימום
        for step in range(self.warmup_steps):
            energy = energy_fn(params)
            self.energy_history.append(energy)
            
            if gradient_fn:
                grad = gradient_fn(params)
            else:
                # SPSA אם אין גרדיאנט
                delta = np.random.choice([-1, 1], size=params.shape)
                eps = 0.01
                energy_plus = energy_fn(params + eps * delta)
                energy_minus = energy_fn(params - eps * delta)
                grad = (energy_plus - energy_minus) / (2 * eps) * delta
            
            self.gradient_history.append(grad)
            params = params - 0.01 * grad
            
            if self.verbose:
                print(f"  Step {step}: energy = {energy:.6f}")
        
        return self._analyze_data()
    
    def _analyze_data(self) -> Dict:
        """ניתוח נתוני חימום לקביעת פרמטרים."""
        if len(self.energy_history) < 2:
            return self._get_default_config()
        
        energies = np.array(self.energy_history)
        gradients = np.array(self.gradient_history)
        
        energy_mean = np.mean(energies)
        energy_std = np.std(energies)
        energy_range = np.ptp(energies)
        
        grad_norms = [np.linalg.norm(g) for g in gradients]
        grad_mean = np.mean(grad_norms)
        grad_std = np.std(grad_norms)
        
        if len(energies) >= 3:
            x = np.arange(len(energies))
            slope = np.polyfit(x, energies, 1)[0]
        else:
            slope = energies[-1] - energies[0]
        
        problem_type, confidence = self._identify_problem_type(
            energies, gradients, slope, energy_std, grad_mean
        )
        
        config = self._get_config_for_problem(problem_type)
        
        if grad_mean > 0:
            suggested_lr = 0.01 / grad_mean
            config["base_lr"] = float(np.clip(suggested_lr, 1e-5, 0.1))
        
        config.update({
            "problem_type": problem_type,
            "confidence": confidence,
            "energy_stats": {
                "mean": float(energy_mean),
                "std": float(energy_std),
                "range": float(energy_range),
                "trend": float(slope)
            },
            "gradient_stats": {
                "mean_norm": float(grad_mean),
                "std_norm": float(grad_std)
            }
        })
        
        self.detected_params = config
        self.warmup_complete = True
        
        if self.verbose:
            self._print_analysis(config)
        
        return config
    
    def _identify_problem_type(self, energies, gradients, slope, energy_std, grad_mean):
        """זיהוי סוג הבעיה לפי נתונים סטטיסטיים."""
        energy_mean = np.mean(energies)
        
        if energy_mean < -0.5 and abs(slope) < 0.1 and grad_mean < 1.0:
            return "VQE/Chemistry", 0.85
        
        if energy_std > 0.5 * abs(energy_mean):
            return "QAOA/Combinatorial", 0.80
        
        if energy_mean > 0 and energy_std > 0.3 * energy_mean:
            return "RL/Trading", 0.75
        
        if energy_mean > 0 and energy_std < 0.2 * energy_mean and slope < 0:
            return "LLM/Deep Learning", 0.80
        
        grad_stds = [np.std(g) for g in gradients.T]
        if np.mean(grad_stds) > 0.5 * grad_mean:
            return "Federated Learning", 0.70
        
        if energy_std > 0.3 * abs(energy_mean) and abs(slope) < 0.05:
            return "Imbalanced Data", 0.65
        
        return "General Optimization", 0.60
    
    def _get_config_for_problem(self, problem_type: str) -> Dict:
        """החזר קונפיגורציה מומלצת לפי סוג הבעיה."""
        configs = {
            "VQE/Chemistry": {
                "method": "standard",
                "maximize": False,
                "mode": "simulation",
                "base_lr": 0.01,
                "base_optimizer": "Adam",
                "use_soft_algebra": True
            },
            "QAOA/Combinatorial": {
                "method": "deep",
                "maximize": False,
                "mode": "simulation",
                "base_lr": 0.1,
                "base_optimizer": "NAdam",
                "use_soft_algebra": True
            },
            "RL/Trading": {
                "method": "adaptive",
                "maximize": True,
                "mode": "simulation",
                "base_lr": 0.0003,
                "base_optimizer": "Momentum",
                "use_soft_algebra": True
            },
            "LLM/Deep Learning": {
                "method": "adaptive",
                "maximize": False,
                "mode": "simulation",
                "base_lr": 0.0003,
                "base_optimizer": "AdamW",
                "use_soft_algebra": True
            },
            "Federated Learning": {
                "method": "standard",
                "maximize": False,
                "mode": "simulation",
                "base_lr": 0.01,
                "base_optimizer": "Adam",
                "use_soft_algebra": True
            },
            "Imbalanced Data": {
                "method": "standard",
                "maximize": False,
                "mode": "simulation",
                "base_lr": 0.01,
                "base_optimizer": "Adam",
                "use_soft_algebra": True
            },
            "General Optimization": {
                "method": "adaptive",
                "maximize": False,
                "mode": "simulation",
                "base_lr": 0.01,
                "base_optimizer": "Adam",
                "use_soft_algebra": True
            }
        }
        
        return configs.get(problem_type, configs["General Optimization"]).copy()
    
    def _get_default_config(self):
        return {
            "method": "adaptive",
            "maximize": False,
            "mode": "simulation",
            "base_lr": 0.01,
            "base_optimizer": "Adam",
            "use_soft_algebra": True,
            "problem_type": "Unknown",
            "confidence": 0.5
        }
    
    def _print_analysis(self, config: Dict):
        print("\n" + "="*60)
        print("🤖 AutoMobiu - ניתוח אוטומטי הושלם!")
        print("="*60)
        print(f"📊 זוהתה בעיה: {config['problem_type']}")
        print(f"   רמת ביטחון: {config['confidence']:.1%}")
        
        stats = config.get('energy_stats', {})
        if stats:
            print(f"📈 סטטיסטיקות אנרגיה:")
            print(f"   ממוצע: {stats.get('mean', 0):.6f}")
            print(f"   סטיית תקן: {stats.get('std', 0):.6f}")
            print(f"   מגמה: {stats.get('trend', 0):.6f} (שלילי = שיפור)")
        
        print(f"⚙️  הגדרות מומלצות:")
        print(f"   Method: {config['method']}")
        print(f"   Maximize: {config['maximize']}")
        print(f"   Mode: {config['mode']}")
        print(f"   Learning Rate: {config['base_lr']}")
        print(f"   Optimizer: {config['base_optimizer']}")
        print("="*60)
    
    def set_custom_config(self, **kwargs):
        """הגדר קונפיגורציה מותאמת אישית."""
        valid_keys = ["method", "maximize", "mode", "base_lr", 
                     "base_optimizer", "use_soft_algebra"]
        
        for key, value in kwargs.items():
            if key in valid_keys:
                self.detected_params[key] = value
        
        if self.verbose:
            print(f"✅ AutoMobiu: קונפיגורציה מותאמת אישית הוגדרה")
    
    def create_optimizer(self):
        """צור את ה-MobiuOptimizer עם ההגדרות שזוהו."""
        if not self.warmup_complete and not self._is_pytorch:
            raise RuntimeError(
                "AutoMobiu: הרץ קודם את run_warmup() או השתמש ב-PyTorch mode"
            )
        
        if self._is_pytorch and not self.detected_params:
            self.detected_params = self._get_default_config()
            if self.base_lr:
                self.detected_params["base_lr"] = self.base_lr
        
        config = self.detected_params.copy()
        
        for key in ["problem_type", "confidence", "energy_stats", "gradient_stats"]:
            config.pop(key, None)
        
        if self._is_pytorch:
            optimizer = MobiuOptimizer(
                self.optimizer,
                license_key=self.license_key,
                **config
            )
        else:
            optimizer = MobiuOptimizer(
                self.params,
                license_key=self.license_key,
                **config
            )
        
        if self.verbose:
            print(f"✅ AutoMobiu: אופטימייזר נוצר עם הגדרות אופטימליות")
        
        return optimizer


def automobiu(
    optimizer_or_params,
    license_key: str,
    warmup_steps: int = 5,
    energy_fn: Optional[Callable] = None,
    gradient_fn: Optional[Callable] = None,
    **kwargs
):
    """
    פונקציית עזר ליצירת AutoMobiu והפעלה אוטומטית.
    
    דוגמאות:
        # PyTorch
        opt = automobiu(base_opt, license_key="xxx")
        
        # Quantum עם חימום
        opt = automobiu(params, license_key="xxx", energy_fn=my_energy_function)
    """
    auto = AutoMobiu(
        optimizer_or_params,
        license_key=license_key,
        warmup_steps=warmup_steps,
        verbose=kwargs.get('verbose', True)
    )
    
    if hasattr(optimizer_or_params, 'step'):
        opt = auto.create_optimizer()
    else:
        if energy_fn is None:
            raise ValueError("energy_fn required for quantum mode")
        auto.run_warmup(energy_fn, gradient_fn)
        opt = auto.create_optimizer()
    
    return opt


__all__ = ["AutoMobiu", "automobiu"]