"""Interactive chat CLI — manage proxym through natural language.

Usage:
    proxym chat              # keyboard input (interactive)
    proxym chat --voice      # microphone input (Whisper STT)
    proxym chat --tts        # + text-to-speech responses
    proxym chat "show my VMs"  # One-shot question
    proxym chat "create ticket for bug" --cli-mode  # Generate CLI command
    proxym chat "start aider on project X" --cli-mode --execute  # Generate and execute
"""

from __future__ import annotations

import json
import re
import shlex
import subprocess
import sys
from pathlib import Path
from typing import Any

import httpx
import structlog

from proxym.cli.dsl import DSLParser
from proxym.config import get_settings

_ANSI_RE = re.compile(r"\033\[[0-9;]*m")

logger = structlog.get_logger()

# ANSI colors
CYAN = "\033[36m"
GREEN = "\033[32m"
YELLOW = "\033[33m"
RED = "\033[31m"
BOLD = "\033[1m"
RESET = "\033[0m"


def _get_config_path() -> Path:
    """Get the path to the user config file."""
    return Path.home() / ".config" / "proxym" / "config.json"


def _load_config() -> dict:
    """Load user config from file."""
    config_path = _get_config_path()
    if config_path.exists():
        try:
            return json.loads(config_path.read_text())
        except (json.JSONDecodeError, IOError):
            pass
    return {}


def _save_config(config: dict) -> None:
    """Save user config to file."""
    config_path = _get_config_path()
    config_path.parent.mkdir(parents=True, exist_ok=True)
    config_path.write_text(json.dumps(config, indent=2) + "\n")


def _test_api_key(api_key: str, proxy_url: str) -> bool:
    """Test if API key is valid by making a simple request."""
    try:
        r = httpx.get(
            f"{proxy_url}/v1/models",
            headers={"Authorization": f"Bearer {api_key}"},
            timeout=10.0,
        )
        return r.status_code == 200
    except Exception:
        return False


def _interactive_api_key_setup(proxy_url: str) -> str | None:
    """Interactively prompt for and validate API key.

    Returns the valid API key or None if user cancelled.
    """
    print(f"\n{BOLD}🔑 API Key Setup{RESET}")
    print(f"{YELLOW}An API key is needed to use the LLM features.{RESET}")
    print(f"You can get one from: https://aistudio.google.com/app/apikey")
    print(f"Or from your OpenAI/Anthropic dashboard.\n")

    # Check if there's already a key in env/config
    config = _load_config()
    existing_key = config.get("api_key", "")

    if existing_key and len(existing_key) > 10:
        print(f"Found existing API key in config ({len(existing_key)} chars)")
        test = input(f"Test this key? [Y/n]: ").strip().lower()
        if test != 'n':
            if _test_api_key(existing_key, proxy_url):
                print(f"{GREEN}✓ Existing API key is valid!{RESET}\n")
                return existing_key
            else:
                print(f"{YELLOW}⚠ Existing key is invalid. Let's set a new one.{RESET}\n")

    # Prompt for new key
    print(f"Enter your API key (starts with 'sk-', 'AIza', or similar):")
    api_key = input(f"{CYAN}API Key: {RESET}").strip()

    if not api_key:
        print(f"{YELLOW}No key entered. Skipping setup.{RESET}\n")
        return None

    if len(api_key) < 10:
        print(f"{YELLOW}Key too short. Please provide a valid API key.{RESET}\n")
        return None

    # Test the key
    print(f"\nTesting API key...")
    if _test_api_key(api_key, proxy_url):
        print(f"{GREEN}✓ API key is valid!{RESET}")

        # Save to config
        save = input(f"\nSave this key for future use? [Y/n]: ").strip().lower()
        if save != 'n':
            config["api_key"] = api_key
            _save_config(config)
            print(f"{GREEN}✓ Saved to {_get_config_path()}{RESET}\n")
        else:
            print(f"{YELLOW}Key not saved (will be used for this session only){RESET}\n")

        return api_key
    else:
        print(f"{RED}✗ API key test failed. The key may be invalid or the server is unreachable.{RESET}\n")
        retry = input(f"Try a different key? [y/N]: ").strip().lower()
        if retry == 'y':
            return _interactive_api_key_setup(proxy_url)
        return None


