"""Main entry point and CLI loop for ieums."""
# ruff: noqa: E402, BLE001, PLR0912, PLR0915

# Suppress deprecation warnings from langchain_core (e.g., Pydantic V1 on Python 3.14+)
# ruff: noqa: E402
import warnings

warnings.filterwarnings("ignore", module="langchain_core._api.deprecation")

import argparse
import asyncio
import os
import sys
import warnings
from pathlib import Path

# Suppress Pydantic v1 compatibility warnings from langchain on Python 3.14+
warnings.filterwarnings("ignore", message=".*Pydantic V1.*", category=UserWarning)

from rich.text import Text

from ieum._version import __version__

# Now safe to import agent (which imports LangChain modules)
from ieum.agent import create_cli_agent, list_agents, reset_agent

# CRITICAL: Import config FIRST to set LANGSMITH_PROJECT before LangChain loads
from ieum.config import (
    NoLLMConfiguredError,
    console,
    create_model,
    settings,
)
from ieum.i18n import set_locale, get_locale, t
from ieum.sessions import (
    delete_thread_command,
    generate_thread_id,
    get_checkpointer,
    get_most_recent,
    get_store,
    get_thread_agent,
    list_threads_command,
    load_env_file,
    save_env_file,
    thread_exists,
)
from ieum.ralph import execute_ralph_command, setup_ralph_parser
from ieum.skills import execute_skills_command, setup_skills_parser
from ieum.tools import fetch_url, http_request, python_repl, ripgrep, web_search
from ieum.approval_policy import APPROVAL_POLICIES
from ieum.ui import show_help


def check_cli_dependencies() -> None:
    """Check if CLI optional dependencies are installed."""
    missing = []

    try:
        import requests  # noqa: F401
    except ImportError:
        missing.append("requests")

    try:
        import dotenv  # noqa: F401
    except ImportError:
        missing.append("python-dotenv")

    try:
        import tavily  # noqa: F401
    except ImportError:
        missing.append("tavily-python")

    try:
        import textual  # noqa: F401
    except ImportError:
        missing.append("textual")

    if missing:
        print("\n❌ Missing required CLI dependencies!")
        print("\nThe following packages are required to use the IEUM CLI:")
        for pkg in missing:
            print(f"  - {pkg}")
        print("\nPlease install them with:")
        print("  pip install 'ieum[cli]'")
        sys.exit(1)


def parse_args() -> argparse.Namespace:
    """Parse command line arguments."""
    parser = argparse.ArgumentParser(
        description="IEUM - AI Coding Assistant",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        add_help=False,
    )
    parser.add_argument(
        "--version",
        action="version",
        version=f"ieum {__version__}",
    )

    subparsers = parser.add_subparsers(dest="command", help="Command to run")

    # List command
    subparsers.add_parser("list", help="List all available agents")

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

    # Reset command
    reset_parser = subparsers.add_parser("reset", help="Reset an agent")
    reset_parser.add_argument("--agent", required=True, help="Name of agent to reset")
    reset_parser.add_argument(
        "--target", dest="source_agent", help="Copy prompt from another agent"
    )

    # Skills command - setup delegated to skills module
    setup_skills_parser(subparsers)

    # Ralph command - autonomous looping mode
    setup_ralph_parser(subparsers)

    # Threads command
    threads_parser = subparsers.add_parser("threads", help="Manage conversation threads")
    threads_sub = threads_parser.add_subparsers(dest="threads_command")

    # threads list
    threads_list = threads_sub.add_parser("list", help="List threads")
    threads_list.add_argument(
        "--agent", default=None, help="Filter by agent name (default: show all)"
    )
    threads_list.add_argument("--limit", type=int, default=20, help="Max threads (default: 20)")

    # threads delete
    threads_delete = threads_sub.add_parser("delete", help="Delete a thread")
    threads_delete.add_argument("thread_id", help="Thread ID to delete")

    # Default interactive mode
    parser.add_argument(
        "--agent",
        default="agent",
        help="Agent identifier for separate memory stores (default: agent).",
    )

    # Thread resume argument - matches PR #638: -r for most recent, -r <ID> for specific
    parser.add_argument(
        "-r",
        "--resume",
        dest="resume_thread",
        nargs="?",
        const="__MOST_RECENT__",
        default=None,
        help="Resume thread: -r for most recent, -r <ID> for specific thread",
    )

    # Initial prompt - auto-submit when session starts
    parser.add_argument(
        "-m",
        "--message",
        dest="initial_prompt",
        help="Initial prompt to auto-submit when session starts",
    )

    parser.add_argument(
        "--model",
        help="Model to use (e.g., claude-sonnet-4-5-20250929, gpt-5.2). "
        "Provider is auto-detected from model name.",
    )
    parser.add_argument(
        "--auto-approve",
        action="store_true",
        help="Auto-approve tool usage without prompting (disables human-in-the-loop)",
    )
    parser.add_argument(
        "--approval-policy",
        default="manual",
        choices=APPROVAL_POLICIES,
        help="Approval preset policy (default: manual)",
    )

    # Web/Server mode arguments
    parser.add_argument(
        "--web",
        action="store_true",
        help="Start web UI server (FastAPI + browser interface)",
    )
    parser.add_argument(
        "--server",
        action="store_true",
        help=argparse.SUPPRESS,  # Hidden alias for --web
    )
    parser.add_argument(
        "--port",
        type=int,
        default=8765,
        help="Web server port (default: 8765)",
    )
    parser.add_argument(
        "--host",
        default="127.0.0.1",
        help="Web server host (default: 127.0.0.1, use 0.0.0.0 for external access)",
    )
    parser.add_argument(
        "--cors-origins",
        default="",
        help="Comma-separated list of allowed CORS origins (default: server config defaults)",
    )
    parser.add_argument(
        "--path",
        default=None,
        help="Working directory path (default: current directory)",
    )

    return parser.parse_args()


