"""CLI entry point for kollab command."""

import asyncio
import argparse
import logging
import shlex
import sys
import re
from pathlib import Path
from importlib.metadata import version, PackageNotFoundError
from typing import Optional, TYPE_CHECKING

if TYPE_CHECKING:
    from .commands.registry import SlashCommandRegistry

# Fix encoding for Windows to support Unicode characters
if sys.platform == "win32":
    # Set UTF-8 mode for stdin/stdout/stderr
    if hasattr(sys.stdout, 'reconfigure'):
        sys.stdout.reconfigure(encoding='utf-8', errors='replace')
    if hasattr(sys.stderr, 'reconfigure'):
        sys.stderr.reconfigure(encoding='utf-8', errors='replace')
    if hasattr(sys.stdin, 'reconfigure') and sys.stdin.isatty():
        sys.stdin.reconfigure(encoding='utf-8', errors='replace')

    # Also set console output code page to UTF-8
    try:
        import ctypes
        kernel32 = ctypes.windll.kernel32
        kernel32.SetConsoleOutputCP(65001)  # UTF-8
        kernel32.SetConsoleCP(65001)  # UTF-8 for input too
    except Exception:
        pass  # Ignore if this fails

# Import from the same directory
from .application import TerminalLLMChat
from .logging import setup_bootstrap_logging

def _get_version_from_pyproject() -> str:
    """Read version from pyproject.toml for development mode."""
    try:
        pyproject_path = Path(__file__).parent.parent / "pyproject.toml"
        if pyproject_path.exists():
            content = pyproject_path.read_text()
            for line in content.splitlines():
                if line.startswith("version ="):
                    # Extract version from: version = "0.4.10"
                    return line.split("=")[1].strip().strip('"').strip("'")
    except Exception:
        pass
    return None  # Return None if not found

def _is_running_from_source() -> bool:
    """Check if we're running from source (development mode) vs installed package."""
    try:
        # If pyproject.toml exists in parent directory, we're running from source
        pyproject_path = Path(__file__).parent.parent / "pyproject.toml"
        return pyproject_path.exists()
    except Exception:
        return False

# Get version: prefer pyproject.toml when running from source, otherwise use installed version
if _is_running_from_source():
    # Development mode: use pyproject.toml
    __version__ = _get_version_from_pyproject() or "0.0.0"
else:
    # Production mode: use installed package version
    try:
        __version__ = version("kollabor")
    except PackageNotFoundError:
        __version__ = "0.0.0"


def parse_timeout(timeout_str: str) -> int:
    """Parse timeout string into seconds.

    Args:
        timeout_str: Timeout string like "30s", "2min", "1h"

    Returns:
        Timeout in seconds

    Raises:
        ValueError: If timeout format is invalid
    """
    timeout_str = timeout_str.strip().lower()

    # Match pattern like "30s", "2min", "1h"
    match = re.match(
        r"^(\d+(?:\.\d+)?)(s|sec|second|seconds|m|min|minute|minutes|h|hour|hours)$",
        timeout_str,
    )
    if not match:
        raise ValueError(
            f"Invalid timeout format: {timeout_str}. Use format like '30s', '2min', or '1h'"
        )

    value = float(match.group(1))
    unit = match.group(2)

    # Convert to seconds
    if unit in ("s", "sec", "second", "seconds"):
        return int(value)
    elif unit in ("m", "min", "minute", "minutes"):
        return int(value * 60)
    elif unit in ("h", "hour", "hours"):
        return int(value * 3600)
    else:
        raise ValueError(f"Unknown time unit: {unit}")


def build_cli_help_text(command_registry: 'SlashCommandRegistry') -> str:
    """Build help text showing CLI-invocable commands.

    Args:
        command_registry: Registry with all registered commands.

    Returns:
        Formatted help text for epilog.
    """
    # Get all non-hidden CLI commands
    cli_commands = []
    for cmd_def in command_registry.get_all_commands():
        if not cmd_def.cli_hidden:
            cli_commands.append(cmd_def)

    if not cli_commands:
        return ""

    # Build help text
    lines = ["\nslash commands (use as --command args...):"]

    # Limit to 50 commands to prevent OOM
    shown = cli_commands[:50]
    for cmd in shown:
        # Build flag list
        if cmd.aliases:
            flags = f"  --{cmd.name}, --{', --'.join(cmd.aliases[:2])}"
        else:
            flags = f"  --{cmd.name}"

        # Format help
        desc = cmd.description[:60] + "..." if len(cmd.description) > 60 else cmd.description
        if len(flags) > 24:
            lines.append(flags)
            lines.append(f"{'':24}{desc}")
        else:
            lines.append(f"{flags:24}{desc}")

    if len(cli_commands) > 50:
        lines.append(f"\n  ... and {len(cli_commands) - 50} more commands")

    return "\n".join(lines)


