"""
Main entry point for Openfund Agent

This module provides the command-line interface for starting and managing
the self-evolving trading agent system.
"""

import argparse
import logging
import signal
import sys
import time
from pathlib import Path
from typing import Optional

from openfund_agent.config.loader import ConfigLoader, ConfigLoadError, ConfigValidationError
from openfund_agent.logging.config import setup_logging
from openfund_agent.evolution.orchestrator import EvolutionOrchestrator
from openfund_agent.evolution.monitor import EvolutionMonitor
from openfund_agent.tools.manager import ToolManager
from openfund_agent.risk.controller import RiskController
from openfund_agent.risk.notification import NotificationService
from openfund_agent.models.evolution import EvolutionConfig, OptimizationGoal
from openfund_agent.modification.git_manager import GitManager
from openfund_agent.decision.engine import DecisionEngine


logger = logging.getLogger(__name__)


class AgentApplication:
    """
    Main application class for Openfund Agent

    This class manages the lifecycle of the agent application including:
    - Configuration loading
    - Component initialization
    - Evolution process management
    - Graceful shutdown
    """

    def __init__(self, config_path: Optional[str] = None, env: str = "development"):
        """
        Initialize the agent application

        Args:
            config_path: Path to configuration file (optional)
            env: Environment name (development, production)
        """
        self.config_path = config_path
        self.env = env
        self.config = None
        self.orchestrator: Optional[EvolutionOrchestrator] = None
        self.monitor: Optional[EvolutionMonitor] = None
        self.shutdown_requested = False

        # Setup signal handlers for graceful shutdown
        signal.signal(signal.SIGINT, self._signal_handler)
        signal.signal(signal.SIGTERM, self._signal_handler)

    def _signal_handler(self, signum, frame):
        """
        Handle shutdown signals

        Args:
            signum: Signal number
            frame: Current stack frame
        """
        signal_name = signal.Signals(signum).name
        logger.info(f"Received {signal_name} signal, initiating graceful shutdown...")
        self.shutdown_requested = True

        if self.monitor:
            self.monitor.stop_evolution()

    def initialize(self) -> bool:
        """
        Initialize all components

        Returns:
            True if initialization successful, False otherwise
        """
        try:
            logger.info("Initializing Openfund Agent...")

            # Load configuration
            logger.info(f"Loading configuration (environment: {self.env})")
            config_loader = ConfigLoader(config_path=self.config_path, env=self.env)
            self.config = config_loader.load()
            logger.info("Configuration loaded successfully")

            # Setup logging based on configuration
            setup_logging(
                level=self.config.logging.level,
                log_file=self.config.logging.file_path,
                log_format=self.config.logging.format,
                max_bytes=self.config.logging.max_file_size_mb * 1024 * 1024,
                backup_count=self.config.logging.backup_count,
            )
            logger.info("Logging configured")

            # Initialize Git manager
            workspace_path = Path.cwd()
            git_manager = GitManager(str(workspace_path))
            logger.info(f"Git manager initialized (workspace: {workspace_path})")

            # Initialize Claude client (placeholder - needs actual implementation)
            # In production, this would use the actual Claude SDK
            claude_client = self._create_claude_client()
            logger.info("Claude client initialized")

            # Initialize Decision Engine
            decision_engine = DecisionEngine(
                claude_client=claude_client, model_version=self.config.claude.model_version
            )
            logger.info("Decision engine initialized")

            # Initialize Tool Manager
            tool_config = {
                "exchange_config": {
                    "name": self.config.exchange.name,
                    "api_key": self.config.exchange.api_key,
                    "api_secret": self.config.exchange.api_secret,
                    "api_password": self.config.exchange.api_password,
                    "testnet": self.config.exchange.testnet,
                    "timeout": self.config.exchange.timeout,
                },
                "cache_ttl": self.config.data_collection.cache_ttl_minutes,
                "maker_config": {
                    "default_strategy": self.config.strategy.default_strategy,
                    "execution_timeout": self.config.strategy.execution_timeout,
                },
                "taker_config": {
                    "max_concurrent_strategies": self.config.strategy.max_concurrent_strategies,
                },
                "exchange_client": None,  # Placeholder - needs actual exchange client
                "claude_client": claude_client,
                "workspace_path": str(workspace_path),
                "git_manager": git_manager,
                "model_version": self.config.claude.model_version,
                "decision_engine": decision_engine,
            }

            tool_manager = ToolManager(tool_config)
            logger.info(
                f"Tool manager initialized with {len(tool_manager.get_registered_tool_names())} tools"
            )

            # Initialize Notification Service
            notification_service = NotificationService(
                feishu_webhook=self.config.notification.feishu_webhook,
                enabled=self.config.notification.enabled,
            )
            logger.info("Notification service initialized")

            # Initialize Risk Controller
            risk_controller = RiskController(
                config=self.config.risk, notification_service=notification_service
            )
            logger.info("Risk controller initialized")

            # Initialize Evolution Orchestrator
            evolution_config = EvolutionConfig(
                symbol="BTC/USDT:USDT",  # Default symbol, can be overridden
                timeframe="1h",  # Default timeframe, can be overridden
                max_iterations=self.config.evolution.max_iterations,
                workspace_path=str(workspace_path),
            )

            self.orchestrator = EvolutionOrchestrator(
                config=evolution_config,
                tool_manager=tool_manager,
                risk_controller=risk_controller,
                notification_service=notification_service,
            )
            logger.info("Evolution orchestrator initialized")

            # Initialize Evolution Monitor
            self.monitor = EvolutionMonitor(self.orchestrator)
            logger.info("Evolution monitor initialized")

            logger.info("Openfund Agent initialization complete")
            return True

        except (ConfigLoadError, ConfigValidationError) as e:
            logger.error(f"Configuration error: {e}")
            return False
        except Exception as e:
            logger.error(f"Initialization failed: {e}", exc_info=True)
            return False

    def _create_claude_client(self):
        """
        Create Claude API client

        Returns:
            Claude client instance (placeholder)

        Note:
            This is a placeholder. In production, this would initialize
            the actual Claude SDK client with proper authentication.
        """
        # Placeholder implementation
        # In production, use: from anthropic import Anthropic
        # return Anthropic(api_key=self.config.claude.api_key)

        class MockClaudeClient:
            """Mock Claude client for development"""

            def __init__(self, api_key: str, model_version: str):
                self.api_key = api_key
                self.model_version = model_version

            def messages_create(self, **kwargs):
                """Mock message creation"""
                return {"content": "Mock response"}

        return MockClaudeClient(
            api_key=self.config.claude.api_key, model_version=self.config.claude.model_version
        )

    def start_evolution(
        self, symbol: str, timeframe: str, goal_metric: str, goal_value: float
    ) -> bool:
        """
        Start the evolution process

        Args:
            symbol: Trading symbol (e.g., "BTC/USDT:USDT")
            timeframe: Timeframe (e.g., "1h", "4h")
            goal_metric: Optimization goal metric (e.g., "sharpe_ratio")
            goal_value: Target value for the metric

        Returns:
            True if evolution completed successfully, False otherwise
        """
        if not self.orchestrator:
            logger.error("Orchestrator not initialized. Call initialize() first.")
            return False

        try:
            logger.info("=" * 80)
            logger.info("Starting Evolution Process")
            logger.info("=" * 80)
            logger.info(f"Symbol: {symbol}")
            logger.info(f"Timeframe: {timeframe}")
            logger.info(f"Goal: {goal_metric} >= {goal_value}")
            logger.info("=" * 80)

            # Update orchestrator config
            self.orchestrator.config.symbol = symbol
            self.orchestrator.config.timeframe = timeframe

            # Define optimization goal
            optimization_goal = OptimizationGoal(
                target_metric=goal_metric,
                target_value=goal_value,
                description=f"Optimize {goal_metric} to reach {goal_value}",
            )

            # Define initial strategy
            initial_strategy = {
                "name": self.config.strategy.default_strategy,
                "version": "v1.0.0",
                "parameters": {
                    "risk_per_trade": 0.02,
                    "max_positions": 3,
                    "timeframes": [timeframe],
                },
            }

            # Start evolution
            result = self.orchestrator.start_evolution(
                initial_strategy=initial_strategy, optimization_goal=optimization_goal
            )

            # Log results
            logger.info("=" * 80)
            logger.info("Evolution Process Complete")
            logger.info("=" * 80)
            logger.info(f"Success: {result.success}")
            logger.info(f"Total Iterations: {result.iterations}")
            logger.info(f"Goal Achieved: {result.report.goal_achieved}")

            if result.final_metrics:
                logger.info(f"Final Sharpe Ratio: {result.final_metrics.sharpe_ratio:.2f}")
                logger.info(f"Final Win Rate: {result.final_metrics.win_rate:.2%}")
                logger.info(f"Final Profit Factor: {result.final_metrics.profit_factor:.2f}")
                logger.info(f"Final Max Drawdown: {result.final_metrics.max_drawdown:.2%}")

            logger.info("=" * 80)

            return result.success

        except KeyboardInterrupt:
            logger.info("Evolution interrupted by user")
            return False
        except Exception as e:
            logger.error(f"Evolution failed: {e}", exc_info=True)
            return False

    def get_status(self) -> dict:
        """
        Get current evolution status

        Returns:
            Dictionary with current status information
        """
        if not self.monitor:
            return {"error": "Monitor not initialized"}

        return self.monitor.get_current_status()

    def shutdown(self):
        """
        Perform graceful shutdown

        Stops any running evolution process and cleans up resources.
        """
        logger.info("Shutting down Openfund Agent...")

        if self.monitor:
            self.monitor.stop_evolution()
            logger.info("Evolution process stopped")

        logger.info("Shutdown complete")