_FORMATTERS: dict[str, Any] = {}  # populated below


def _fmt_status(data: dict) -> str:
    parts = []
    if "vms" in data:
        parts.append(f"VMs: {data['vms'].get('count', '?')}")
    if "accounts" in data:
        parts.append(f"Accounts: {data['accounts'].get('count', '?')}")
    if "costs" in data and isinstance(data["costs"], dict):
        parts.append(f"Today: ${data['costs'].get('daily_total_usd', 0):.2f}")
    return ("✅ " + " | ".join(parts)) if parts else json.dumps(data, indent=2)


def _fmt_vms(data: dict) -> str:
    vms = data.get("vms", data.get("data", []))
    if not vms:
        return "No VMs found."
    lines = []
    for vm in vms:
        if isinstance(vm, dict):
            vid = vm.get("id", vm.get("vm_id", "?"))
            state = vm.get("state", "?")
            tool = vm.get("tool", "?")
            project = vm.get("project", vm.get("code_mount", "?"))
            lines.append(f"  {vid:<20} │ {state:<10} │ {tool:<10} │ {project}")
    return "\n".join(lines) if lines else json.dumps(data, indent=2)


def _fmt_costs(data: dict) -> str:
    daily = data.get("daily_total_usd", data.get("today_usd", 0))
    monthly = data.get("monthly_total_usd", data.get("month_usd", 0))
    return f"Today: ${daily:.2f} | This month: ${monthly:.2f}"


def _fmt_vm_action(data: dict) -> str:
    status = data.get("status", "done")
    vm_id = data.get("vm_id", "")
    return f"✅ {status}" + (f" ({vm_id})" if vm_id else "")


_FORMATTERS = {
    "status": _fmt_status,
    "vms": _fmt_vms,
    "costs": _fmt_costs,
    "vm_start": _fmt_vm_action,
    "vm_stop": _fmt_vm_action,
    "vm_create": _fmt_vm_action,
    "vm_snapshot": _fmt_vm_action,
}


def _format_result(data: dict[str, Any], pattern_name: str) -> str:
    """Format API response for human-readable chat output."""
    if "error" in data:
        return f"\033[31m✗ {data['error']}\033[0m"
    fmt = _FORMATTERS.get(pattern_name)
    if fmt:
        return fmt(data)
    return json.dumps(data, indent=2, ensure_ascii=False)


