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

import asyncio
import argparse
import logging
import sys
import re
from pathlib import Path
from importlib.metadata import version, PackageNotFoundError

# 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 discover_plugin_args() -> list:
    """Discover plugins and collect their CLI arg registrations.

    Returns:
        List of plugin class types that may register CLI args.
    """
    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


def parse_arguments(plugin_classes: list = None, argv: list[str] = 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).

    Returns:
        Parsed arguments namespace
    """
    parser = argparse.ArgumentParser(
        description="Kollab - Terminal-based LLM chat interface",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
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
  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
        """,
    )

    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 by default, pipe mode with -p flag)",
    )

    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(
        "-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",
    )

    # 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}")

    return parser.parse_args(argv)


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."""
    # Setup bootstrap logging before application starts
    setup_bootstrap_logging()
    logger = logging.getLogger(__name__)

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

    # Parse all args (core + plugin)
    args = parse_arguments(plugin_classes)

    # 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

    # 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)
    pipe_mode_active = args.pipe or not sys.stdin.isatty()

    piped_input = None
    initial_message = None

    if pipe_mode_active:
        # Pipe mode: read from query arg or stdin, process and exit
        if args.query:
            piped_input = args.query.strip()
        else:
            # Read from stdin
            piped_input = sys.stdin.read().strip()
            if not piped_input:
                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...")
        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,
        )
        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)