async def run_textual_cli_async(
    assistant_id: str,
    *,
    auto_approve: bool = False,
    approval_policy: str = "manual",
    model_name: str | None = None,
    thread_id: str | None = None,
    is_resumed: bool = False,
    initial_prompt: str | None = None,
) -> None:
    """Run the Textual CLI interface (async version).

    Args:
        assistant_id: Agent identifier for memory storage
        auto_approve: Whether to auto-approve tool usage
        approval_policy: Approval preset policy
        model_name: Optional model name to use
        thread_id: Thread ID to use (new or resumed)
        is_resumed: Whether this is a resumed session
        initial_prompt: Optional prompt to auto-submit when session starts
    """
    from ieum.app import run_textual_app

    # Try to create model - may fail if no API keys configured
    model = None
    setup_mode = False
    try:
        model = create_model(model_name)
    except NoLLMConfiguredError:
        setup_mode = True
        console.print(f"[yellow]{t('tui.warning.no_api_key')}[/yellow]")
        console.print(f"[dim]{t('tui.warning.setup_hint')}[/dim]\n")

    # Get context window size from model profile (if available)
    max_context_tokens = None
    if model:
        profile = getattr(model, "profile", None)
        if profile and isinstance(profile, dict):
            max_context_tokens = profile.get("max_input_tokens")

    # Show thread info (only if not in setup mode)
    if not setup_mode:
        if is_resumed:
            console.print(f"[green]Resuming thread:[/green] {thread_id}")
        else:
            console.print(f"[dim]Thread: {thread_id}[/dim]")

        # Show Langfuse status
        if settings.has_langfuse:
            host = os.environ.get("LANGFUSE_BASE_URL") or os.environ.get("LANGFUSE_HOST") or "https://cloud.langfuse.com"
            console.print(f"[dim]Langfuse: {host}[/dim]")
        else:
            console.print("[dim]Langfuse: Not configured[/dim]")

    # Use async context managers for checkpointer and store
    async with get_checkpointer() as checkpointer, get_store() as store:
        agent = None
        composite_backend = None

        if not setup_mode:
            # Create agent with conditional tools
            tools = [http_request, fetch_url, ripgrep, python_repl]
            if settings.has_tavily:
                tools.append(web_search)

            try:
                agent, composite_backend = create_cli_agent(
                    model=model,
                    assistant_id=assistant_id,
                    tools=tools,
                    auto_approve=auto_approve,
                    checkpointer=checkpointer,
                    store=store,
                    enable_skills=False,  # TUI handles /skill commands directly
                )
            except Exception as e:
                error_text = Text("❌ Failed to create agent: ", style="red")
                error_text.append(str(e))
                console.print(error_text)
                sys.exit(1)

        # Run Textual app (in setup mode if no model)
        await run_textual_app(
            agent=agent,
            assistant_id=assistant_id,
            backend=composite_backend,
            auto_approve=auto_approve,
            approval_policy=approval_policy,
            cwd=Path.cwd(),
            thread_id=thread_id,
            initial_prompt=initial_prompt,
            checkpointer=checkpointer,
            store=store,
            max_context_tokens=max_context_tokens,
            setup_mode=setup_mode,
        )