def _ask_proxym_llm(text: str, proxy_url: str, api_key: str, interactive: bool = True) -> str:
    """Forward a question to the LLM via proxym with MCP tools."""
    from proxym.mcp.self_server import TOOL_SCHEMA

    # Check if API key is valid (basic check)
    is_key_valid = api_key and api_key != "your-api-key-here" and len(api_key) >= 10

    # Try interactive setup if key is missing and we're in interactive mode
    if not is_key_valid and interactive:
        new_key = _interactive_api_key_setup(proxy_url)
        if new_key:
            api_key = new_key
            is_key_valid = True
        else:
            return (
                "\033[33m⚠ LLM not available: API key is required.\033[0m\n"
                "Run 'proxym chat' and try again, or set API_KEY in your .env file."
            )

    if not is_key_valid:
        return (
            "\033[33m⚠ LLM not available: Invalid or missing API key.\033[0m\n"
            "Set a valid API key in your .env file or use --key flag."
        )

    try:
        r = httpx.post(
            f"{proxy_url}/v1/chat/completions",
            headers={"Authorization": f"Bearer {api_key}"},
            json={
                "model": "cheap",
                "messages": [
                    {
                        "role": "system",
                        "content": (
                            "Jesteś asystentem zarządzającym proxym — lokalnym proxy AI. "
                            "Używaj dostępnych narzędzi aby odpowiadać na pytania. "
                            "Odpowiadaj zwięźle po polsku."
                        ),
                    },
                    {"role": "user", "content": text},
                ],
                "tools": TOOL_SCHEMA,
                "tool_choice": "auto",
            },
            timeout=30.0,
        )

        # Handle HTTP errors
        if r.status_code == 401:
            return "\033[33m⚠ Authentication error: Invalid API key for LLM.\033[0m"
        if r.status_code == 429:
            return "\033[33m⚠ Rate limit exceeded. Please try again later.\033[0m"
        if r.status_code >= 500:
            return f"\033[33m⚠ LLM service error ({r.status_code}). Please try again later.\033[0m"

        data = r.json()

        # Handle LiteLLM/Gemini errors
        if "error" in data:
            error_detail = data.get("error", {})
            if isinstance(error_detail, dict):
                message = error_detail.get("message", str(error_detail))
                if "API key not valid" in message:
                    return "\033[33m⚠ LLM authentication failed: Invalid API key.\033[0m"
                return f"\033[33m⚠ LLM error: {message[:100]}\033[0m"
            return f"\033[33m⚠ LLM error: {str(error_detail)[:100]}\033[0m"

        choices = data.get("choices", [])
        if choices:
            return choices[0].get("message", {}).get("content", "(no response)")
        return json.dumps(data, indent=2, ensure_ascii=False)

    except httpx.ConnectError:
        return "\033[31m✗ Cannot connect to proxym server\033[0m"
    except httpx.TimeoutException:
        return "\033[33m⚠ LLM request timed out. Please try again.\033[0m"
    except Exception as exc:
        return f"\033[33m⚠ LLM error: {exc}\033[0m"


