"""
NC1709 CLI - Main Command Line Interface
Provides both direct command mode and interactive shell mode with optional agentic execution
"""
import sys
import os
import argparse
from pathlib import Path
from typing import Optional

from .config import get_config
from .llm_adapter import LLMAdapter, TaskType, TaskClassifier
from .file_controller import FileController
from .executor import CommandExecutor
from .reasoning_engine import ReasoningEngine
from .remote_client import RemoteClient, is_remote_mode
from .cli_ui import (
    ActionSpinner, Color, Icons,
    status, thinking, success, error, warning, info,
    action_spinner
)

# Import agent module
try:
    from .agent import Agent, AgentConfig, PermissionManager, PermissionPolicy, integrate_mcp_with_agent
    HAS_AGENT = True
except ImportError:
    HAS_AGENT = False


class NC1709CLI:
    """Main CLI application"""

    def __init__(self, remote_url: Optional[str] = None, api_key: Optional[str] = None):
        """Initialize the CLI

        Args:
            remote_url: URL of remote NC1709 server (uses local if None)
            api_key: API key for remote server authentication
        """
        self.config = get_config()
        self.running = True

        # Check for remote mode
        self.remote_url = remote_url or os.environ.get("NC1709_API_URL")
        self.api_key = api_key or os.environ.get("NC1709_API_KEY")
        self.remote_client: Optional[RemoteClient] = None

        if self.remote_url:
            # Remote mode - connect to remote server
            self._init_remote_mode()
        else:
            # Local mode - use local LLMs
            self._init_local_mode()

        # Memory module (lazy loaded)
        self._session_manager = None
        self._project_indexer = None
        self._memory_enabled = self.config.get("memory.enabled", False)

        # Plugin system (lazy loaded)
        self._plugin_manager = None

        # MCP support (lazy loaded)
        self._mcp_manager = None

        # Agent mode (lazy loaded)
        self._agent = None
        self._agent_mode = False

    def _init_remote_mode(self):
        """Initialize remote mode"""
        try:
            self.remote_client = RemoteClient(
                server_url=self.remote_url,
                api_key=self.api_key
            )
            # Verify connection
            status = self.remote_client.check_status()
            print(f"🌐 Connected to remote NC1709 server: {self.remote_url}")
            print(f"   Server version: {status.get('version', 'unknown')}")

            # Set up minimal local components (no LLM needed)
            self.file_controller = FileController()
            self.executor = CommandExecutor()
            self.llm = None
            self.reasoning_engine = None

        except Exception as e:
            print(f"❌ Failed to connect to remote server: {e}")
            print("   Falling back to local mode...")
            self.remote_client = None
            self._init_local_mode()

    def _init_local_mode(self):
        """Initialize local mode"""
        self.llm = LLMAdapter()
        self.file_controller = FileController()
        self.executor = CommandExecutor()
        self.reasoning_engine = ReasoningEngine()

    @property
    def session_manager(self):
        """Lazy load session manager"""
        if self._session_manager is None:
            try:
                from .memory.sessions import SessionManager
                self._session_manager = SessionManager()
            except ImportError:
                pass
        return self._session_manager

    @property
    def project_indexer(self):
        """Lazy load project indexer"""
        if self._project_indexer is None and self._memory_enabled:
            try:
                from .memory.indexer import ProjectIndexer
                self._project_indexer = ProjectIndexer(str(Path.cwd()))
            except ImportError:
                pass
        return self._project_indexer

    @property
    def plugin_manager(self):
        """Lazy load plugin manager"""
        if self._plugin_manager is None:
            try:
                from .plugins import PluginManager
                self._plugin_manager = PluginManager()
                # Discover and load built-in plugins
                self._plugin_manager.discover_plugins()
                self._plugin_manager.load_all()
            except ImportError:
                pass
        return self._plugin_manager

    @property
    def mcp_manager(self):
        """Lazy load MCP manager"""
        if self._mcp_manager is None:
            try:
                from .mcp import MCPManager
                self._mcp_manager = MCPManager(name="nc1709", version="1.0.0")
                self._mcp_manager.setup_default_tools()
            except ImportError:
                pass
        return self._mcp_manager

    @property
    def agent(self):
        """Lazy load agent"""
        if self._agent is None and HAS_AGENT and self.llm:
            try:
                # Create agent configuration
                config = AgentConfig(
                    max_iterations=self.config.get("agent.max_iterations", 50),
                    verbose=self.config.get("ui.verbose", False),
                )

                # Create agent
                self._agent = Agent(llm=self.llm, config=config)

                # Integrate MCP tools if available
                if self.mcp_manager:
                    try:
                        integrate_mcp_with_agent(self._agent, self.mcp_manager)
                    except Exception as e:
                        warning(f"Failed to integrate MCP with agent: {e}")

            except Exception as e:
                error(f"Failed to create agent: {e}")
                self._agent = None
        return self._agent

    def run(self, args: Optional[list] = None) -> int:
        """Run the CLI

        Args:
            args: Command line arguments (default: sys.argv)

        Returns:
            Exit code
        """
        parser = self._create_parser()
        parsed_args = parser.parse_args(args)

        # Handle different modes
        if parsed_args.version:
            self._print_version()
            return 0

        if parsed_args.config:
            self._show_config()
            return 0

        # Session management
        if parsed_args.sessions:
            return self._list_sessions()

        if parsed_args.resume:
            return self._run_shell(resume_session=parsed_args.resume)

        # Project indexing
        if parsed_args.index:
            return self._index_project()

        if parsed_args.search:
            return self._search_code(parsed_args.search)

        # Plugin commands
        if parsed_args.plugins:
            return self._list_plugins()

        if parsed_args.plugin:
            return self._run_plugin_action(parsed_args.plugin)

        # MCP commands
        if parsed_args.mcp_status:
            return self._mcp_show_status()

        if parsed_args.mcp_serve:
            return self._mcp_run_server()

        if parsed_args.mcp_connect:
            return self._mcp_connect_servers(parsed_args.mcp_connect)

        if parsed_args.mcp_tool:
            args_json = parsed_args.args if parsed_args.args else "{}"
            return self._mcp_call_tool(parsed_args.mcp_tool, args_json)

        # Web dashboard
        if parsed_args.web:
            serve_remote = getattr(parsed_args, 'serve', False)
            return self._run_web_dashboard(parsed_args.port, serve_remote=serve_remote)

        # Shell completions
        if parsed_args.completion:
            return self._generate_completion(parsed_args.completion)

        # AI Agents
        if parsed_args.fix:
            auto_apply = getattr(parsed_args, 'apply', False)
            return self._run_auto_fix(parsed_args.fix, auto_apply=auto_apply)

        if parsed_args.generate_tests:
            output_file = getattr(parsed_args, 'output', None)
            return self._run_test_generator(parsed_args.generate_tests, output_file=output_file)

        # Agent mode
        if parsed_args.agent:
            self._agent_mode = True

        if parsed_args.shell or not parsed_args.prompt:
            # Interactive shell mode
            return self._run_shell()
        else:
            # Direct command mode
            return self._run_command(parsed_args.prompt)
    
    def _create_parser(self) -> argparse.ArgumentParser:
        """Create argument parser
        
        Returns:
            Argument parser
        """
        parser = argparse.ArgumentParser(
            prog="nc1709",
            description="NC1709 - A Local-First AI Developer Assistant",
            epilog="Examples:\n"
                   "  nc1709 'create a Python script to parse JSON'\n"
                   "  nc1709 --shell\n"
                   "  nc1709 --config",
            formatter_class=argparse.RawDescriptionHelpFormatter
        )
        
        parser.add_argument(
            "prompt",
            nargs="?",
            help="Your request or question"
        )
        
        parser.add_argument(
            "-s", "--shell",
            action="store_true",
            help="Start interactive shell mode"
        )
        
        parser.add_argument(
            "-v", "--version",
            action="store_true",
            help="Show version information"
        )
        
        parser.add_argument(
            "-c", "--config",
            action="store_true",
            help="Show current configuration"
        )
        
        parser.add_argument(
            "--verbose",
            action="store_true",
            help="Enable verbose output"
        )

        # Session management arguments
        parser.add_argument(
            "--sessions",
            action="store_true",
            help="List saved sessions"
        )

        parser.add_argument(
            "--resume",
            metavar="SESSION_ID",
            help="Resume a previous session"
        )

        # Memory/indexing arguments
        parser.add_argument(
            "--index",
            action="store_true",
            help="Index the current project for semantic search"
        )

        parser.add_argument(
            "--search",
            metavar="QUERY",
            help="Search indexed code semantically"
        )

        # Plugin arguments
        parser.add_argument(
            "--plugins",
            action="store_true",
            help="List available plugins"
        )

        parser.add_argument(
            "--plugin",
            metavar="NAME",
            help="Execute a plugin action (e.g., --plugin git:status)"
        )

        # MCP arguments
        parser.add_argument(
            "--mcp-status",
            action="store_true",
            help="Show MCP server status and available tools"
        )

        parser.add_argument(
            "--mcp-serve",
            action="store_true",
            help="Run NC1709 as an MCP server (stdio transport)"
        )

        parser.add_argument(
            "--mcp-connect",
            metavar="CONFIG",
            help="Connect to MCP servers from config file"
        )

        parser.add_argument(
            "--mcp-tool",
            metavar="TOOL",
            help="Call an MCP tool (e.g., --mcp-tool read_file --args '{\"path\": \"file.txt\"}')"
        )

        parser.add_argument(
            "--args",
            metavar="JSON",
            help="JSON arguments for --mcp-tool"
        )

        # Web Dashboard arguments
        parser.add_argument(
            "--web",
            action="store_true",
            help="Start the web dashboard (default: http://localhost:8709)"
        )

        parser.add_argument(
            "--port",
            type=int,
            default=8709,
            help="Port for web dashboard (default: 8709)"
        )

        # Remote mode arguments
        parser.add_argument(
            "--remote",
            metavar="URL",
            help="Connect to remote NC1709 server (e.g., --remote https://your-server.ngrok.io)"
        )

        parser.add_argument(
            "--api-key",
            metavar="KEY",
            help="API key for remote server authentication"
        )

        parser.add_argument(
            "--serve",
            action="store_true",
            help="Run as a server for remote clients (use with --web)"
        )

        # AI Agents arguments
        parser.add_argument(
            "--fix",
            metavar="FILE",
            help="Auto-fix code errors in a file"
        )

        parser.add_argument(
            "--apply",
            action="store_true",
            help="Auto-apply fixes (use with --fix)"
        )

        parser.add_argument(
            "--generate-tests",
            metavar="FILE",
            help="Generate unit tests for a file"
        )

        parser.add_argument(
            "--output",
            metavar="FILE",
            help="Output file for generated tests (use with --generate-tests)"
        )

        # Shell completions
        parser.add_argument(
            "--completion",
            choices=["bash", "zsh", "fish"],
            help="Generate shell completion script"
        )

        # Agentic mode
        parser.add_argument(
            "--agent", "-a",
            action="store_true",
            help="Enable agentic mode with tool execution (Claude Code-style)"
        )

        parser.add_argument(
            "--permission",
            choices=["strict", "normal", "permissive", "trust"],
            default="normal",
            help="Permission policy for agent tools (default: normal)"
        )

        return parser
    
    def _print_version(self) -> None:
        """Print version information"""
        from . import __version__
        print(f"NC1709 CLI v{__version__}")
        print("A Local-First AI Developer Assistant")

        if self.remote_client:
            print(f"\n🌐 Remote Mode: {self.remote_url}")
            try:
                status = self.remote_client.check_status()
                print(f"   Server: {status.get('server', 'nc1709')}")
                print(f"   Version: {status.get('version', 'unknown')}")
                models = status.get('models', {})
                if models:
                    print("\n   Available Models:")
                    for task, model in models.items():
                        print(f"     {task:12} → {model}")
            except Exception as e:
                print(f"   (Unable to fetch server info: {e})")
        elif self.llm:
            print("\nConfigured Models:")
            for task_type in TaskType:
                model_info = self.llm.get_model_info(task_type)
                print(f"  {task_type.value:12} → {model_info['model']}")
    
    def _show_config(self) -> None:
        """Show current configuration"""
        import json
        print("Current Configuration:")
        print(json.dumps(self.config.config, indent=2))
        print(f"\nConfig file: {self.config.config_path}")
    
    def _run_shell(self, resume_session: Optional[str] = None) -> int:
        """Run interactive shell mode

        Args:
            resume_session: Session ID to resume

        Returns:
            Exit code
        """
        self._print_banner()

        # Initialize or resume session
        if self.session_manager:
            if resume_session:
                session = self.session_manager.load_session(resume_session)
                if session:
                    self.session_manager.current_session = session
                    print(f"📂 Resumed session: {session.name} ({len(session.messages)} messages)")
                else:
                    print(f"⚠️  Session '{resume_session}' not found, starting new session")
                    self.session_manager.start_session(project_path=str(Path.cwd()))
            else:
                self.session_manager.start_session(project_path=str(Path.cwd()))
                print(f"📝 Started new session: {self.session_manager.current_session.id}")

        print("\nType 'help' for available commands, 'exit' to quit.\n")

        while self.running:
            try:
                # Get user input with styled prompt
                prompt_str = f"\n{Color.CYAN}{Icons.THINKING}{Color.RESET} {Color.BOLD}nc1709>{Color.RESET} "
                prompt = input(prompt_str).strip()

                if not prompt:
                    continue

                # Handle special commands
                if prompt.lower() in ["exit", "quit", "q"]:
                    if self.session_manager and self.session_manager.current_session:
                        self.session_manager.save_session(self.session_manager.current_session)
                        info(f"Session saved: {self.session_manager.current_session.id}")
                    success("Goodbye!")
                    break

                if prompt.lower() == "help":
                    self._print_help()
                    continue

                if prompt.lower() == "clear":
                    self.llm.clear_history()
                    success("Conversation history cleared")
                    continue

                if prompt.lower() == "history":
                    self._show_history()
                    continue

                if prompt.lower().startswith("config "):
                    self._handle_config_command(prompt)
                    continue

                # New memory commands
                if prompt.lower() == "sessions":
                    self._list_sessions()
                    continue

                if prompt.lower().startswith("search "):
                    query = prompt[7:].strip()
                    self._search_code(query)
                    continue

                if prompt.lower() == "index":
                    self._index_project()
                    continue

                # Plugin commands
                if prompt.lower() == "plugins":
                    self._list_plugins()
                    continue

                if prompt.lower().startswith("git "):
                    self._run_plugin_action(f"git:{prompt[4:].strip()}")
                    continue

                if prompt.lower().startswith("docker "):
                    self._run_plugin_action(f"docker:{prompt[7:].strip()}")
                    continue

                # MCP commands
                if prompt.lower() == "mcp":
                    self._mcp_show_status()
                    continue

                if prompt.lower() == "mcp tools":
                    self._mcp_list_tools()
                    continue

                if prompt.lower().startswith("mcp call "):
                    tool_spec = prompt[9:].strip()
                    self._mcp_call_tool_interactive(tool_spec)
                    continue

                # Agent mode commands
                if prompt.lower() == "agent on":
                    if HAS_AGENT:
                        self._agent_mode = True
                        success("Agent mode enabled. Autonomous tool execution active.")
                    else:
                        error("Agent module not available")
                    continue

                if prompt.lower() == "agent off":
                    self._agent_mode = False
                    info("Agent mode disabled. Using standard reasoning engine.")
                    continue

                if prompt.lower() == "agent tools":
                    self._show_agent_tools()
                    continue

                if prompt.lower() == "agent status":
                    self._show_agent_status()
                    continue

                # Process the request
                self._process_request(prompt)

            except KeyboardInterrupt:
                warning("\nUse 'exit' to quit.")
                continue

            except Exception as e:
                error(f"Error: {e}")
                if self.config.get("ui.verbose"):
                    import traceback
                    traceback.print_exc()

        return 0
    
    def _run_command(self, prompt: str) -> int:
        """Run a single command

        Args:
            prompt: User's prompt

        Returns:
            Exit code
        """
        try:
            self._process_request(prompt)
            return 0
        except Exception as e:
            error(f"Error: {e}")
            return 1
    
    def _process_request(self, prompt: str) -> None:
        """Process a user request

        Args:
            prompt: User's prompt
        """
        if self.remote_client:
            # Remote mode - send to remote server
            try:
                response = self.remote_client.chat(prompt)
                print(f"\n{response}")
            except Exception as e:
                print(f"\n❌ Remote request failed: {e}")
        elif self._agent_mode and HAS_AGENT:
            # Agent mode - use the agent for tool execution
            self._process_request_agent(prompt)
        else:
            # Local mode - standard reasoning engine
            # Classify the task
            task_type = TaskClassifier.classify(prompt)

            # Get context
            context = {
                "cwd": str(Path.cwd()),
                "task_type": task_type.value
            }

            # Use reasoning engine for complex requests
            response = self.reasoning_engine.process_request(prompt, context)

            # Print response
            print(f"\n{response}")

    def _process_request_agent(self, prompt: str) -> None:
        """Process a user request using the agent

        Args:
            prompt: User's prompt
        """
        if not self.agent:
            error("Agent not available. Check LLM configuration.")
            return

        print()  # Add spacing
        thinking("Processing with agent...")

        try:
            response = self.agent.run(prompt)

            # Show tool execution history
            tool_history = self.agent.get_tool_history()
            if tool_history:
                print(f"\n{Color.DIM}Tools executed: {len(tool_history)}{Color.RESET}")
                for entry in tool_history[-5:]:  # Show last 5
                    icon = Icons.SUCCESS if entry['success'] else Icons.FAILURE
                    duration = f"{entry['duration_ms']:.0f}ms" if entry['duration_ms'] else ""
                    print(f"  {icon} {entry['tool']}({entry['target']}) {Color.DIM}{duration}{Color.RESET}")

            # Print final response
            print(f"\n{response}")

        except Exception as e:
            error(f"Agent error: {e}")
            if self.config.get("ui.verbose"):
                import traceback
                traceback.print_exc()

    def _show_agent_tools(self) -> None:
        """Show available agent tools"""
        if not HAS_AGENT:
            error("Agent module not available")
            return

        if not self.agent:
            warning("Agent not initialized. Enable agent mode first with 'agent on'")
            return

        print(f"\n{Color.BOLD}🔧 Agent Tools{Color.RESET}")
        print("=" * 60)

        # Group tools by category
        tools_by_category = {}
        for tool_name in self.agent.registry.list_names():
            tool = self.agent.registry.get(tool_name)
            if tool:
                category = getattr(tool, 'category', 'other')
                if category not in tools_by_category:
                    tools_by_category[category] = []
                tools_by_category[category].append(tool)

        for category, tools in sorted(tools_by_category.items()):
            print(f"\n{Color.CYAN}{category.title()}{Color.RESET}")
            for tool in tools:
                perm = self.agent.registry.get_permission(tool.name)
                perm_icon = {
                    "auto": f"{Color.GREEN}✓{Color.RESET}",
                    "ask": f"{Color.YELLOW}?{Color.RESET}",
                    "deny": f"{Color.RED}✗{Color.RESET}",
                }.get(perm.value, "?")
                print(f"  {perm_icon} {Color.BOLD}{tool.name}{Color.RESET}")
                print(f"      {Color.DIM}{tool.description[:60]}...{Color.RESET}" if len(tool.description) > 60 else f"      {Color.DIM}{tool.description}{Color.RESET}")

        print(f"\n{Color.DIM}Permission key: ✓=auto, ?=ask, ✗=deny{Color.RESET}")
        print(f"Total tools: {len(self.agent.registry.list_names())}")

    def _show_agent_status(self) -> None:
        """Show agent status and tool history"""
        if not HAS_AGENT:
            error("Agent module not available")
            return

        print(f"\n{Color.BOLD}🤖 Agent Status{Color.RESET}")
        print("=" * 60)

        # Mode status
        mode_status = f"{Color.GREEN}Enabled{Color.RESET}" if self._agent_mode else f"{Color.DIM}Disabled{Color.RESET}"
        print(f"\nAgent Mode: {mode_status}")

        if self._agent:
            # Agent state
            print(f"State: {self._agent.state.value}")
            print(f"Iteration: {self._agent.iteration_count}")
            print(f"Registered Tools: {len(self._agent.registry.list_names())}")

            # Tool history
            history = self._agent.get_tool_history()
            if history:
                print(f"\n{Color.BOLD}Recent Tool Executions:{Color.RESET}")
                for entry in history[-10:]:
                    icon = Icons.SUCCESS if entry['success'] else Icons.FAILURE
                    duration = f"{entry['duration_ms']:.0f}ms" if entry['duration_ms'] else ""
                    print(f"  {icon} {entry['tool']}({entry['target']}) {Color.DIM}{duration}{Color.RESET}")
            else:
                print(f"\n{Color.DIM}No tool executions yet{Color.RESET}")
        else:
            print(f"\n{Color.DIM}Agent not initialized. Use 'agent on' to enable.{Color.RESET}")

    def _print_banner(self) -> None:
        """Print welcome banner"""
        from . import __version__

        # Use color-coded banner
        title = f"{Color.BOLD}{Color.CYAN}NC1709 CLI{Color.RESET} v{__version__}"

        if self.remote_client:
            mode_info = f"{Color.YELLOW}Remote Mode{Color.RESET}: {self.remote_url[:40]}"
            banner = f"""
{Color.DIM}╔═══════════════════════════════════════════════════════════╗{Color.RESET}
{Color.DIM}║{Color.RESET}                                                           {Color.DIM}║{Color.RESET}
{Color.DIM}║{Color.RESET}              {title}                        {Color.DIM}║{Color.RESET}
{Color.DIM}║{Color.RESET}       {Color.BRIGHT_WHITE}A Local-First AI Developer Assistant{Color.RESET}                {Color.DIM}║{Color.RESET}
{Color.DIM}║{Color.RESET}                                                           {Color.DIM}║{Color.RESET}
{Color.DIM}║{Color.RESET}  {mode_info:<55} {Color.DIM}║{Color.RESET}
{Color.DIM}║{Color.RESET}                                                           {Color.DIM}║{Color.RESET}
{Color.DIM}╚═══════════════════════════════════════════════════════════╝{Color.RESET}
"""
        elif self._agent_mode and HAS_AGENT:
            features = f"{Color.GREEN}Agent Mode{Color.RESET}  •  {Color.BLUE}Tool Execution{Color.RESET}  •  {Color.MAGENTA}Autonomous{Color.RESET}"
            banner = f"""
{Color.DIM}╔═══════════════════════════════════════════════════════════╗{Color.RESET}
{Color.DIM}║{Color.RESET}                                                           {Color.DIM}║{Color.RESET}
{Color.DIM}║{Color.RESET}              {title}                        {Color.DIM}║{Color.RESET}
{Color.DIM}║{Color.RESET}       {Color.BRIGHT_WHITE}A Local-First AI Developer Assistant{Color.RESET}                {Color.DIM}║{Color.RESET}
{Color.DIM}║{Color.RESET}                                                           {Color.DIM}║{Color.RESET}
{Color.DIM}║{Color.RESET}  {features}       {Color.DIM}║{Color.RESET}
{Color.DIM}║{Color.RESET}                                                           {Color.DIM}║{Color.RESET}
{Color.DIM}╚═══════════════════════════════════════════════════════════╝{Color.RESET}
"""
        else:
            features = f"{Color.GREEN}100% Local{Color.RESET}  •  {Color.BLUE}Multi-Model{Color.RESET}  •  {Color.MAGENTA}Powerful{Color.RESET}"
            banner = f"""
{Color.DIM}╔═══════════════════════════════════════════════════════════╗{Color.RESET}
{Color.DIM}║{Color.RESET}                                                           {Color.DIM}║{Color.RESET}
{Color.DIM}║{Color.RESET}              {title}                        {Color.DIM}║{Color.RESET}
{Color.DIM}║{Color.RESET}       {Color.BRIGHT_WHITE}A Local-First AI Developer Assistant{Color.RESET}                {Color.DIM}║{Color.RESET}
{Color.DIM}║{Color.RESET}                                                           {Color.DIM}║{Color.RESET}
{Color.DIM}║{Color.RESET}  {features}         {Color.DIM}║{Color.RESET}
{Color.DIM}║{Color.RESET}                                                           {Color.DIM}║{Color.RESET}
{Color.DIM}╚═══════════════════════════════════════════════════════════╝{Color.RESET}
"""
        print(banner)
    
    def _print_help(self) -> None:
        """Print help message"""
        base_help = """
Available Commands:

  help          Show this help message
  exit/quit     Exit the shell
  clear         Clear conversation history
  history       Show command execution history
  config        Show current configuration
"""

        agent_help = """
Agent Mode Commands:

  agent on      Enable agent mode (autonomous tool execution)
  agent off     Disable agent mode
  agent tools   List available agent tools
  agent status  Show agent status and tool history
"""

        memory_help = """
Memory & Search:

  sessions      List saved conversation sessions
  index         Index current project for semantic search
  search <q>    Search indexed code (e.g., "search authentication logic")
"""

        plugin_help = """
Plugins:

  plugins       List available plugins
  git <cmd>     Git operations (status, diff, log, branch, commit, etc.)
  docker <cmd>  Docker operations (ps, images, logs, compose up/down, etc.)
"""

        mcp_help = """
MCP (Model Context Protocol):

  mcp           Show MCP status and info
  mcp tools     List all available MCP tools
  mcp call <t>  Call an MCP tool (e.g., "mcp call read_file path=main.py")
"""

        examples = """
Examples:

  • "Read the file main.py and explain what it does"
  • "Create a Python script to fetch data from an API"
  • "git status" or "git diff"
  • "docker ps" or "docker compose up"
  • "search where is the database connection"

Tips:

  • Be specific about what you want
  • Mention file names when working with code
  • The assistant will ask for confirmation before making changes
  • Use Ctrl+C to cancel the current operation
  • Run 'nc1709 --agent' to start in agent mode with tool execution
"""

        # Build help text based on available features
        help_text = base_help
        if HAS_AGENT:
            help_text += agent_help
        help_text += memory_help + plugin_help + mcp_help + examples

        print(help_text)
    
    def _show_history(self) -> None:
        """Show command execution history"""
        history = self.executor.get_execution_history(limit=20)
        
        if not history:
            print("No command history yet.")
            return
        
        print("\nRecent Command History:")
        print("="*60)
        
        for i, entry in enumerate(history, 1):
            status = "✅" if entry["success"] else "❌"
            print(f"{i}. {status} {entry['command']}")
            print(f"   Time: {entry['timestamp']}")
            print(f"   Exit code: {entry['return_code']}")
            print()
    
    def _handle_config_command(self, prompt: str) -> None:
        """Handle config commands
        
        Args:
            prompt: Config command
        """
        parts = prompt.split(maxsplit=2)
        
        if len(parts) < 2:
            self._show_config()
            return
        
        if parts[1] == "get":
            if len(parts) < 3:
                print("Usage: config get <key>")
                return
            key = parts[2]
            value = self.config.get(key)
            print(f"{key} = {value}")
        
        elif parts[1] == "set":
            if len(parts) < 3:
                print("Usage: config set <key> <value>")
                return
            # Parse key=value
            if "=" not in parts[2]:
                print("Usage: config set <key>=<value>")
                return
            key, value = parts[2].split("=", 1)
            self.config.set(key.strip(), value.strip())
            print(f"✅ Set {key} = {value}")
        
        else:
            print("Unknown config command. Use 'config get <key>' or 'config set <key>=<value>'")

    def _list_sessions(self) -> int:
        """List saved sessions

        Returns:
            Exit code
        """
        if not self.session_manager:
            print("⚠️  Session management not available")
            return 1

        sessions = self.session_manager.list_sessions(limit=20)

        if not sessions:
            print("No saved sessions found.")
            return 0

        print("\n📚 Saved Sessions:")
        print("=" * 70)

        for session in sessions:
            msg_count = session.get("message_count", 0)
            project = session.get("project_path", "N/A")
            if project and len(project) > 30:
                project = "..." + project[-27:]

            print(f"  ID: {session['id']}")
            print(f"  Name: {session['name']}")
            print(f"  Messages: {msg_count}")
            print(f"  Updated: {session.get('updated_at', 'N/A')[:19]}")
            print(f"  Project: {project}")
            print("-" * 70)

        print(f"\nTo resume a session: nc1709 --resume <session_id>")
        return 0

    def _index_project(self) -> int:
        """Index the current project for semantic search

        Returns:
            Exit code
        """
        try:
            from .memory.indexer import ProjectIndexer

            print(f"🔍 Indexing project: {Path.cwd()}")
            print("This may take a few minutes for large projects...\n")

            indexer = ProjectIndexer(str(Path.cwd()))
            stats = indexer.index_project(show_progress=True)

            print(f"\n✅ Indexing complete!")
            print(f"   Files indexed: {stats['files_indexed']}")
            print(f"   Total chunks: {stats['chunks_created']}")

            if stats['errors']:
                print(f"   Errors: {len(stats['errors'])}")

            return 0

        except ImportError:
            print("⚠️  Memory module dependencies not installed.")
            print("   Install with: pip install chromadb sentence-transformers")
            return 1
        except Exception as e:
            print(f"❌ Error indexing project: {e}")
            return 1

    def _search_code(self, query: str) -> int:
        """Search indexed code

        Args:
            query: Search query

        Returns:
            Exit code
        """
        if not query:
            print("Usage: search <query>")
            return 1

        try:
            from .memory.indexer import ProjectIndexer

            indexer = ProjectIndexer(str(Path.cwd()))

            # Check if project is indexed
            summary = indexer.get_project_summary()
            if summary['total_files'] == 0:
                print("⚠️  Project not indexed yet. Run 'index' first.")
                return 1

            print(f"\n🔍 Searching for: {query}\n")

            results = indexer.search(query, n_results=5)

            if not results:
                print("No results found.")
                return 0

            print(f"Found {len(results)} results:\n")
            print("=" * 70)

            for i, result in enumerate(results, 1):
                similarity = result.get('similarity', 0) * 100
                location = result.get('location', 'Unknown')
                language = result.get('language', 'unknown')

                print(f"\n📄 Result {i} ({similarity:.1f}% match)")
                print(f"   Location: {location}")
                print(f"   Language: {language}")
                print("-" * 70)

                # Show code preview (first 10 lines)
                code = result.get('content', '')
                lines = code.split('\n')[:10]
                for line in lines:
                    print(f"   {line[:80]}")
                if len(code.split('\n')) > 10:
                    print("   ...")

            print("\n" + "=" * 70)
            return 0

        except ImportError:
            print("⚠️  Memory module dependencies not installed.")
            print("   Install with: pip install chromadb sentence-transformers")
            return 1
        except Exception as e:
            print(f"❌ Error searching: {e}")
            return 1

    def _list_plugins(self) -> int:
        """List available plugins

        Returns:
            Exit code
        """
        if not self.plugin_manager:
            print("⚠️  Plugin system not available")
            return 1

        status = self.plugin_manager.get_status()

        if not status:
            print("No plugins registered.")
            return 0

        print("\n🔌 Available Plugins:")
        print("=" * 70)

        for name, info in status.items():
            status_icon = "✅" if info["status"] == "active" else "❌"
            builtin_tag = " [built-in]" if info.get("builtin") else ""

            print(f"\n  {status_icon} {name} v{info['version']}{builtin_tag}")
            print(f"      Status: {info['status']}")

            if info.get("error"):
                print(f"      Error: {info['error']}")

            # Show actions for loaded plugins
            plugin = self.plugin_manager.get_plugin(name)
            if plugin and plugin.actions:
                actions = ", ".join(plugin.actions.keys())
                print(f"      Actions: {actions}")

        print("\n" + "=" * 70)
        print("\nUsage: nc1709 --plugin <name>:<action>")
        print("Example: nc1709 --plugin git:status")
        return 0

    def _run_plugin_action(self, action_spec: str) -> int:
        """Run a plugin action

        Args:
            action_spec: Plugin:action specification (e.g., "git:status")

        Returns:
            Exit code
        """
        if not self.plugin_manager:
            print("⚠️  Plugin system not available")
            return 1

        # Parse action spec
        if ":" not in action_spec:
            # Try to find a plugin that can handle this as a request
            handlers = self.plugin_manager.find_handler(action_spec)
            if handlers:
                plugin_name = handlers[0][0]
                plugin = self.plugin_manager.get_plugin(plugin_name)
                if plugin and hasattr(plugin, 'handle_request'):
                    result = plugin.handle_request(action_spec)
                    if result:
                        self._print_action_result(result)
                        return 0 if result.success else 1

            print(f"❌ Invalid format. Use: <plugin>:<action>")
            print(f"   Example: git:status, docker:ps")
            return 1

        parts = action_spec.split(":", 1)
        plugin_name = parts[0].strip()
        action_name = parts[1].strip() if len(parts) > 1 else ""

        # Get the plugin
        plugin = self.plugin_manager.get_plugin(plugin_name)
        if not plugin:
            # Try to load it
            if not self.plugin_manager.load_plugin(plugin_name):
                print(f"❌ Plugin '{plugin_name}' not found")
                return 1
            plugin = self.plugin_manager.get_plugin(plugin_name)

        # If no action specified, show plugin help
        if not action_name:
            print(plugin.get_help())
            return 0

        # Check if action exists
        if action_name not in plugin.actions:
            # Try to handle as natural language
            if hasattr(plugin, 'handle_request'):
                result = plugin.handle_request(action_name)
                if result:
                    self._print_action_result(result)
                    return 0 if result.success else 1

            print(f"❌ Unknown action: {action_name}")
            print(f"   Available actions: {', '.join(plugin.actions.keys())}")
            return 1

        # Execute the action
        result = self.plugin_manager.execute_action(plugin_name, action_name)
        self._print_action_result(result)
        return 0 if result.success else 1

    def _print_action_result(self, result) -> None:
        """Print an action result

        Args:
            result: ActionResult to print
        """
        if result.success:
            print(f"\n✅ {result.message}")
        else:
            print(f"\n❌ {result.message}")
            if result.error:
                print(f"   Error: {result.error}")

        # Print data if present
        if result.data:
            if isinstance(result.data, str):
                print(f"\n{result.data}")
            elif isinstance(result.data, list):
                for item in result.data:
                    if hasattr(item, '__dict__'):
                        # Dataclass or object
                        print(f"  - {item}")
                    else:
                        print(f"  - {item}")
            elif hasattr(result.data, '__dict__'):
                # Single object
                for key, value in vars(result.data).items():
                    if not key.startswith('_'):
                        print(f"  {key}: {value}")

    # =========================================================================
    # MCP Methods
    # =========================================================================

    def _mcp_show_status(self) -> int:
        """Show MCP status

        Returns:
            Exit code
        """
        if not self.mcp_manager:
            print("⚠️  MCP module not available")
            return 1

        status = self.mcp_manager.get_status()

        print("\n🔌 MCP Status:")
        print("=" * 60)

        # Server info
        server = status["server"]
        print(f"\n📡 Local Server: {server['name']} v{server['version']}")
        print(f"   Running: {'Yes' if server['running'] else 'No'}")
        print(f"   Tools: {server['tools']}")
        print(f"   Resources: {server['resources']}")
        print(f"   Prompts: {server['prompts']}")

        # Connected servers
        client = status["client"]
        print(f"\n🌐 Connected Servers: {client['connected_servers']}")

        if client["servers"]:
            for srv in client["servers"]:
                status_icon = "✅" if srv["connected"] else "❌"
                print(f"   {status_icon} {srv['name']}: {srv['tools']} tools, {srv['resources']} resources")

        print("\n" + "=" * 60)
        print("\nCommands:")
        print("  mcp tools     - List available tools")
        print("  mcp call <t>  - Call a tool")
        print("  --mcp-serve   - Run as MCP server")
        return 0

    def _mcp_list_tools(self) -> int:
        """List MCP tools

        Returns:
            Exit code
        """
        if not self.mcp_manager:
            print("⚠️  MCP module not available")
            return 1

        all_tools = self.mcp_manager.get_all_tools()

        print("\n🔧 Available MCP Tools:")
        print("=" * 60)

        # Local tools
        if all_tools["local"]:
            print("\n📍 Local Tools:")
            for tool in all_tools["local"]:
                print(f"\n  {tool.name}")
                print(f"    Description: {tool.description}")
                if tool.parameters:
                    params = ", ".join(p.name for p in tool.parameters)
                    print(f"    Parameters: {params}")

        # Remote tools
        if all_tools["remote"]:
            print("\n🌐 Remote Tools:")
            for tool in all_tools["remote"]:
                print(f"\n  {tool.name}")
                print(f"    Description: {tool.description}")

        if not all_tools["local"] and not all_tools["remote"]:
            print("\nNo tools available.")

        print("\n" + "=" * 60)
        return 0

    def _mcp_call_tool_interactive(self, tool_spec: str) -> int:
        """Call an MCP tool from interactive mode

        Args:
            tool_spec: Tool name and args (e.g., "read_file path=main.py")

        Returns:
            Exit code
        """
        import json

        parts = tool_spec.split(maxsplit=1)
        tool_name = parts[0]
        args_str = parts[1] if len(parts) > 1 else ""

        # Parse key=value args
        args = {}
        if args_str:
            for pair in args_str.split():
                if "=" in pair:
                    key, value = pair.split("=", 1)
                    # Try to parse as JSON for complex values
                    try:
                        args[key] = json.loads(value)
                    except json.JSONDecodeError:
                        args[key] = value

        return self._mcp_call_tool(tool_name, json.dumps(args))

    def _mcp_call_tool(self, tool_name: str, args_json: str) -> int:
        """Call an MCP tool

        Args:
            tool_name: Tool name
            args_json: JSON string of arguments

        Returns:
            Exit code
        """
        import json
        import asyncio

        if not self.mcp_manager:
            print("⚠️  MCP module not available")
            return 1

        try:
            args = json.loads(args_json)
        except json.JSONDecodeError as e:
            print(f"❌ Invalid JSON arguments: {e}")
            return 1

        print(f"\n🔧 Calling tool: {tool_name}")
        if args:
            print(f"   Arguments: {args}")

        try:
            # Run async call
            result = asyncio.run(self.mcp_manager.call_tool(tool_name, args))

            if "error" in result:
                print(f"\n❌ Error: {result['error']}")
                return 1

            print("\n✅ Result:")
            print("-" * 40)

            if "content" in result:
                for item in result["content"]:
                    if item.get("type") == "text":
                        print(item.get("text", ""))
            else:
                print(json.dumps(result, indent=2))

            return 0

        except Exception as e:
            print(f"❌ Error calling tool: {e}")
            return 1

    def _mcp_run_server(self) -> int:
        """Run NC1709 as an MCP server

        Returns:
            Exit code (never returns normally)
        """
        import asyncio

        if not self.mcp_manager:
            print("⚠️  MCP module not available", file=sys.stderr)
            return 1

        # Don't print to stdout as it's used for MCP communication
        print("Starting NC1709 MCP server (stdio)...", file=sys.stderr)

        try:
            asyncio.run(self.mcp_manager.server.run_stdio())
        except KeyboardInterrupt:
            print("\nServer stopped.", file=sys.stderr)

        return 0

    def _mcp_connect_servers(self, config_path: str) -> int:
        """Connect to MCP servers from config

        Args:
            config_path: Path to MCP config file

        Returns:
            Exit code
        """
        import asyncio

        if not self.mcp_manager:
            print("⚠️  MCP module not available")
            return 1

        print(f"🔌 Connecting to MCP servers from: {config_path}")

        try:
            count = asyncio.run(self.mcp_manager.auto_discover_servers(config_path))

            if count > 0:
                print(f"\n✅ Connected to {count} server(s)")

                # Show connected servers
                servers = self.mcp_manager.list_connected_servers()
                for srv in servers:
                    print(f"   - {srv['name']}: {srv['tools']} tools")

                return 0
            else:
                print("\n⚠️  No servers connected. Check config file.")
                return 1

        except Exception as e:
            print(f"❌ Error connecting: {e}")
            return 1

    # =========================================================================
    # Web Dashboard Methods
    # =========================================================================

    def _run_web_dashboard(self, port: int = 8709, serve_remote: bool = False) -> int:
        """Run the web dashboard

        Args:
            port: Port to run on
            serve_remote: If True, bind to 0.0.0.0 for remote clients

        Returns:
            Exit code
        """
        try:
            from .web import run_server
            run_server(host="127.0.0.1", port=port, serve_remote=serve_remote)
            return 0
        except ImportError as e:
            print("⚠️  Web dashboard dependencies not installed.")
            print("   Install with: pip install fastapi uvicorn")
            print(f"   Error: {e}")
            return 1
        except Exception as e:
            error(f"Error starting web dashboard: {e}")
            return 1

    # =========================================================================
    # Shell Completions
    # =========================================================================

    def _generate_completion(self, shell: str) -> int:
        """Generate shell completion script

        Args:
            shell: Shell type (bash, zsh, fish)

        Returns:
            Exit code
        """
        try:
            from .shell_completions import get_completion_script
            script = get_completion_script(shell)
            print(script)
            return 0
        except ImportError:
            error("Shell completions module not available")
            return 1
        except Exception as e:
            error(f"Error generating completions: {e}")
            return 1

    # =========================================================================
    # AI Agents
    # =========================================================================

    def _run_auto_fix(self, file_path: str, auto_apply: bool = False) -> int:
        """Run auto-fix agent on a file

        Args:
            file_path: Path to file to fix
            auto_apply: Whether to auto-apply fixes

        Returns:
            Exit code
        """
        try:
            from .agents.auto_fix import AutoFixAgent

            with action_spinner(f"Analyzing {file_path}") as spinner:
                agent = AutoFixAgent(self.llm)
                spinner.update("Detecting errors")
                errors = agent.analyze_file(file_path)

                if not errors:
                    spinner.success(f"No errors found in {file_path}")
                    return 0

                spinner.update(f"Found {len(errors)} error(s)")
                spinner.add_action("Analyze", file_path)

                # Generate fixes
                fixes = agent.fix_errors(errors)
                spinner.success(f"Found {len(errors)} error(s), generated {len(fixes)} fix(es)")

            # Display errors and fixes
            print(f"\n{Color.BOLD}Auto-Fix Analysis: {file_path}{Color.RESET}")
            print(f"{Color.DIM}{'─'*60}{Color.RESET}")

            for i, err in enumerate(errors, 1):
                print(f"\n{Color.RED}{Icons.FAILURE}{Color.RESET} Error {i}: {err.message}")
                print(f"   {Color.DIM}Line {err.line}: {err.error_type}{Color.RESET}")

            if fixes:
                print(f"\n{Color.BOLD}Generated Fixes:{Color.RESET}")
                for i, fix in enumerate(fixes, 1):
                    confidence = f"{fix.confidence*100:.0f}%" if hasattr(fix, 'confidence') else "N/A"
                    print(f"  {Color.GREEN}{Icons.SUCCESS}{Color.RESET} Fix {i}: {fix.description}")
                    print(f"     {Color.DIM}Confidence: {confidence}{Color.RESET}")

            if auto_apply and fixes:
                print(f"\n{Color.YELLOW}Applying fixes...{Color.RESET}")
                agent.fix_file(file_path, auto_apply=True)
                success(f"Fixes applied to {file_path}")

            return 0

        except ImportError:
            error("Auto-fix agent not available")
            return 1
        except Exception as e:
            error(f"Error running auto-fix: {e}")
            return 1

    def _run_test_generator(self, file_path: str, output_file: Optional[str] = None) -> int:
        """Run test generator agent on a file

        Args:
            file_path: Path to file to generate tests for
            output_file: Optional output file path

        Returns:
            Exit code
        """
        try:
            from .agents.test_generator import TestGeneratorAgent

            with action_spinner(f"Analyzing {file_path}") as spinner:
                agent = TestGeneratorAgent(self.llm)
                spinner.update("Finding functions to test")
                functions = agent.analyze_file(file_path)

                if not functions:
                    spinner.success(f"No testable functions found in {file_path}")
                    return 0

                spinner.update(f"Found {len(functions)} function(s)")
                spinner.add_action("Analyze", file_path)

                # Generate tests
                tests = agent.generate_tests(functions)
                spinner.success(f"Generated {len(tests)} test(s)")

            # Display results
            print(f"\n{Color.BOLD}Test Generator: {file_path}{Color.RESET}")
            print(f"{Color.DIM}{'─'*60}{Color.RESET}")

            print(f"\n{Color.CYAN}Functions found:{Color.RESET}")
            for func in functions:
                print(f"  {Icons.TREE_BRANCH} {func.name}() - line {func.line}")

            print(f"\n{Color.CYAN}Tests generated:{Color.RESET}")
            for test in tests:
                print(f"  {Color.GREEN}{Icons.SUCCESS}{Color.RESET} {test.name}")

            # Write output file
            if output_file:
                agent.generate_test_file(file_path, output_file)
                success(f"Tests written to {output_file}")
            else:
                # Default output file
                from pathlib import Path
                p = Path(file_path)
                default_output = p.parent / f"test_{p.name}"
                agent.generate_test_file(file_path, str(default_output))
                success(f"Tests written to {default_output}")

            return 0

        except ImportError:
            error("Test generator agent not available")
            return 1
        except Exception as e:
            error(f"Error generating tests: {e}")
            return 1


def main():
    """Main entry point"""
    # Pre-parse to get remote args before creating CLI
    parser = argparse.ArgumentParser(add_help=False)
    parser.add_argument("--remote", metavar="URL")
    parser.add_argument("--api-key", metavar="KEY")
    pre_args, _ = parser.parse_known_args()

    # Create CLI with remote settings if provided
    cli = NC1709CLI(
        remote_url=pre_args.remote,
        api_key=pre_args.api_key
    )
    sys.exit(cli.run())


if __name__ == "__main__":
    main()