def create_parser() -> argparse.ArgumentParser:
    """
    Create command-line argument parser

    Returns:
        Configured ArgumentParser instance
    """
    parser = argparse.ArgumentParser(
        description="Openfund Agent - Self-Evolving Trading Agent System",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  # Start evolution with default settings
  python -m openfund_agent.main start

  # Start evolution with custom parameters
  python -m openfund_agent.main start --symbol BTC/USDT:USDT --timeframe 4h --goal-metric sharpe_ratio --goal-value 2.5

  # Use production configuration
  python -m openfund_agent.main start --env production

  # Use custom configuration file
  python -m openfund_agent.main start --config /path/to/config.yaml

  # Check current status
  python -m openfund_agent.main status
        """,
    )

    parser.add_argument(
        "--config", type=str, help="Path to configuration file (optional)", default=None
    )

    parser.add_argument(
        "--env",
        type=str,
        choices=["development", "production"],
        default="development",
        help="Environment name (default: development)",
    )

    parser.add_argument(
        "--log-level",
        type=str,
        choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
        help="Override logging level from config",
    )

    subparsers = parser.add_subparsers(dest="command", help="Available commands")

    # Start command
    start_parser = subparsers.add_parser("start", help="Start the evolution process")
    start_parser.add_argument(
        "--symbol",
        type=str,
        default="BTC/USDT:USDT",
        help="Trading symbol (default: BTC/USDT:USDT)",
    )
    start_parser.add_argument("--timeframe", type=str, default="1h", help="Timeframe (default: 1h)")
    start_parser.add_argument(
        "--goal-metric",
        type=str,
        choices=["sharpe_ratio", "win_rate", "profit_factor", "max_drawdown"],
        default="sharpe_ratio",
        help="Optimization goal metric (default: sharpe_ratio)",
    )
    start_parser.add_argument(
        "--goal-value", type=float, default=2.0, help="Target value for goal metric (default: 2.0)"
    )

    # Status command
    subparsers.add_parser("status", help="Get current evolution status")

    # Version command
    subparsers.add_parser("version", help="Show version information")

    return parser


def main():
    """
    Main entry point for the application

    Parses command-line arguments and executes the requested command.
    """
    parser = create_parser()
    args = parser.parse_args()

    # Show help if no command specified
    if not args.command:
        parser.print_help()
        sys.exit(0)

    # Handle version command
    if args.command == "version":
        print("Openfund Agent v1.0.0")
        print("Self-Evolving Trading Agent System")
        sys.exit(0)

    # Create application instance
    app = AgentApplication(config_path=args.config, env=args.env)

    # Initialize application
    if not app.initialize():
        logger.error("Failed to initialize application")
        sys.exit(1)

    # Override log level if specified
    if args.log_level:
        logging.getLogger().setLevel(args.log_level)
        logger.info(f"Log level overridden to {args.log_level}")

    # Execute command
    try:
        if args.command == "start":
            success = app.start_evolution(
                symbol=args.symbol,
                timeframe=args.timeframe,
                goal_metric=args.goal_metric,
                goal_value=args.goal_value,
            )
            sys.exit(0 if success else 1)

        elif args.command == "status":
            status = app.get_status()
            print("\nCurrent Evolution Status:")
            print("=" * 50)
            for key, value in status.items():
                print(f"{key}: {value}")
            print("=" * 50)
            sys.exit(0)

    except KeyboardInterrupt:
        logger.info("\nInterrupted by user")
        app.shutdown()
        sys.exit(130)  # Standard exit code for SIGINT

    except Exception as e:
        logger.error(f"Unexpected error: {e}", exc_info=True)
        app.shutdown()
        sys.exit(1)

    finally:
        app.shutdown()


if __name__ == "__main__":
    main()