def _generate_cli_command(user_request: str, proxy_url: str, api_key: str, interactive: bool = True) -> dict[str, Any]:
    """Generate CLI command from natural language request using DSL first, then LLM."""
    from proxym.cli._cli_schema import get_cli_schema_text
    from proxym.cli.dsl import DSLParser

    # ── STEP 1: Try DSL pattern matching first (no LLM needed) ──────────────────
    dsl = DSLParser()
    dsl_cmd = dsl.parse(user_request)

    if dsl_cmd and dsl_cmd.action_type == "command":
        # Build the equivalent CLI command from DSL match
        if "ticket" in dsl_cmd.raw_pattern and "create" in dsl_cmd.raw_pattern:
            task = dsl_cmd.params.get("task", "")
            project = dsl_cmd.params.get("project", "default")
            ticket_type = dsl_cmd.params.get("type", "task")
            priority = dsl_cmd.params.get("priority", "medium")

            cmd_str = f'proxym ticket add "{task}" -p {project} -t aider --priority {priority} --type {ticket_type}'
            return {
                "command": cmd_str,
                "explanation": f"Creates a {priority} priority {ticket_type} ticket: {task}",
                "confidence": "high",
                "alternatives": [
                    f'proxym ticket add "{task}" -p {project}',
                    "proxym ticket list"
                ],
                "dangerous": False
            }

        elif dsl_cmd.raw_pattern == "project_register":
            name = dsl_cmd.params.get("name", "")
            path = dsl_cmd.params.get("path", "")
            tool = dsl_cmd.params.get("tool", "aider")
            cmd_str = f'proxym project add {path} --name {name}'
            if tool and tool != "aider":
                cmd_str += f' --default-tool {tool}'
            return {
                "command": cmd_str,
                "explanation": f"Registers project '{name}' at {path}" + (f" with tool {tool}" if tool else ""),
                "confidence": "high",
                "alternatives": ["proxym project list"],
                "dangerous": False
            }

        elif dsl_cmd.raw_pattern == "project_remove":
            name = dsl_cmd.params.get("project_name", "")
            return {
                "command": f'proxym project remove {name}',
                "explanation": f"Removes project '{name}'",
                "confidence": "high",
                "alternatives": ["proxym project list"],
                "dangerous": True
            }

        elif dsl_cmd.raw_pattern == "sprint_create":
            name = dsl_cmd.params.get("name", "")
            goal = dsl_cmd.params.get("goal", "")
            cmd_str = f'proxym sprint create --name "{name}"'
            if goal:
                cmd_str += f' --goal "{goal}"'
            return {
                "command": cmd_str,
                "explanation": f"Creates sprint '{name}'" + (f" with goal: {goal}" if goal else ""),
                "confidence": "high",
                "alternatives": ["proxym sprint list"],
                "dangerous": False
            }

        elif dsl_cmd.raw_pattern == "sprint_start":
            name = dsl_cmd.params.get("sprint_name", "")
            return {
                "command": f'proxym sprint start "{name}"',
                "explanation": f"Starts sprint '{name}'",
                "confidence": "high",
                "alternatives": ["proxym sprint list"],
                "dangerous": False
            }

        elif dsl_cmd.raw_pattern == "sprint_close":
            name = dsl_cmd.params.get("sprint_name", "")
            return {
                "command": f'proxym sprint close "{name}"',
                "explanation": f"Closes sprint '{name}'",
                "confidence": "high",
                "alternatives": ["proxym sprint list"],
                "dangerous": False
            }

        elif dsl_cmd.raw_pattern == "sprint_add_ticket":
            ticket_id = dsl_cmd.params.get("ticket_id", "")
            sprint_name = dsl_cmd.params.get("sprint_name", "")
            return {
                "command": f'proxym sprint add-ticket "{ticket_id}" --sprint "{sprint_name}"',
                "explanation": f"Adds ticket {ticket_id} to sprint {sprint_name}",
                "confidence": "high",
                "alternatives": ["proxym sprint list"],
                "dangerous": False
            }

        elif dsl_cmd.raw_pattern == "ticket_assign":
            ticket_id = dsl_cmd.params.get("ticket_id", "")
            tool = dsl_cmd.params.get("tool", "aider")
            model = dsl_cmd.params.get("model", "smart")
            if ticket_id.lower() == "all":
                return {
                    "command": None,
                    "explanation": "Dispatch 'all' requires project context. Use 'dispatch all tickets for <project>' instead.",
                    "confidence": "low",
                    "alternatives": ["proxym tools dispatch --all -p <project>"],
                    "dangerous": False
                }
            cmd_str = f'proxym dispatch {ticket_id}'
            if tool:
                cmd_str += f' --tool {tool}'
            if model and model != "smart":
                cmd_str += f' --model {model}'
            return {
                "command": cmd_str,
                "explanation": f"Dispatches ticket {ticket_id}" + (f" using {tool}" if tool else ""),
                "confidence": "high",
                "alternatives": ["proxym ticket list"],
                "dangerous": False
            }

        elif dsl_cmd.raw_pattern == "batch_tickets":
            project = dsl_cmd.params.get("project", "")
            return {
                "command": f'proxym tools dispatch --all -p {project}',
                "explanation": f"Dispatches all tickets for project '{project}'",
                "confidence": "high",
                "alternatives": ["proxym ticket list -p {project}"],
                "dangerous": False
            }

        elif dsl_cmd.raw_pattern == "ticket_close":
            ticket_id = dsl_cmd.params.get("ticket_id", "")
            return {
                "command": f'proxym ticket close {ticket_id}',
                "explanation": f"Closes ticket {ticket_id}",
                "confidence": "high",
                "alternatives": ["proxym ticket list"],
                "dangerous": False
            }

        elif dsl_cmd.raw_pattern == "ticket_update":
            ticket_id = dsl_cmd.params.get("ticket_id", "")
            priority = dsl_cmd.params.get("priority", "")
            return {
                "command": f'proxym ticket update {ticket_id} --priority {priority}',
                "explanation": f"Updates ticket {ticket_id} priority to {priority}",
                "confidence": "high",
                "alternatives": ["proxym ticket show {ticket_id}"],
                "dangerous": False
            }

        elif dsl_cmd.raw_pattern == "run_workflow":
            task = dsl_cmd.params.get("task", "")
            project = dsl_cmd.params.get("project", "")
            return {
                "command": f'proxym run "{task}" --on {project}',
                "explanation": f"Runs task '{task}' on project '{project}'",
                "confidence": "high",
                "alternatives": ["proxym ticket list -p {project}"],
                "dangerous": False
            }

        elif dsl_cmd.raw_pattern == "vm_create":
            tool = dsl_cmd.params.get("tool", "")
            project = dsl_cmd.params.get("project", "")
            return {
                "command": f'proxym vm create --tool {tool} --project {project}',
                "explanation": f"Creates VM with {tool} for project {project}",
                "confidence": "high",
                "alternatives": ["proxym vm list"],
                "dangerous": False
            }

        elif dsl_cmd.raw_pattern in ("vm_start", "vm_stop", "vm_restart", "vm_delete"):
            vm_id = dsl_cmd.params.get("vm_id", "")
            action = dsl_cmd.raw_pattern.replace("vm_", "")
            return {
                "command": f'proxym vm {action} {vm_id}',
                "explanation": f"{action.capitalize()}s VM {vm_id}",
                "confidence": "high",
                "alternatives": ["proxym vm list"],
                "dangerous": action in ("delete", "stop")
            }

        elif dsl_cmd.raw_pattern == "vm_snapshot":
            vm_id = dsl_cmd.params.get("vm_id", "")
            name = dsl_cmd.params.get("name", "")
            cmd_str = f'proxym vm snapshot {vm_id}'
            if name:
                cmd_str += f' --name "{name}"'
            return {
                "command": cmd_str,
                "explanation": f"Creates snapshot of VM {vm_id}" + (f" named '{name}'" if name else ""),
                "confidence": "high",
                "alternatives": ["proxym vm list"],
                "dangerous": False
            }

        elif dsl_cmd.raw_pattern == "vm_restore":
            vm_id = dsl_cmd.params.get("vm_id", "")
            name = dsl_cmd.params.get("name", "")
            return {
                "command": f'proxym vm restore {vm_id} --snapshot "{name}"',
                "explanation": f"Restores VM {vm_id} from snapshot '{name}'",
                "confidence": "high",
                "alternatives": ["proxym vm list"],
                "dangerous": True
            }

        elif dsl_cmd.raw_pattern == "switch_project":
            vm_id = dsl_cmd.params.get("vm_id", "")
            project = dsl_cmd.params.get("project", "")
            return {
                "command": f'proxym vm switch-project {vm_id} --project {project}',
                "explanation": f"Switches VM {vm_id} to project {project}",
                "confidence": "high",
                "alternatives": ["proxym vm list"],
                "dangerous": False
            }

        elif dsl_cmd.raw_pattern == "add_account":
            provider = dsl_cmd.params.get("provider", "")
            return {
                "command": f'proxym accounts add --provider {provider}',
                "explanation": f"Adds {provider} account (interactive prompt for key)",
                "confidence": "high",
                "alternatives": ["proxym accounts list"],
                "dangerous": False
            }

    # ── STEP 2: Fall back to LLM if no DSL pattern matched ─────────────────────

    # Check if API key is valid
    is_key_valid = api_key and api_key != "your-api-key-here" and len(api_key) >= 10

    # Try interactive setup if key is missing and we're in interactive mode
    if not is_key_valid and interactive:
        new_key = _interactive_api_key_setup(proxy_url)
        if new_key:
            api_key = new_key
            is_key_valid = True
        else:
            return {
                "command": None,
                "explanation": "API key is required for CLI generation. Run 'proxym chat' interactively to set up.",
                "confidence": "low",
                "alternatives": [
                    "proxym ticket list",
                    "proxym project list",
                    "proxym board",
                    "proxym status"
                ],
                "dangerous": False,
                "error": "missing_api_key"
            }

    if not is_key_valid:
        return {
            "command": None,
            "explanation": "LLM not available: Invalid or missing API key. Set a valid API key in .env file or run interactively.",
            "confidence": "low",
            "alternatives": [
                "proxym ticket list",
                "proxym project list",
                "proxym board",
                "proxym status"
            ],
            "dangerous": False,
            "error": "missing_api_key"
        }

    cli_schema_text = get_cli_schema_text()

    prompt = f"""Based on the following CLI schema, generate a proxym CLI command for this request:

User Request: {user_request}

{cli_schema_text}

Return ONLY a JSON object with these fields:
- "command": The complete proxym command (e.g., "proxym vm list")
- "explanation": Brief explanation of what the command does
- "confidence": "high" | "medium" | "low" - how well the request matches available commands
- "alternatives": Array of alternative commands if confidence is medium/low
- "dangerous": Boolean - true if command modifies/deletes data

Example response:
{{"command": "proxym vm list", "explanation": "Lists all VMs", "confidence": "high", "alternatives": [], "dangerous": false}}

Response:"""

    try:
        r = httpx.post(
            f"{proxy_url}/v1/chat/completions",
            headers={"Authorization": f"Bearer {api_key}"},
            json={
                "model": "smart",
                "messages": [
                    {
                        "role": "system",
                        "content": "You are a CLI command generator. Return ONLY valid JSON."
                    },
                    {"role": "user", "content": prompt}
                ],
                "temperature": 0.3,
            },
            timeout=30.0,
        )

        # Handle HTTP errors
        if r.status_code == 401:
            return {
                "command": None,
                "explanation": "Cannot generate command: Invalid API key for LLM.",
                "confidence": "low",
                "alternatives": [
                    "proxym ticket add \"<description>\" -p <project> -t <tool>",
                    "proxym ticket list",
                    "proxym project list",
                    "proxym board"
                ],
                "dangerous": False,
                "error": "auth_error"
            }

        if r.status_code >= 500:
            return {
                "command": None,
                "explanation": f"LLM service unavailable (status {r.status_code}). Try again later.",
                "confidence": "low",
                "alternatives": [
                    "proxym ticket add \"<description>\" -p <project> -t <tool>",
                    "proxym ticket list",
                    "proxym project list",
                    "proxym board"
                ],
                "dangerous": False,
                "error": "service_error"
            }

        data = r.json()

        # Handle API errors
        if "error" in data:
            error_detail = data.get("error", {})
            if isinstance(error_detail, dict):
                message = error_detail.get("message", str(error_detail))
                return {
                    "command": None,
                    "explanation": f"LLM error: {message[:100]}",
                    "confidence": "low",
                    "alternatives": [
                        "proxym ticket add \"<description>\" -p <project> -t <tool>",
                        "proxym ticket list",
                        "proxym project list",
                        "proxym board"
                    ],
                    "dangerous": False,
                    "error": "llm_error"
                }

        content = data.get("choices", [{}])[0].get("message", {}).get("content", "")

        # Extract JSON from response
        if content.strip():
            # Try to find JSON in the response
            json_match = re.search(r'\{[\s\S]*?\}', content)
            if json_match:
                try:
                    result = json.loads(json_match.group())
                    # Ensure all required fields exist
                    return {
                        "command": result.get("command"),
                        "explanation": result.get("explanation", ""),
                        "confidence": result.get("confidence", "low"),
                        "alternatives": result.get("alternatives", []),
                        "dangerous": result.get("dangerous", False)
                    }
                except json.JSONDecodeError:
                    pass

        # Fallback: return raw content as explanation
        return {
            "command": None,
            "explanation": content[:200] if content else "Could not parse LLM response",
            "confidence": "low",
            "alternatives": [],
            "dangerous": False
        }

    except httpx.TimeoutException:
        return {
            "command": None,
            "explanation": "Request timed out. LLM service may be slow or unavailable.",
            "confidence": "low",
            "alternatives": [
                "proxym ticket add \"<description>\" -p <project> -t <tool>",
                "proxym ticket list",
                "proxym project list",
                "proxym board"
            ],
            "dangerous": False,
            "error": "timeout"
        }

    except Exception as exc:
        return {
            "command": None,
            "explanation": f"Error generating command: {exc}",
            "confidence": "low",
            "alternatives": [
                "proxym ticket add \"<description>\" -p <project> -t <tool>",
                "proxym ticket list",
                "proxym project list",
                "proxym board"
            ],
            "dangerous": False,
            "error": "exception"
        }