def _is_windows_terminal() -> bool:
    """Check if running inside Windows Terminal (vs raw PowerShell/cmd)."""
    # Windows Terminal sets WT_SESSION environment variable
    return os.environ.get("WT_SESSION") is not None


def _run_web_server(args: argparse.Namespace) -> None:
    """Run the web UI server.

    Args:
        args: Parsed command line arguments.
    """
    try:
        from ieum.server import run_server
        from ieum.server.config import ServerConfig
    except ImportError as e:
        print("\n❌ Web server dependencies not installed!")
        print("\nPlease install with:")
        print("  pip install ieum[server]")
        print(f"\nError: {e}")
        sys.exit(1)

    # Parse CORS origins (optional override)
    cors_origins = None
    if args.cors_origins:
        parsed = [origin.strip() for origin in args.cors_origins.split(",") if origin.strip()]
        if "*" in parsed:
            msg = "Wildcard CORS origin '*' is not allowed for web server."
            raise SystemExit(msg)
        cors_origins = parsed

    # Resolve working directory
    if args.path:
        working_dir = os.path.abspath(args.path)
    else:
        # Default: create playground folder in current directory
        working_dir = os.path.join(os.getcwd(), "playground")
        os.makedirs(working_dir, exist_ok=True)

    config_kwargs = dict(
        host=args.host,
        port=args.port,
        working_directory=working_dir,
    )
    if cors_origins is not None:
        config_kwargs["cors_origins"] = cors_origins
    config = ServerConfig(**config_kwargs)

    url = f"http://{config.host}:{config.port}"
    if config.host == "0.0.0.0":
        url = f"http://localhost:{config.port}"

    console.print(f"\n[bold blue]🌐 IEUM Web UI[/bold blue]")
    console.print(f"[dim]━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━[/dim]")
    console.print(f"[green]➜[/green] Local:   [bold]{url}[/bold]")
    if config.host == "0.0.0.0":
        console.print(f"[green]➜[/green] Network: [dim]http://0.0.0.0:{config.port}[/dim]")
    console.print(f"[green]➜[/green] Path:    [dim]{working_dir}[/dim]")
    console.print(f"\n[dim]Press Ctrl+C to stop[/dim]\n")

    run_server(config)


def _setup_windows_terminal() -> None:
    """Configure Windows terminal for proper ANSI escape sequence handling."""
    if sys.platform != "win32":
        return

    # Check for safe mode
    if os.environ.get("IEUM_SAFE_MODE") == "1":
        os.environ["TERM"] = "dumb"
        os.environ["NO_COLOR"] = "1"
        return

    # If not in Windows Terminal, use safe mode automatically
    if not _is_windows_terminal():
        # Raw PowerShell/cmd - use minimal terminal features
        os.environ.setdefault("TERM", "dumb")
        # Don't set NO_COLOR - let Textual handle it
        return

    # Windows Terminal - enable full features
    if "TERM" not in os.environ:
        os.environ["TERM"] = "xterm-256color"

    # Enable Virtual Terminal Processing on Windows
    try:
        import ctypes
        from ctypes import wintypes

        kernel32 = ctypes.windll.kernel32

        # Constants
        STD_OUTPUT_HANDLE = -11
        ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
        ENABLE_PROCESSED_OUTPUT = 0x0001

        # Get stdout handle
        handle = kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
        if handle == -1:
            return

        # Get current mode
        mode = wintypes.DWORD()
        if not kernel32.GetConsoleMode(handle, ctypes.byref(mode)):
            return

        # Enable VT processing
        new_mode = mode.value | ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT
        kernel32.SetConsoleMode(handle, new_mode)

        # Also set UTF-8 code page
        kernel32.SetConsoleOutputCP(65001)
    except Exception:
        pass  # Silently ignore if it fails