def print_full_help(parser, command_registry: 'SlashCommandRegistry', start_time: float = None) -> None:
    """Print full help including plugin commands.

    Args:
        parser: The argparse parser instance.
        command_registry: Registry with all registered commands.
        start_time: Optional start time for elapsed time display.
    """
    # Print standard argparse help
    parser.print_help()

    # Print plugin commands section
    cli_commands = []
    for cmd_def in command_registry.get_all_commands():
        if not cmd_def.cli_hidden:
            cli_commands.append(cmd_def)

    if cli_commands:
        print("\nPlugin Commands (invoke as kollab --<command>):")
        for cmd in sorted(cli_commands, key=lambda c: c.name):
            if cmd.aliases:
                aliases = f" ({', '.join(cmd.aliases[:2])})"
            else:
                aliases = ""
            name_part = f"--{cmd.name}{aliases}"
            desc = cmd.description[:50] + "..." if len(cmd.description) > 50 else cmd.description
            print(f"  {name_part:36} {desc}")

    # Print render time
    if start_time is not None:
        import time
        elapsed = (time.perf_counter() - start_time) * 1000
        print(f"\n({elapsed:.0f}ms)")


def discover_plugin_args() -> tuple:
    """Discover plugins and collect their CLI arg registrations.

    Returns:
        Tuple of (plugin_classes, discovery) where:
        - plugin_classes: List of plugin class types that may register CLI args
        - discovery: PluginDiscovery object for reuse in app initialization
    """
    from pathlib import Path
    from .plugins.discovery import PluginDiscovery

    # Determine plugins directory (same logic as application.py)
    package_dir = Path(__file__).parent.parent
    plugins_dir = package_dir / "plugins"
    if not plugins_dir.exists():
        plugins_dir = Path.cwd() / "plugins"

    discovery = PluginDiscovery(plugins_dir)
    plugin_classes = discovery.discover_classes_only()

    return plugin_classes, discovery