def _execute_cli_command(command: str) -> tuple[bool, str]:
    """Execute a CLI command and return (success, output)."""
    try:
        # Remove 'proxym ' prefix if present
        if command.startswith("proxym "):
            command = command[7:]

        result = subprocess.run(
            ["proxym"] + shlex.split(command),
            capture_output=True,
            text=True,
            timeout=60
        )

        if result.returncode == 0:
            return True, result.stdout
        else:
            return False, result.stderr

    except subprocess.TimeoutExpired:
        return False, "Command timed out"
    except Exception as exc:
        return False, f"Execution error: {exc}"


def _print_cli_generation_result(result: dict[str, Any]) -> None:
    """Print CLI generation result in a formatted way."""
    command = result.get("command")
    confidence = result.get("confidence", "low")
    dangerous = result.get("dangerous", False)

    # Confidence indicator
    if confidence == "high":
        icon = "\033[32m✓\033[0m"
    elif confidence == "medium":
        icon = "\033[33m?\033[0m"
    else:
        icon = "\033[31m!\033[0m"

    print(f"\n{icon} \033[1mGenerated Command:\033[0m")

    if command:
        # Highlight the command
        print(f"  \033[36m{command}\033[0m")
    else:
        print("  \033[33m(No command could be generated)\033[0m")

    # Explanation
    if result.get("explanation"):
        print(f"\n  {result['explanation']}")

    # Danger warning
    if dangerous and command:
        print(f"\n  \033[31m⚠ Warning: This command may modify or delete data!\033[0m")

    # Alternatives
    alternatives = result.get("alternatives", [])
    if alternatives:
        print(f"\n  \033[33mAlternative commands:\033[0m")
        for alt in alternatives[:3]:
            print(f"    • {alt}")

    print()