def cli_main() -> None:
    """Entry point for console script."""
    # Configure Windows terminal for ANSI escape sequences
    _setup_windows_terminal()

    # Warn if not running in Windows Terminal on Windows
    if sys.platform == "win32" and not _is_windows_terminal():
        console.print(
            f"[yellow]{t('tui.warning.powershell')}[/yellow]\n"
            f"[dim]{t('tui.warning.powershell_hint')}[/dim]\n"
            f"[dim]{t('tui.warning.powershell_install')}[/dim]\n"
        )

    # Fix for gRPC fork issue on macOS
    # https://github.com/grpc/grpc/issues/37642
    if sys.platform == "darwin":
        os.environ["GRPC_ENABLE_FORK_SUPPORT"] = "0"

    # Note: LANGSMITH_PROJECT is already overridden in config.py (before LangChain imports)
    # This ensures agent traces → IEUM_LANGSMITH_PROJECT
    # Shell commands → user's original LANGSMITH_PROJECT (via ShellMiddleware env)

    # Check dependencies first
    check_cli_dependencies()

    # --- First-run language selection ---
    env_settings = load_env_file("global")
    if env_settings is None or "lang" not in env_settings:
        # No language set yet - show bilingual selection prompt
        print("\n  Select language / 언어를 선택하세요:\n")
        print("    1. English")
        print("    2. 한국어 (Korean)\n")
        try:
            choice = input("  > ").strip()
        except (EOFError, KeyboardInterrupt):
            choice = "1"
        if choice == "2":
            selected_lang = "ko"
        else:
            selected_lang = "en"
        # Save to .env
        settings_to_save = env_settings or {}
        settings_to_save["lang"] = selected_lang
        save_env_file(settings_to_save, "global")
        os.environ["IEUM_LANG"] = selected_lang
        set_locale(selected_lang)
        lang_name = "English" if selected_lang == "en" else "한국어"
        print(f"  → {lang_name}\n")
    else:
        # Apply saved language setting
        saved_lang = env_settings.get("lang", "en")
        os.environ["IEUM_LANG"] = saved_lang
        set_locale(saved_lang)

    try:
        args = parse_args()

        # Handle web/server mode
        if getattr(args, "web", False) or getattr(args, "server", False):
            _run_web_server(args)
            return

        if args.command == "help":
            show_help()
        elif args.command == "list":
            list_agents()
        elif args.command == "reset":
            reset_agent(args.agent, args.source_agent)
        elif args.command == "skills":
            execute_skills_command(args)
        elif args.command == "ralph":
            execute_ralph_command(args)
        elif args.command == "threads":
            if args.threads_command == "list":
                asyncio.run(
                    list_threads_command(
                        agent_name=getattr(args, "agent", None),
                        limit=getattr(args, "limit", 20),
                    )
                )
            elif args.threads_command == "delete":
                asyncio.run(delete_thread_command(args.thread_id))
            else:
                console.print("[yellow]Usage: ieum threads <list|delete>[/yellow]")
        else:
            # Interactive mode - handle thread resume
            thread_id = None
            is_resumed = False

            if args.resume_thread == "__MOST_RECENT__":
                # -r (no ID): Get most recent thread
                # If --agent specified, filter by that agent; otherwise get most recent overall
                agent_filter = args.agent if args.agent != "agent" else None
                thread_id = asyncio.run(get_most_recent(agent_filter))
                if thread_id:
                    is_resumed = True
                    agent_name = asyncio.run(get_thread_agent(thread_id))
                    if agent_name:
                        args.agent = agent_name
                else:
                    if agent_filter:
                        msg = Text("No previous thread for '", style="yellow")
                        msg.append(args.agent)
                        msg.append("', starting new.", style="yellow")
                    else:
                        msg = Text("No previous threads, starting new.", style="yellow")
                    console.print(msg)

            elif args.resume_thread:
                # -r <ID>: Resume specific thread
                if asyncio.run(thread_exists(args.resume_thread)):
                    thread_id = args.resume_thread
                    is_resumed = True
                    if args.agent == "agent":
                        agent_name = asyncio.run(get_thread_agent(thread_id))
                        if agent_name:
                            args.agent = agent_name
                else:
                    error_msg = Text("Thread '", style="red")
                    error_msg.append(args.resume_thread)
                    error_msg.append("' not found.", style="red")
                    console.print(error_msg)
                    console.print(
                        "[dim]Use 'ieum threads list' to see available threads.[/dim]"
                    )
                    sys.exit(1)

            # Generate new thread ID if not resuming
            if thread_id is None:
                thread_id = generate_thread_id()

            # Change to specified working directory if provided
            if args.path:
                working_dir = os.path.abspath(args.path)
                os.chdir(working_dir)

            # Run Textual CLI
            asyncio.run(
                run_textual_cli_async(
                    assistant_id=args.agent,
                    auto_approve=args.auto_approve,
                    approval_policy=args.approval_policy,
                    model_name=getattr(args, "model", None),
                    thread_id=thread_id,
                    is_resumed=is_resumed,
                    initial_prompt=getattr(args, "initial_prompt", None),
                )
            )
    except KeyboardInterrupt:
        # Clean exit on Ctrl+C - suppress ugly traceback
        console.print("\n\n[yellow]Interrupted[/yellow]")
        sys.exit(0)


if __name__ == "__main__":
    cli_main()
