import argparse
import asyncio
import json
import sys
from pathlib import Path

from smf.inspector.client import SMFInspector
from smf.inspector.modes import interactive_mode, list_mode


def run_official_inspector(server_file: str, args: list) -> int:
    """
    Run the official MCP Inspector using npx.
    
    Args:
        server_file: Path to server file
        args: Additional arguments
        
    Returns:
        Exit code
    """
    import shutil
    import subprocess
    
    # Check for npx
    npx_path = shutil.which("npx")
    if not npx_path:
        print("Error: 'npx' is required to run the official inspector.", file=sys.stderr)
        print("Please install Node.js and npm, or use --text for the text-based inspector.", file=sys.stderr)
        return 1
        
    # Construct command
    # npx -y @modelcontextprotocol/inspector uv run <server_file>
    # Use the resolved npx path
    # Also resolve server_file to absolute path to avoid issues with relative paths (like .\server.py)
    # when passed through npx/inspector on Windows.
    # IMPORTANT: Use .as_posix() to ensure forward slashes are used.
    # Backslashes can be stripped or misinterpreted as escape characters when passed through
    # the inspector's argument parsing or shell execution, leading to "H:Desktopsmfadminserver.py".
    server_path = Path(server_file).resolve().as_posix()
    cmd = [npx_path, "-y", "@modelcontextprotocol/inspector", "uv", "run", server_path]
    
    # Add any additional args passed to the server
    # Note: The inspector might not pass these through easily depending on how it invokes the command
    # For now, we assume server_file is the script path
    
    print(f"Launching official MCP Inspector for {server_file}...")
    print(f"Command: {' '.join(cmd)}")
    
    try:
        # Run interactively
        # On Windows, shell=True is often needed for .cmd files even with full path
        use_shell = sys.platform == "win32"
        return subprocess.call(cmd, shell=use_shell)
    except KeyboardInterrupt:
        return 0
    except Exception as e:
        print(f"Error running inspector: {e}", file=sys.stderr)
        return 1


def main() -> int:
    """Main CLI entry point."""
    parser = argparse.ArgumentParser(
        description="SMF Inspector - Interactive tool for testing SMF servers",
        epilog="""
Examples:
  # Launch Official Web Inspector (recommended)
  smf-inspector server.py
  
  # Launch Text-based Inspector
  smf-inspector server.py --text
  
  # List all components (Text mode)
  smf-inspector server.py --list
  
  # Call a tool (Text mode)
  smf-inspector server.py --tool greet --args '{"name": "Alice"}'
        """,
        formatter_class=argparse.RawDescriptionHelpFormatter,
    )
    parser.add_argument(
        "server",
        help="Server file path (e.g., server.py)",
        nargs="?",
        default="server.py",
    )
    parser.add_argument(
        "--text", "--cli",
        action="store_true",
        help="Use text-based inspector instead of official web UI",
    )
    parser.add_argument(
        "--list",
        action="store_true",
        help="List all components and exit (implies --text)",
    )
    parser.add_argument(
        "--tool",
        help="Call a tool (implies --text)",
    )
    parser.add_argument(
        "--args",
        help="Tool arguments as JSON",
    )
    parser.add_argument(
        "--resource",
        help="Read a resource (implies --text)",
    )
    parser.add_argument(
        "--prompt",
        help="Get a prompt (implies --text)",
    )

    args = parser.parse_args()

    # Determine mode
    use_text_mode = args.text or args.list or args.tool or args.resource or args.prompt
    
    if not use_text_mode:
        return run_official_inspector(args.server, [])

    # Text mode
    # For text mode, we might need to handle "server.py:mcp" format vs "server.py"
    # The official inspector expects a file to run with `uv run`.
    # Our text inspector loads the module.
    
    server_path = args.server
    # If it doesn't have a colon, append default :mcp for text inspector
    if ":" not in server_path and not server_path.endswith(".py"):
         # It might be just a module name? 
         pass
    elif ":" not in server_path:
        server_path = f"{server_path}:mcp"

    inspector = SMFInspector(server_path)

    async def run_tool_command():
        """Run tool command with proper async context."""
        await inspector.connect()
        try:
            if args.args:
                # Try to parse JSON, with helpful error messages
                try:
                    tool_args = json.loads(args.args)
                except json.JSONDecodeError as e:
                    print(f"Error parsing JSON arguments: {e}", file=sys.stderr)
                    print(f"\nReceived: {args.args!r}", file=sys.stderr)
                    print("\nTips:", file=sys.stderr)
                    print("  PowerShell: smf-inspector server.py --tool greet --args '{\"name\": \"Alice\"}'", file=sys.stderr)
                    print("  Bash/CMD:   smf-inspector server.py --tool greet --args '{\"name\": \"Alice\"}'", file=sys.stderr)
                    return 1
            else:
                tool_args = {}
            result = await inspector.call_tool(args.tool, tool_args)
            print(json.dumps(result, indent=2, default=str))
        finally:
            await inspector.disconnect()
        return 0

    async def run_resource_command():
        """Run resource command with proper async context."""
        await inspector.connect()
        try:
            result = await inspector.read_resource(args.resource)
            print(json.dumps(result, indent=2, default=str))
        finally:
            await inspector.disconnect()
        return 0

    async def run_prompt_command():
        """Run prompt command with proper async context."""
        await inspector.connect()
        try:
            result = await inspector.get_prompt(args.prompt)
            print(json.dumps(result, indent=2, default=str))
        finally:
            await inspector.disconnect()
        return 0

    try:
        if args.list:
            exit_code = asyncio.run(list_mode(inspector))
            return exit_code
        elif args.tool:
            exit_code = asyncio.run(run_tool_command())
            return exit_code
        elif args.resource:
            exit_code = asyncio.run(run_resource_command())
            return exit_code
        elif args.prompt:
            exit_code = asyncio.run(run_prompt_command())
            return exit_code
        else:
            # Interactive mode
            asyncio.run(interactive_mode(inspector))
    except KeyboardInterrupt:
        print("\n\nInspector stopped.")
        return 0
    except Exception as e:
        print(f"Error: {e}", file=sys.stderr)
        return 1

    return 0