def parse_arguments(
    plugin_classes: list = None,
    argv: list[str] = None,
    command_registry: Optional['SlashCommandRegistry'] = None
):
    """Parse command-line arguments including plugin-registered args.

    Args:
        plugin_classes: Optional list of plugin classes for CLI arg registration.
        argv: Optional list of argument strings to parse (defaults to sys.argv).
        command_registry: Optional command registry for CLI slash command support.

    Returns:
        Parsed arguments namespace
    """
    # Check for -h/--help early - we'll handle it after plugins are initialized
    # so we can show plugin commands in help output
    args_to_check = argv if argv is not None else sys.argv[1:]
    help_requested = '-h' in args_to_check or '--help' in args_to_check

    # Remove help flags so argparse doesn't handle them immediately
    if help_requested:
        args_to_check = [a for a in args_to_check if a not in ('-h', '--help')]
        argv = args_to_check

    # Build CLI help epilog if registry available
    cli_help = ""
    if command_registry:
        cli_help = build_cli_help_text(command_registry)

    parser = argparse.ArgumentParser(
        description="Kollab - Terminal-based LLM chat interface",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        add_help=False,  # We handle -h ourselves to show plugin commands
        epilog=f"""
Examples:
  kollab                                    # Start interactive mode
  kollab "what is 1+1?"                     # Interactive mode with initial message
  kollab -p "what is 1+1?"                  # Pipe mode: process and exit
  kollab --timeout 30s -p "complex query"   # Pipe mode with custom timeout
  kollab --timeout 5min -p "long task"      # Pipe mode with custom timeout
  echo "hello" | kollab -p                  # Pipe mode from stdin
  cat file.txt | kollab -p --timeout 1h     # Process file with 1 hour timeout
  cat code.py | kollab "find issues" -p     # Combine stdin + query (stdin as context)
  git diff | kollab "write commit msg" -p   # Pipe git diff with instruction
  kollab --system-prompt my-prompt.md       # Use custom system prompt
  kollab --agent lint-editor               # Use specific agent
  kollab -a lint-editor                    # Short form for agent
  kollab --profile claude                  # Use specific LLM profile
  kollab -a myagent -s coding -s review    # Agent with multiple skills
  kollab --agent myagent --skill coding    # Agent with skill (long form)
  kollab --reset-config                    # Reset configs to defaults with updated profiles
  kollab --sub list                         # Execute /sub list and exit
  kollab --sub list --stay                  # Execute /sub list, then interactive mode
{cli_help}
        """,
    )

    # Manual help argument (we handle it after plugins are initialized)
    parser.add_argument(
        "-h",
        "--help",
        action="store_true",
        dest="show_help",
        help="Show this help message and exit",
    )

    parser.add_argument(
        "-v",
        "--version",
        action="version",
        version=f"%(prog)s {__version__}",
        help="Show version number and exit",
    )

    parser.add_argument(
        "query",
        nargs="?",
        help="Message to send (interactive mode) or instruction (pipe mode). In pipe mode, combines with stdin content.",
    )

    parser.add_argument(
        "-p",
        "--pipe",
        action="store_true",
        help="Pipe mode: process input and exit (requires -p flag or stdin redirect)",
    )

    parser.add_argument(
        "--timeout",
        type=str,
        default="2min",
        help="Timeout for pipe mode processing (e.g., 30s, 2min, 1h). Default: 2min",
    )

    parser.add_argument(
        "--system-prompt",
        type=str,
        default=None,
        metavar="FILE",
        help="Use a custom system prompt file (e.g., --system-prompt my-prompt.md)",
    )

    parser.add_argument(
        "-a",
        "--agent",
        type=str,
        default=None,
        metavar="AGENT",
        help="Use a specific agent (e.g., --agent lint-editor)",
    )

    parser.add_argument(
        "--profile",
        type=str,
        default=None,
        metavar="PROFILE",
        help="Use a specific LLM profile (e.g., --profile claude, --profile openai)",
    )

    parser.add_argument(
        "--save",
        action="store_true",
        default=False,
        help="Save auto-created profile to global config (use with --profile for env-var profiles)",
    )

    parser.add_argument(
        "--local",
        action="store_true",
        default=False,
        help="With --save, save profile to local project config instead of global",
    )

    parser.add_argument(
        "--simple",
        action="store_true",
        default=False,
        help="Use simple text output (no fancy boxes or colors)",
    )

    parser.add_argument(
        "-s",
        "--skill",
        type=str,
        action="append",
        default=None,
        metavar="SKILL",
        help="Load a skill for the active agent (can be used multiple times: -s skill1 -s skill2)",
    )

    parser.add_argument(
        "--font-dir",
        action="store_true",
        help="Print path to bundled Nerd Fonts directory and exit (for use with agg)",
    )

    parser.add_argument(
        "--reset-config",
        action="store_true",
        help="Reset global and local config.json to defaults with updated profiles",
    )

    # CLI slash command support
    parser.add_argument(
        "--stay",
        action="store_true",
        help="Stay in interactive mode after CLI command executes",
    )

    # Register plugin CLI arguments
    if plugin_classes:
        registered_args = set()  # Track registered argument names to detect conflicts
        for plugin_class in plugin_classes:
            if hasattr(plugin_class, 'register_cli_args'):
                try:
                    # Track arguments before registration
                    before_count = len(parser._actions)
                    plugin_class.register_cli_args(parser)
                    # Check for conflicts by detecting new argument names
                    for action in parser._actions[before_count:]:
                        for arg_str in action.option_strings:
                            if arg_str in registered_args:
                                logging.warning(
                                    f"Plugin {plugin_class.__name__} argument conflict: "
                                    f"{arg_str} already registered by another plugin"
                                )
                            registered_args.add(arg_str)
                except Exception as e:
                    logging.warning(f"Plugin {plugin_class.__name__} arg registration failed: {e}")

    # Parse known args, capture unknown as potential CLI commands
    args, unknown = parser.parse_known_args(argv)

    # Store help request flag and parser for deferred help generation
    args._help_pending = help_requested or getattr(args, 'show_help', False)
    args._parser = parser

    # Initialize CLI command storage
    args.cli_command = None
    args._unknown_args = []

    # Process unknown args as potential CLI commands
    if unknown:
        if unknown[0].startswith('--') and len(unknown[0]) > 2:
            cmd_name = unknown[0][2:]  # Strip '--'

            # Validate command name (prevent injection)
            if not cmd_name.replace('-', '').replace('_', '').isalnum():
                parser.error(f"Invalid command name: {cmd_name}")

            # IMPORTANT: Handle positional args that got split between query and unknown
            # due to argparse's greedy positional matching.
            #
            # Case 1: `kollab --permission trust`
            #   -> unknown=['--permission'], query='trust'
            #   -> CLI arg is 'trust', no message
            #
            # Case 2: `kollab "hello" --permission trust`
            #   -> unknown=['--permission', 'trust'], query='hello'
            #   -> CLI arg is 'trust', message is 'hello'
            #
            # Case 3: `kollab --permission trust --stay "hello"`
            #   -> unknown=['--permission', 'hello'], query='trust'
            #   -> CLI arg is 'trust', message is 'hello'
            #   -> Need to detect this by checking argv order!

            cmd_flag = unknown[0]
            non_flag_unknown = [a for a in unknown[1:] if not a.startswith('-')]

            if args.query and len(unknown) == 1:
                # Case 1: Just CLI flag, query is the CLI arg
                unknown.append(args.query)
                args.query = None
            elif args.query and non_flag_unknown:
                # Cases 2 or 3: Both query and extra positionals exist
                # Check original argv order to determine which is which
                argv_to_check = argv if argv is not None else sys.argv[1:]
                try:
                    cmd_pos = argv_to_check.index(cmd_flag)
                    query_pos = argv_to_check.index(args.query)

                    if query_pos > cmd_pos:
                        # Case 3: Query appears AFTER CLI flag in argv
                        # -> query is actually the CLI arg (argparse grabbed it first positionally)
                        # -> the positional in unknown is the user's message
                        cli_arg = args.query
                        args.query = non_flag_unknown[0]
                        # Rebuild unknown with the correct CLI arg
                        unknown = [cmd_flag, cli_arg]
                    # else: Case 2 - query is BEFORE CLI flag, it's the user's message
                    # No changes needed, unknown already has the CLI arg
                except ValueError:
                    # Couldn't find positions, leave as-is
                    pass

            if command_registry:
                # Full processing with registry
                cmd_def = command_registry.get_command(cmd_name)

                if cmd_def:
                    if cmd_def.cli_hidden:
                        parser.error(
                            f"Command /{cmd_name} is not available from command line.\n"
                            f"Start kollabor in interactive mode and use /{cmd_name} instead."
                        )

                    # Sanitize arguments
                    sanitized_args = [shlex.quote(arg) for arg in unknown[1:]]

                    args.cli_command = {
                        'name': cmd_def.name,  # Use canonical name
                        'args': sanitized_args,
                        'raw': f"/{cmd_def.name} {' '.join(sanitized_args)}".strip(),
                        'requires_interactive': cmd_def.requires_interactive,
                        'command_def': cmd_def
                    }
                else:
                    # Unknown command - show available commands
                    available = [c.name for c in command_registry.get_all_commands()
                                if not c.cli_hidden]
                    available_flags = [f"--{c}" for c in sorted(available[:10])]
                    parser.error(
                        f"Unknown command: --{cmd_name}\n"
                        f"Available commands: {', '.join(available_flags)}\n"
                        f"Run 'kollab -h' to see all"
                    )
            else:
                # No registry yet - store for later processing in application.py
                args._unknown_args = unknown
        else:
            # Unknown args that don't look like CLI commands
            parser.error(f"Unrecognized arguments: {' '.join(unknown)}")

    return args


