"""
EntropyBrain - Orquestador principal del sistema Entropic Core
100% FREE & OPEN SOURCE - NO RESTRICTIONS
"""

import logging
import time
from datetime import datetime
from typing import Any, Dict, List, Optional

from .config import Config
from .conversion.converter import ConversionEngine
from .conversion.tracker import UsageTracker
from .core.agent_adapter import AgentAdapter
from .core.entropy_monitor import EntropyMonitor
from .core.entropy_regulator import EntropyRegulator
from .core.evolutionary_memory import EvolutionaryMemory
from .messaging.messages import MessageBuilder
from .telemetry.collector import TelemetryCollector

logger = logging.getLogger(__name__)


class EntropyBrain:
    """
    Cerebro entrópico que orquesta todo el sistema.

    100% FREE & OPEN SOURCE - All features included, no tiers, no limits.

    API minimalista:
    - connect(agents): Conecta agentes al sistema
    - measure(): Mide la entropía actual
    - regulate(): Regula el sistema automáticamente
    - diagnose(): Análisis causal de problemas
    - forecast(): Predicción de entropía futura
    - run(): Ejecuta loop automático
    """

    def __init__(
        self,
        agents_or_db: Any = None,  # Support both agents list and db_path
        auto_regulate: bool = True,
        monitoring_interval: float = 5.0,
        user_friendly_messages: bool = True,
        **config_kwargs,
    ) -> None:
        """
        Inicializa el cerebro entrópico.

        Args:
            agents_or_db: Either a list of agents OR a db_path string
            auto_regulate: Si debe regular automáticamente
            monitoring_interval: Intervalo de monitoreo en segundos
            user_friendly_messages: Use business-friendly messages
            **config_kwargs: Configuración adicional personalizada
        """
        self.config = Config(**config_kwargs)

        import os

        use_postgres = bool(os.getenv("DATABASE_URL"))

        if isinstance(agents_or_db, list):
            # Called with agents list: EntropyBrain(agents)
            db_path = "entropy_memory.db"
            initial_agents = agents_or_db
        elif isinstance(agents_or_db, str) or agents_or_db is None:
            # Called with db_path: EntropyBrain(db_path)
            db_path = agents_or_db or "entropy_memory.db"
            initial_agents = []
        else:
            # Fallback
            db_path = "entropy_memory.db"
            initial_agents = []

        db_connection = os.getenv("DATABASE_URL", db_path)

        # Core modules (always available)
        self.monitor = EntropyMonitor()
        self.regulator = EntropyRegulator()
        self.memory = EvolutionaryMemory(db_connection, use_postgres=use_postgres)

        self.telemetry = TelemetryCollector()
        self.message_builder = MessageBuilder()
        self.user_friendly_messages = user_friendly_messages

        self.agents: List[Any] = []
        self.wrapped_agents: List[Any] = []

        self.auto_regulate = auto_regulate
        self.monitoring_interval = monitoring_interval
        self.running = False

        self.current_entropy = 0.0
        self.last_regulation: Optional[Dict[str, Any]] = None

        self.causal_analyzer: Optional[Any] = None
        self.predictive_engine: Optional[Any] = None

        self.usage_tracker = UsageTracker()
        self.conversion_engine = ConversionEngine(self.usage_tracker)

        if self._is_first_use():
            print(self.message_builder.value_proposition("first_use"))
            self._prompt_telemetry_opt_in()

        try:
            from .advanced.causal_analyzer import CausalAnalyzer
            from .advanced.predictive_engine import PredictiveEngine

            self.causal_analyzer = CausalAnalyzer(brain=self)
            self.predictive_engine = PredictiveEngine(brain=self)
            logger.info("All advanced modules loaded (100% FREE)")
        except ImportError as e:
            logger.warning(f"Some advanced modules not available: {e}")

        self.telemetry.track_event(
            "brain_initialized",
            {
                "auto_regulate": auto_regulate,
                "has_causal_analyzer": self.causal_analyzer is not None,
                "has_predictive_engine": self.predictive_engine is not None,
            },
        )

        if initial_agents:
            self.connect(initial_agents)

        logger.info(
            f"EntropyBrain initialized (auto_regulate={auto_regulate}, FREE & OPEN SOURCE)"
        )

    def _is_first_use(self) -> bool:
        """Check if this is the first time using Entropic Core"""
        from pathlib import Path

        marker_file = Path.home() / ".entropic" / "first_use_complete"

        if not marker_file.exists():
            marker_file.parent.mkdir(parents=True, exist_ok=True)
            marker_file.touch()
            return True
        return False

    def _prompt_telemetry_opt_in(self):
        """Prompt user for telemetry opt-in"""
        print("\n" + "=" * 60)
        print("Help improve Entropic Core (Optional)")
        print("=" * 60)
        print("\nWe'd love to understand how Entropic Core is used to make it better.")
        print("We collect ONLY anonymous usage data:")
        print("  • What features are used")
        print("  • System performance metrics")
        print("  • Error types (no personal data)")
        print("\nWe NEVER collect:")
        print("  ✗ Personal information")
        print("  ✗ Agent data or decisions")
        print("  ✗ API keys or credentials")
        print("\nEnable anonymous telemetry? (y/N): ", end="")

        try:
            response = input().strip().lower()
            if response in ("y", "yes"):
                TelemetryCollector.enable()
            else:
                print("\nNo problem! You can enable it later with:")
                print("  entropic-core telemetry enable\n")
        except (KeyboardInterrupt, EOFError):
            print("\nSkipping telemetry setup.\n")

    def connect(self, agents: List[Any]) -> None:
        """
        Conecta agentes al sistema entrópico.

        Args:
            agents: Lista de agentes a conectar (unlimited)
        """
        self.agents = agents

        # Envolver agentes con capacidades entrópicas
        self.wrapped_agents = [
            AgentAdapter.wrap_agent(agent, agent_id=f"agent_{i}")
            for i, agent in enumerate(agents)
        ]

        logger.info(f"Connected {len(agents)} agents (no limits)")

        self.telemetry.track_event("agents_connected", {"agent_count": len(agents)})

        # Log inicial
        self.memory.log_event(
            entropy=0.0,
            event_type="SYSTEM_INIT",
            outcome="SUCCESS",
            metadata={"agent_count": len(agents), "free_open_source": True},
        )

    def measure(self) -> float:
        """
        Mide la entropía actual del sistema.

        Returns:
            Float value of combined entropy (backward compatible)
        """
        if not self.wrapped_agents:
            return 0.0

        # Medir entropía
        metrics = self.monitor.measure_system_entropy(self.wrapped_agents)
        self.current_entropy = metrics["combined"]

        # Guardar en memoria
        self.memory.log_metrics(metrics, len(self.wrapped_agents))

        if self.user_friendly_messages:
            status = self.message_builder.entropy_status(self.current_entropy)
            logger.info(f"\n{status}\n")

        self.telemetry.track_event(
            "entropy_measured",
            {
                "entropy_value": round(self.current_entropy, 3),
                "agent_count": len(self.wrapped_agents),
            },
        )

        # Actualizar contexto entrópico en agentes
        for agent in self.wrapped_agents:
            if hasattr(agent, "set_entropy_context"):
                agent.set_entropy_context(
                    {"chaos_level": metrics["combined"], "metrics": metrics}
                )

        # Return float for backward compatibility (tests expect float)
        return self.current_entropy

    def regulate(self) -> Dict[str, Any]:
        """
        Regula el sistema basándose en la entropía actual.

        Returns:
            Decisión de regulación tomada
        """
        if not self.wrapped_agents:
            return {"action": "NO_AGENTS", "commands": []}

        # Tomar decisión de regulación
        decision = self.regulator.regulate(self.current_entropy, self.wrapped_agents)
        self.last_regulation = decision

        if self.user_friendly_messages and decision["action"] != "MAINTAIN":
            message = self.message_builder.regulation_action(decision["action"])
            logger.info(f"\n{message}\n")

            # Show value proposition for specific scenarios
            if self.current_entropy > 0.8:
                print(self.message_builder.value_proposition("chaos_detected"))
            elif self.current_entropy < 0.2:
                print(self.message_builder.value_proposition("stagnation_detected"))

        # Registrar en memoria
        self.memory.log_event(
            entropy=self.current_entropy,
            event_type=decision["action"],
            action=decision["action"],
            outcome=decision["message"],
            metadata={
                "commands": decision["commands"],
                "severity": decision["severity"],
            },
        )

        if decision["action"] not in ["MAINTAIN", "NO_AGENTS"]:
            import hashlib

            pattern_data = f"{self.current_entropy:.2f}_{decision['action']}"
            pattern_hash = hashlib.md5(pattern_data.encode()).hexdigest()[:16]

            self.memory.store_pattern(
                pattern_hash=pattern_hash,
                conditions={
                    "entropy_level": self.current_entropy,
                    "action": decision["action"],
                    "agent_count": len(self.wrapped_agents),
                },
                description=f"Regulation pattern: {decision['action']} at entropy {self.current_entropy:.2f}",
                success_rate=0.7,  # Initial success rate
            )

        self.telemetry.track_event(
            "regulation_performed",
            {
                "regulation_action": decision["action"],
                "entropy_value": round(self.current_entropy, 3),
            },
        )

        # Aplicar regulación a los agentes
        self.regulator.apply_regulation(self.wrapped_agents, decision)

        self.usage_tracker.track_regulation(decision["action"])

        conversion_msg = self.conversion_engine.check_and_show_message(
            {"entropy": self.current_entropy, "action": decision["action"]}
        )

        if conversion_msg:
            print(conversion_msg)

        return decision

    def diagnose(self) -> Dict[str, Any]:
        """
        Diagnostica causas raíz de problemas de entropía.
        100% FREE - Always available, no restrictions.

        Returns:
            Diagnóstico detallado con causas y soluciones
        """
        if not self.causal_analyzer:
            return {
                "error": "Causal analyzer not loaded. Install scipy and scikit-learn.",
                "install_command": "pip install scipy scikit-learn",
            }

        return self.causal_analyzer.find_root_cause(self.current_entropy)

    def forecast(self, steps: int = 10) -> Dict[str, Any]:
        """
        Predice la entropía futura del sistema.
        100% FREE - Always available, no restrictions.

        Args:
            steps: Número de pasos a predecir

        Returns:
            Predicción con intervalos de confianza
        """
        if not self.predictive_engine:
            return {
                "error": "Predictive engine not loaded. Install scipy and scikit-learn.",
                "install_command": "pip install scipy scikit-learn",
            }

        return self.predictive_engine.forecast_system_health(steps=steps)

    def detect_anomalies(self) -> List[Dict[str, Any]]:
        """
        Detecta anomalías en patrones de entropía.
        100% FREE - Always available, no restrictions.

        Returns:
            Lista de anomalías detectadas
        """
        if not self.predictive_engine:
            return []

        return self.predictive_engine.detect_anomalies()

    def log_interaction(self, context: Any, response: Any, entropy: float):
        """
        Registra una interacción en la memoria.

        Args:
            context: Contexto de la interacción
            response: Respuesta generada
            entropy: Entropía en ese momento
        """
        self.memory.log_event(
            entropy=entropy,
            event_type="INTERACTION",
            outcome=str(response)[:100],
            metadata={"context": str(context)[:100]},
        )

    def run_cycle(self) -> Dict[str, Any]:
        """
        Ejecuta un ciclo completo: medir + regular + analizar + predecir.

        Returns:
            Resultados del ciclo
        """
        self.usage_tracker.track_cycle()

        # Medir entropía
        metrics = self.measure()

        # Decidir si regular
        decision = None
        if self.auto_regulate:
            decision = self.regulate()

        diagnosis = None
        forecast = None

        if self.causal_analyzer and metrics > 0.7:
            diagnosis = self.diagnose()

        if self.predictive_engine and self.monitor.get_trend() == "increasing":
            forecast = self.forecast(steps=5)

        self.telemetry.track_event(
            "cycle_completed",
            {
                "entropy_value": round(metrics, 3),
                "regulation_action": decision["action"] if decision else "none",
                "has_diagnosis": diagnosis is not None,
                "has_forecast": forecast is not None,
            },
        )

        return {
            "timestamp": datetime.now(),
            "entropy": metrics,
            "regulation": decision,
            "trend": self.monitor.get_trend(),
            "diagnosis": diagnosis,
            "forecast": forecast,
        }

    def run(
        self, duration: Optional[float] = None, cycles: Optional[int] = None
    ) -> None:
        """
        Ejecuta el loop de monitoreo automático.

        Args:
            duration: Duración en segundos (None = infinito)
            cycles: Número de ciclos (None = infinito)
        """
        self.running = True
        start_time = time.time()
        cycle_count = 0

        logger.info(f"Starting monitoring loop (100% FREE)...")
        logger.info(f"  Interval: {self.monitoring_interval}s")
        logger.info(f"  Auto-regulate: {self.auto_regulate}")
        logger.info(f"  All features enabled: YES")

        try:
            while self.running:
                results = self.run_cycle()
                cycle_count += 1

                entropy = results["entropy"]
                trend = results["trend"]

                if results["regulation"]:
                    action = results["regulation"]["action"]
                    message = results["regulation"]["message"]
                    logger.info(
                        f"[Cycle {cycle_count}] Entropy: {entropy:.3f} ({trend}) -> {action}"
                    )
                    logger.debug(f"  {message}")
                else:
                    logger.info(
                        f"[Cycle {cycle_count}] Entropy: {entropy:.3f} ({trend})"
                    )

                if results.get("diagnosis"):
                    diag = results["diagnosis"]
                    logger.info(
                        f"  DIAGNOSIS: {diag.get('primary_cause')} (confidence: {diag.get('confidence', 0):.2f})"
                    )

                if results.get("forecast"):
                    forecast = results["forecast"]
                    risk = forecast.get("risk_level", "UNKNOWN")
                    logger.info(f"  FORECAST: Risk level {risk}")

                if duration and (time.time() - start_time) >= duration:
                    logger.info("Duration limit reached")
                    break

                if cycles and cycle_count >= cycles:
                    logger.info("Cycle limit reached")
                    break

                time.sleep(self.monitoring_interval)

        except KeyboardInterrupt:
            logger.info("Interrupted by user")
        finally:
            self.running = False
            logger.info(f"Stopped after {cycle_count} cycles")

    def stop(self):
        """Detiene el loop de monitoreo."""
        self.running = False

    def get_status(self) -> Dict[str, Any]:
        """Retorna el estado actual del sistema."""
        return {
            "current_entropy": self.current_entropy,
            "agent_count": len(self.agents),
            "last_regulation": self.last_regulation,
            "entropy_trend": self.monitor.get_trend(),
            "free_open_source": True,
            "all_features_enabled": True,
        }

    def get_insights(self) -> Dict[str, Any]:
        """Genera insights basados en la memoria acumulada."""
        recent_events = self.memory.get_recent_events(limit=10)
        patterns = self.memory.find_similar_patterns({}, limit=5)
        metrics_history = self.memory.get_metrics_history(hours=24)

        insights = {
            "recent_events": recent_events,
            "learned_patterns": patterns,
            "metrics_summary": self._summarize_metrics(metrics_history),
        }

        if self.causal_analyzer:
            insights["detected_patterns"] = (
                self.causal_analyzer.detect_entropy_patterns()
            )

        if self.predictive_engine:
            insights["anomalies"] = self.predictive_engine.detect_anomalies()

        return insights

    def _summarize_metrics(self, metrics_history: List[Dict]) -> Dict[str, Any]:
        """Genera resumen de métricas históricas"""
        if not metrics_history:
            return {}

        import numpy as np

        combined_values = [
            m["combined"] for m in metrics_history if m["combined"] is not None
        ]

        if not combined_values:
            return {}

        return {
            "avg_entropy": float(np.mean(combined_values)),
            "max_entropy": float(np.max(combined_values)),
            "min_entropy": float(np.min(combined_values)),
            "std_entropy": float(np.std(combined_values)),
            "data_points": len(combined_values),
        }

    def close(self) -> None:
        """Cierra el cerebro y libera recursos."""
        self.stop()
        if self.memory:
            self.memory.close()

        if self.user_friendly_messages:
            print("\n" + "=" * 60)
            print("Entropic Core Session Summary")
            print("=" * 60)
            insights = self.get_insights()
            print(f"Total cycles run: {len(insights.get('recent_events', []))}")
            print(
                f"Average entropy: {insights.get('metrics_summary', {}).get('avg_entropy', 0):.3f}"
            )
            print(
                f"Regulations performed: {sum(1 for e in insights.get('recent_events', []) if e.get('event_type') in ['REDUCE_CHAOS', 'INCREASE_CHAOS'])}"
            )
            print("\nThank you for using Entropic Core!")
            print("=" * 60 + "\n")

        logger.info("EntropyBrain closed")

    def log(self) -> None:
        """Log current state to memory (backward compatible method)"""
        if hasattr(self, "memory") and self.current_entropy is not None:
            self.memory.log_event(
                entropy=self.current_entropy,
                event_type="LOG",
                outcome="logged",
                metadata={"agents": len(self.wrapped_agents)},
            )
            self.memory.log_metrics(
                {
                    "combined": self.current_entropy,
                    "communication": self.current_entropy * 0.8,
                    "decision": self.current_entropy * 0.9,
                    "resource": self.current_entropy * 0.7,
                },
                len(self.wrapped_agents),
            )

    def get_recent_events(self, limit: int = 10) -> List[Dict[str, Any]]:
        """Get recent events from memory"""
        return self.memory.get_recent_events(limit=limit)

    def find_similar_patterns(
        self, pattern: Dict[str, Any], limit: int = 5
    ) -> List[Dict[str, Any]]:
        """Find similar patterns in memory"""
        return self.memory.find_similar_patterns(pattern, limit=limit)

    def get_metrics_history(self, hours: int = 24) -> List[Dict[str, Any]]:
        """
        Get metrics history from memory.

        Args:
            hours: Number of hours to look back

        Returns:
            List of metrics dictionaries
        """
        if not hasattr(self, "memory") or not self.memory:
            return []

        # Get recent events and convert to metrics format
        events = self.memory.get_recent_events(limit=1000)

        # Filter to recent time window
        from datetime import datetime, timedelta

        cutoff_time = datetime.now() - timedelta(hours=hours)

        metrics = []
        for event in events:
            if "timestamp" in event:
                try:
                    event_time = datetime.fromisoformat(event["timestamp"])
                    if event_time >= cutoff_time:
                        metrics.append(
                            {
                                "timestamp": event["timestamp"],
                                "combined": event.get("entropy", 0.5),
                                "chaos": event.get("entropy", 0.5),
                                "decision": 0.0,
                                "state": 0.0,
                            }
                        )
                except (ValueError, TypeError):
                    pass

        return metrics