def _get_input(voice) -> str | None:
    """Get user input from voice or keyboard. Returns None on empty."""
    if voice:
        text = voice.listen_once()
        if text:
            print(f"\033[36mYou:\033[0m {text}")
        return text or None
    text = input("\033[36mYou:\033[0m ").strip()
    return text or None


def _process_input(text: str, dsl: DSLParser, proxy_url: str, api_key: str, interactive: bool = True) -> str:
    """Route text through DSL or LLM and return formatted response."""
    cmd = dsl.parse(text)
    if cmd:
        result = dsl.execute(cmd)
        return _format_result(result, cmd.raw_pattern)
    return _ask_proxym_llm(text, proxy_url, api_key, interactive=interactive)


def _init_voice(args):
    """Initialize voice listener if requested."""
    if not getattr(args, "voice", False):
        return None
    try:
        from proxym.cli.voice import VoiceListener
        return VoiceListener()
    except ImportError as exc:
        print(f"\033[31mVoice not available: {exc}\033[0m")
        sys.exit(1)


def _init_tts(args):
    """Initialize TTS engine if requested."""
    if not getattr(args, "tts", False):
        return None
    from proxym.cli.tts import TTSEngine
    return TTSEngine()


def cmd_chat(args) -> None:
    """Interactive chat with proxym — DSL first, LLM fallback, CLI generation."""
    settings = get_settings()
    proxy_url = getattr(args, "proxy", settings.default_proxy_url)
    api_key = getattr(args, "key", settings.default_api_key)

    # Try to load API key from config if not provided via args/env
    if not api_key or api_key == "your-api-key-here":
        config = _load_config()
        api_key = config.get("api_key", api_key)

    cli_mode = getattr(args, "cli_mode", False)
    execute = getattr(args, "execute", False)
    prompt = getattr(args, "prompt", None)

    # One-shot mode: prompt provided as argument
    if prompt:
        if cli_mode:
            # CLI generation mode - non-interactive (no prompts during one-shot)
            print(f"\033[36mGenerating CLI command for:\033[0m {prompt}")
            result = _generate_cli_command(prompt, proxy_url, api_key, interactive=False)
            _print_cli_generation_result(result)

            # Execute if requested and confidence is high enough
            if execute and result.get("command"):
                if result.get("confidence") in ["high", "medium"]:
                    if result.get("dangerous"):
                        confirm = input("\033[33mExecute this command? [y/N]: \033[0m").strip().lower()
                        if confirm != 'y':
                            print("Cancelled.")
                            return

                    print(f"\033[36mExecuting:\033[0m {result['command']}")
                    success, output = _execute_cli_command(result['command'])
                    if success:
                        print(f"\033[32m✓ Output:\033[0m\n{output}")
                    else:
                        print(f"\033[31m✗ Error:\033[0m\n{output}")
                else:
                    print("\033[33mCommand not executed due to low confidence. Review and run manually.\033[0m")
        else:
            # Regular chat mode - interactive (can prompt for API key)
            dsl = DSLParser()
            response = _process_input(prompt, dsl, proxy_url, api_key)
            print(f"\033[32mproxym:\033[0m {response}")
        return

    # Interactive mode
    dsl = DSLParser()
    voice = _init_voice(args)
    tts = _init_tts(args)

    mode = "voice" if voice else "text"
    print(f"\033[1mproxym chat\033[0m — {mode} mode (Ctrl+C to exit)")
    print("Type 'help' for available DSL commands.")
    if cli_mode:
        print("Type 'cli: <request>' to generate CLI commands.")
    print()

    while True:
        try:
            text = _get_input(voice)
            if not text:
                continue
            if text.lower() in ("exit", "quit", "bye"):
                print("Do widzenia!")
                break
            if text.lower() == "help":
                _print_help(dsl)
                continue

            # CLI generation shortcut in interactive mode
            if cli_mode and text.lower().startswith("cli:"):
                request = text[4:].strip()
                print(f"\033[36mGenerating CLI command...\033[0m")
                result = _generate_cli_command(request, proxy_url, api_key, interactive=True)
                _print_cli_generation_result(result)
                continue

            response = _process_input(text, dsl, proxy_url, api_key)
            print(f"\033[32mproxym:\033[0m {response}")

            if tts:
                tts.speak(_ANSI_RE.sub("", response))

        except KeyboardInterrupt:
            print("\nDo widzenia!")
            break
        except EOFError:
            break


def _print_help(dsl: DSLParser) -> None:
    """Print available DSL patterns."""
    print("\n\033[1mAvailable commands (DSL — no LLM needed):\033[0m\n")
    for entry in dsl.list_patterns():
        ptype = "📖" if entry["type"] == "query" else "⚡"
        print(f"  {ptype} \033[1m{entry['name']}\033[0m")
        for p in entry["patterns"][:3]:
            print(f"     {p}")
        print()
    print("Anything else is forwarded to LLM with proxym tools.")
    print("Use 'cli: <request>' to generate CLI commands.\n")