def process_cli_command(
    args: argparse.Namespace,
    command_registry: 'SlashCommandRegistry'
) -> Optional[dict]:
    """Process CLI command with command registry.

    Called from application.py once command registry is available.

    Args:
        args: Parsed arguments with potential _unknown_args.
        command_registry: Initialized command registry.

    Returns:
        CLI command dict if valid command found, None otherwise.

    Raises:
        ValueError: If command is invalid or not CLI-invocable.
    """
    # Check for stored unknown args
    unknown = getattr(args, '_unknown_args', [])
    if not unknown:
        return args.cli_command  # May already be set

    if not unknown[0].startswith('--') or len(unknown[0]) <= 2:
        raise ValueError(f"Unrecognized arguments: {' '.join(unknown)}")

    cmd_name = unknown[0][2:]  # Strip '--'

    # Validate command name
    if not cmd_name.replace('-', '').replace('_', '').isalnum():
        raise ValueError(f"Invalid command name: {cmd_name}")

    # NOTE: Positional arg fixing already happened in parse_arguments()
    # when _unknown_args was stored. Don't re-apply the fix here.

    # Look up in registry
    cmd_def = command_registry.get_command(cmd_name)

    if not cmd_def:
        available = [c.name for c in command_registry.get_all_commands()
                    if not c.cli_hidden]
        available_flags = [f"--{c}" for c in sorted(available[:10])]
        raise ValueError(
            f"Unknown command: --{cmd_name}\n"
            f"Available commands: {', '.join(available_flags)}\n"
            f"Run 'kollab -h' to see all"
        )

    if cmd_def.cli_hidden:
        raise ValueError(
            f"Command /{cmd_name} is not available from command line.\n"
            f"Start kollabor in interactive mode and use /{cmd_name} instead."
        )

    # Sanitize arguments
    sanitized_args = [shlex.quote(arg) for arg in unknown[1:]]

    return {
        'name': cmd_def.name,  # Use canonical name
        'args': sanitized_args,
        'raw': f"/{cmd_def.name} {' '.join(sanitized_args)}".strip(),
        'requires_interactive': cmd_def.requires_interactive,
        'command_def': cmd_def
    }


def handle_early_plugin_args(args, plugin_classes: list) -> bool:
    """Let plugins handle args that should exit early.

    Args:
        args: Parsed command-line arguments.
        plugin_classes: List of plugin classes.

    Returns:
        True if a plugin requested early exit, False otherwise.
    """
    should_exit = False

    for plugin_class in plugin_classes:
        if hasattr(plugin_class, 'handle_early_args'):
            try:
                result = plugin_class.handle_early_args(args)
                # Handle both old-style (bool) and new-style (tuple) returns
                if isinstance(result, tuple):
                    plugin_should_exit, output_message = result
                    if plugin_should_exit and output_message:
                        print(output_message)
                    should_exit = should_exit or plugin_should_exit
                elif result:  # Old-style bool return
                    should_exit = True
            except Exception as e:
                logging.error(f"Plugin {plugin_class.__name__} early arg handler failed: {e}")

    return should_exit


async def async_main() -> None:
    """Main async entry point for the application with proper error handling."""
    import time
    _start_time = time.perf_counter()

    # Check for help flag - use null logging to avoid file creation
    help_mode = '-h' in sys.argv or '--help' in sys.argv

    if help_mode:
        # Use null logging for help mode - no files created
        logging.basicConfig(
            level=logging.WARNING,
            handlers=[logging.NullHandler()]
        )
    else:
        # Setup bootstrap logging before application starts
        setup_bootstrap_logging()

    logger = logging.getLogger(__name__)

    # Early plugin discovery for CLI args
    plugin_classes, discovery = discover_plugin_args()

    # Parse all args (core + plugin)
    args = parse_arguments(plugin_classes)
    args._start_time = _start_time  # For timing display in help

    # Handle early-exit args from plugins (like --capture)
    if handle_early_plugin_args(args, plugin_classes):
        return  # Plugin handled it and requested exit

    # Handle --reset-config: reset configs and exit
    if args.reset_config:
        from .utils.config_utils import initialize_config

        global_config = Path.home() / ".kollabor-cli" / "config.json"
        local_config = Path.cwd() / ".kollabor-cli" / "config.json"

        print("Resetting configuration files...")
        initialize_config(force=True)
        print("Configuration reset complete!")
        print(f"  - Global config: {global_config}")
        print(f"  - Local config:  {local_config}")
        return

    # Handle --font-dir: print font directory and exit
    if args.font_dir:
        try:
            from fonts import get_font_dir

            print(get_font_dir())
        except ImportError:
            # Fallback for development mode
            font_dir = Path(__file__).parent.parent / "fonts"
            if font_dir.exists():
                print(font_dir)
            else:
                print("Error: fonts directory not found", file=sys.stderr)
                sys.exit(1)
        return

    # Check if we have a CLI command or --help pending
    # These should bypass pipe mode detection
    has_cli_command = (
        getattr(args, 'cli_command', None) is not None or
        getattr(args, '_unknown_args', []) or
        getattr(args, '_help_pending', False)
    )

    # Determine if we're in pipe mode and what the input is
    # Pipe mode ONLY when:
    # 1. Explicit -p/--pipe flag is set, OR
    # 2. stdin is not a tty (redirected/piped input) AND no CLI command
    pipe_mode_active = args.pipe or (not sys.stdin.isatty() and not has_cli_command)

    piped_input = None
    initial_message = None

    if pipe_mode_active:
        # Pipe mode: read from query arg and/or stdin, process and exit
        stdin_content = None
        query_text = None

        # Check for piped input from stdin
        if not sys.stdin.isatty():
            stdin_content = sys.stdin.read().strip()

        # Check for query argument
        if args.query:
            query_text = args.query.strip()

        # Combine both if present (stdin as context, query as instruction)
        if stdin_content and query_text:
            piped_input = f"{stdin_content}\n\n{query_text}"
        elif stdin_content:
            piped_input = stdin_content
        elif query_text:
            piped_input = query_text
        else:
            print("Error: No input received from pipe", file=sys.stderr)
            sys.exit(1)
    else:
        # Interactive mode: query arg becomes initial message
        if args.query:
            initial_message = args.query.strip()

    app = None
    try:
        logger.info("Creating application instance...")
        
        # Create plugin registry once to avoid double discovery
        # Reuse the discovery object from CLI arg parsing
        from .plugins import PluginRegistry
        plugin_registry = PluginRegistry(discovery.plugins_dir)
        plugin_registry.discovery = discovery  # Reuse discovery from CLI args
        
        app = TerminalLLMChat(
            args=args,
            system_prompt_file=args.system_prompt,
            agent_name=args.agent,
            profile_name=args.profile,
            save_profile=args.save,
            save_local=args.local,
            skill_names=args.skill,
            plugin_registry=plugin_registry,  # Pass pre-initialized registry
        )
        logger.info("Starting application...")

        if pipe_mode_active and piped_input:
            # Parse timeout for pipe mode
            try:
                timeout_seconds = parse_timeout(args.timeout)
            except ValueError as e:
                print(f"Error: {e}", file=sys.stderr)
                sys.exit(1)

            # Pipe mode: send input and exit after response
            await app.start_pipe_mode(piped_input, timeout=timeout_seconds)
        else:
            # Interactive mode (with optional initial message)
            await app.start(initial_message=initial_message)
    except KeyboardInterrupt:
        # print("\n\nApplication interrupted by user")
        logger.info("Application interrupted by user")
    except Exception as e:
        print(f"\n\nApplication failed to start: {e}")
        logger.error(f"Application startup failed: {type(e).__name__}: {e}")
        # Print helpful error message for common issues
        if "permission" in str(e).lower():
            print(
                "\nTip: Check file permissions and try running with appropriate privileges"
            )
        elif "already in use" in str(e).lower():
            print(
                "\nTip: Another instance may be running. Try closing other applications."
            )
        elif "not found" in str(e).lower():
            print("\nTip: Check that all required dependencies are installed.")
        raise  # Re-raise for full traceback in debug mode
    finally:
        # Ensure cleanup happens even if startup fails (skip if in pipe mode and already cleaned up)
        pipe_mode = getattr(app, "pipe_mode", False) if app else False
        if app and not app._startup_complete and not pipe_mode:
            logger.info("Performing emergency cleanup after startup failure...")
            try:
                await app.cleanup()
            except Exception as cleanup_error:
                logger.error(f"Emergency cleanup failed: {cleanup_error}")
                # print("Warning: Some resources may not have been cleaned up properly")


def cli_main() -> None:
    """Synchronous entry point for pip-installed CLI command."""
    try:
        asyncio.run(async_main())
    except KeyboardInterrupt:
        # print("\n\nExited cleanly!")
        pass
    except Exception as e:
        print(f"\n\nFatal error: {e}")
        # Exit with error code for scripts that depend on it
        sys.exit(1)
