"""CLI entry point for SmartTools."""

import argparse
import sys
from pathlib import Path

from . import __version__
from .tool import list_tools, load_tool, save_tool, delete_tool, Tool, ToolArgument, PromptStep, CodeStep
from .ui import run_ui
from .providers import load_providers, add_provider, delete_provider, Provider, call_provider
from .config import load_config, save_config, set_registry_token
from .manifest import (
    load_manifest, save_manifest, create_manifest, find_manifest,
    Manifest, Dependency, MANIFEST_FILENAME
)
from .resolver import (
    resolve_tool, find_tool, install_from_registry, uninstall_tool,
    list_installed_tools, ToolNotFoundError, ToolSpec
)


def cmd_list(args):
    """List all tools."""
    tools = list_tools()

    if not tools:
        print("No tools found.")
        print("Create your first tool with: smarttools ui")
        return 0

    print(f"Available tools ({len(tools)}):\n")
    for name in tools:
        tool = load_tool(name)
        if tool:
            print(f"  {name}")
            print(f"    {tool.description or 'No description'}")

            # Show arguments
            if tool.arguments:
                args_str = ", ".join(arg.flag for arg in tool.arguments)
                print(f"    Arguments: {args_str}")

            # Show steps
            if tool.steps:
                step_info = []
                for step in tool.steps:
                    if isinstance(step, PromptStep):
                        step_info.append(f"PROMPT[{step.provider}]")
                    elif isinstance(step, CodeStep):
                        step_info.append("CODE")
                print(f"    Steps: {' -> '.join(step_info)}")

            print()

    return 0


def cmd_create(args):
    """Create a new tool (basic CLI creation - use 'ui' for full builder)."""
    name = args.name

    # Check if already exists
    existing = load_tool(name)
    if existing and not args.force:
        print(f"Error: Tool '{name}' already exists. Use --force to overwrite.")
        return 1

    # Create a tool with a single prompt step
    steps = []
    if args.prompt:
        steps.append(PromptStep(
            prompt=args.prompt,
            provider=args.provider or "mock",
            output_var="response"
        ))

    tool = Tool(
        name=name,
        description=args.description or "",
        arguments=[],
        steps=steps,
        output="{response}" if steps else "{input}"
    )

    path = save_tool(tool)
    print(f"Created tool '{name}'")
    print(f"Config: {path}")
    print(f"\nUse 'smarttools ui' to add arguments, steps, and customize.")
    print(f"Or run: {name} < input.txt")

    return 0


def cmd_edit(args):
    """Edit a tool (opens in $EDITOR or nano)."""
    import os
    import subprocess

    tool = load_tool(args.name)
    if not tool:
        print(f"Error: Tool '{args.name}' not found.")
        return 1

    from .tool import get_tools_dir
    config_path = get_tools_dir() / args.name / "config.yaml"

    editor = os.environ.get("EDITOR", "nano")

    try:
        subprocess.run([editor, str(config_path)], check=True)
        print(f"Tool '{args.name}' updated.")
        return 0
    except subprocess.CalledProcessError:
        print(f"Error: Editor failed.")
        return 1
    except FileNotFoundError:
        print(f"Error: Editor '{editor}' not found. Set $EDITOR environment variable.")
        return 1


def cmd_delete(args):
    """Delete a tool."""
    if not load_tool(args.name):
        print(f"Error: Tool '{args.name}' not found.")
        return 1

    if not args.force:
        confirm = input(f"Delete tool '{args.name}'? [y/N] ")
        if confirm.lower() != 'y':
            print("Cancelled.")
            return 0

    if delete_tool(args.name):
        print(f"Deleted tool '{args.name}'.")
        return 0
    else:
        print(f"Error: Failed to delete '{args.name}'.")
        return 1


def cmd_test(args):
    """Test a tool with mock provider."""
    tool = load_tool(args.name)
    if not tool:
        print(f"Error: Tool '{args.name}' not found.")
        return 1

    from .runner import run_tool

    # Read test input
    if args.input:
        from pathlib import Path
        input_text = Path(args.input).read_text()
    else:
        print("Enter test input (Ctrl+D to end):")
        input_text = sys.stdin.read()

    print("\n--- Running with mock provider ---\n")

    output, code = run_tool(
        tool=tool,
        input_text=input_text,
        custom_args={},
        provider_override="mock",
        dry_run=args.dry_run,
        show_prompt=True,
        verbose=True
    )

    if output:
        print("\n--- Output ---\n")
        print(output)

    return code


def cmd_run(args):
    """Run a tool."""
    from pathlib import Path
    from .runner import run_tool

    tool = load_tool(args.name)
    if not tool:
        print(f"Error: Tool '{args.name}' not found.", file=sys.stderr)
        return 1

    # Read input
    if args.input:
        # Read from file
        input_path = Path(args.input)
        if not input_path.exists():
            print(f"Error: Input file not found: {args.input}", file=sys.stderr)
            return 1
        input_text = input_path.read_text()
    elif args.stdin:
        # Explicit interactive input requested
        print("Reading from stdin (Ctrl+D to end):", file=sys.stderr)
        input_text = sys.stdin.read()
    elif not sys.stdin.isatty():
        # Stdin is piped - read it
        input_text = sys.stdin.read()
    else:
        # No input provided - use empty string
        input_text = ""

    # Collect custom args from remaining arguments
    custom_args = {}
    if args.tool_args:
        # Parse tool-specific arguments
        i = 0
        while i < len(args.tool_args):
            arg = args.tool_args[i]
            if arg.startswith('--'):
                key = arg[2:].replace('-', '_')
                if i + 1 < len(args.tool_args) and not args.tool_args[i + 1].startswith('--'):
                    custom_args[key] = args.tool_args[i + 1]
                    i += 2
                else:
                    custom_args[key] = True
                    i += 1
            elif arg.startswith('-'):
                key = arg[1:].replace('-', '_')
                if i + 1 < len(args.tool_args) and not args.tool_args[i + 1].startswith('-'):
                    custom_args[key] = args.tool_args[i + 1]
                    i += 2
                else:
                    custom_args[key] = True
                    i += 1
            else:
                i += 1

    # Run tool
    output, code = run_tool(
        tool=tool,
        input_text=input_text,
        custom_args=custom_args,
        provider_override=args.provider,
        dry_run=args.dry_run,
        show_prompt=args.show_prompt,
        verbose=args.verbose
    )

    # Write output
    if code == 0 and output:
        if args.output:
            Path(args.output).write_text(output)
        else:
            print(output)

    return code


def cmd_ui(args):
    """Launch the interactive UI."""
    run_ui()
    return 0


def cmd_refresh(args):
    """Refresh all wrapper scripts with the current Python path."""
    from .tool import list_tools, create_wrapper_script

    tools = list_tools()
    if not tools:
        print("No tools found.")
        return 0

    print(f"Refreshing wrapper scripts for {len(tools)} tools...")
    for name in tools:
        path = create_wrapper_script(name)
        print(f"  {name} -> {path}")

    print("\nDone. Wrapper scripts updated to use current Python interpreter.")
    return 0


def cmd_docs(args):
    """View or edit tool documentation."""
    import os
    import subprocess

    from .tool import get_tools_dir

    tool = load_tool(args.name)
    if not tool:
        print(f"Error: Tool '{args.name}' not found.")
        return 1

    readme_path = get_tools_dir() / args.name / "README.md"

    if args.edit:
        # Edit/create README
        editor = os.environ.get("EDITOR", "nano")

        # Create a template if README doesn't exist
        if not readme_path.exists():
            template = f"""# {args.name}

{tool.description or 'No description provided.'}

## Usage

```bash
echo "input" | {args.name}
```

## Arguments

| Flag | Default | Description |
|------|---------|-------------|
"""
            for arg in tool.arguments:
                template += f"| `{arg.flag}` | {arg.default or ''} | {arg.description or ''} |\n"

            template += """
## Examples

```bash
# Example 1
```

## Requirements

- List any dependencies here
"""
            readme_path.write_text(template)
            print(f"Created template: {readme_path}")

        try:
            subprocess.run([editor, str(readme_path)], check=True)
            print(f"Documentation updated: {readme_path}")
            return 0
        except subprocess.CalledProcessError:
            print("Error: Editor failed.")
            return 1
        except FileNotFoundError:
            print(f"Error: Editor '{editor}' not found. Set $EDITOR environment variable.")
            return 1
    else:
        # View README
        if not readme_path.exists():
            print(f"No documentation found for '{args.name}'.")
            print(f"Create it with: smarttools docs {args.name} --edit")
            return 1

        print(readme_path.read_text())
        return 0


PROVIDER_INSTALL_INFO = {
    "claude": {
        "group": "Anthropic Claude",
        "install_cmd": "npm install -g @anthropic-ai/claude-code",
        "requires": "Node.js 18+ and npm",
        "setup": "Run 'claude' - opens browser for sign-in (auto-saves auth tokens)",
        "cost": "Pay-per-use (billed to your Anthropic account)",
        "variants": ["claude", "claude-haiku", "claude-opus", "claude-sonnet"],
    },
    "codex": {
        "group": "OpenAI Codex",
        "install_cmd": "npm install -g @openai/codex",
        "requires": "Node.js 18+ and npm",
        "setup": "Run 'codex' - opens browser for sign-in (auto-saves auth tokens)",
        "cost": "Pay-per-use (billed to your OpenAI account)",
        "variants": ["codex"],
    },
    "gemini": {
        "group": "Google Gemini",
        "install_cmd": "npm install -g @google/gemini-cli",
        "requires": "Node.js 18+ and npm",
        "setup": "Run 'gemini' - opens browser for Google sign-in",
        "cost": "Free tier available, pay-per-use for more",
        "variants": ["gemini", "gemini-flash"],
    },
    "opencode": {
        "group": "OpenCode (75+ providers)",
        "install_cmd": "curl -fsSL https://opencode.ai/install | bash",
        "requires": "curl, bash",
        "setup": "Run 'opencode' - opens browser to connect more providers",
        "cost": "4 FREE models included (Big Pickle, GLM-4.7, Grok Code Fast 1, MiniMax M2.1), 75+ more available",
        "variants": ["opencode-pickle", "opencode-deepseek", "opencode-nano", "opencode-reasoner", "opencode-grok"],
    },
    "ollama": {
        "group": "Ollama (Local LLMs)",
        "install_cmd": "curl -fsSL https://ollama.ai/install.sh | bash",
        "requires": "curl, bash, 8GB+ RAM (GPU recommended)",
        "setup": "Run 'ollama pull llama3' to download a model, then add provider",
        "cost": "FREE (runs entirely on your machine)",
        "variants": [],
        "custom": True,
        "post_install_note": "After installing, add the provider:\n  smarttools providers add ollama 'ollama run llama3' -d 'Local Llama 3'",
    },
}


def cmd_providers(args):
    """Manage AI providers."""
    import shutil
    import subprocess

    if args.providers_cmd == "install":
        print("=" * 60)
        print("SmartTools Provider Installation Guide")
        print("=" * 60)
        print()

        # Check what's already installed
        providers = load_providers()
        installed_groups = set()
        for p in providers:
            if p.name.lower() == "mock":
                continue
            cmd_parts = p.command.split()[0]
            cmd_expanded = cmd_parts.replace("$HOME", str(Path.home())).replace("~", str(Path.home()))
            if shutil.which(cmd_expanded) or Path(cmd_expanded).exists():
                # Find which group this belongs to
                for group, info in PROVIDER_INSTALL_INFO.items():
                    if p.name in info.get("variants", []):
                        installed_groups.add(group)

        # Show available provider groups
        print("Available AI Provider Groups:\n")
        options = []
        for i, (key, info) in enumerate(PROVIDER_INSTALL_INFO.items(), 1):
            status = "[INSTALLED]" if key in installed_groups else ""
            print(f"  {i}. {info['group']} {status}")
            print(f"     Cost: {info['cost']}")
            print(f"     Models: {', '.join(info['variants']) if info['variants'] else 'Custom'}")
            print()
            options.append(key)

        print("  0. Cancel")
        print()

        try:
            choice = input("Select a provider to install (1-{}, or 0 to cancel): ".format(len(options)))
            choice = int(choice)
        except (ValueError, EOFError):
            print("Cancelled.")
            return 0

        if choice == 0 or choice > len(options):
            print("Cancelled.")
            return 0

        selected = options[choice - 1]
        info = PROVIDER_INSTALL_INFO[selected]

        print()
        print("=" * 60)
        print(f"Installing: {info['group']}")
        print("=" * 60)
        print()
        print(f"Requirements: {info['requires']}")
        print(f"Install command: {info['install_cmd']}")
        print(f"Post-install: {info['setup']}")
        print()

        try:
            confirm = input("Run installation command? (y/N): ").strip().lower()
        except EOFError:
            confirm = "n"

        if confirm == "y":
            print()
            print(f"Running: {info['install_cmd']}")
            print("-" * 40)
            result = subprocess.run(info['install_cmd'], shell=True)
            print("-" * 40)

            if result.returncode == 0:
                # Refresh PATH to pick up newly installed tools
                import os
                new_paths = []

                # Common install locations that might have been added
                potential_paths = [
                    Path.home() / ".opencode" / "bin",  # OpenCode
                    Path.home() / ".local" / "bin",      # pip/pipx installs
                    Path("/usr/local/bin"),              # Ollama, system installs
                ]

                # Also try to get npm global bin path
                try:
                    npm_result = subprocess.run(
                        ["npm", "bin", "-g"],
                        capture_output=True, text=True, timeout=5
                    )
                    if npm_result.returncode == 0:
                        npm_bin = npm_result.stdout.strip()
                        if npm_bin:
                            potential_paths.append(Path(npm_bin))
                except (subprocess.TimeoutExpired, FileNotFoundError):
                    pass

                current_path = os.environ.get("PATH", "")
                for p in potential_paths:
                    if p.exists() and str(p) not in current_path:
                        new_paths.append(str(p))

                if new_paths:
                    os.environ["PATH"] = ":".join(new_paths) + ":" + current_path

                print()
                print("Installation completed!")
                print()
                print("IMPORTANT: Refresh your shell PATH before continuing:")
                print("  source ~/.bashrc")
                print()
                print(f"Next steps:")
                print(f"  1. source ~/.bashrc   (required!)")
                print(f"  2. {info['setup']}")
                if info.get('post_install_note'):
                    print(f"  3. {info['post_install_note']}")
                    print(f"  4. Test with: smarttools providers test {selected}")
                else:
                    print(f"  3. Test with: smarttools providers test {info['variants'][0] if info['variants'] else selected}")
            else:
                print()
                print(f"Installation failed (exit code {result.returncode})")
                print("Try running the command manually:")
                print(f"  {info['install_cmd']}")
        else:
            print()
            print("To install manually, run:")
            print(f"  {info['install_cmd']}")
            print()
            print(f"Then: {info['setup']}")

        return 0

    elif args.providers_cmd == "list":
        providers = load_providers()
        print(f"Configured providers ({len(providers)}):\n")
        for p in providers:
            # Mock provider is always available
            if p.name.lower() == "mock":
                print(f"  [+] {p.name}")
                print(f"      Command: (built-in)")
                print(f"      Status: OK (always available)")
                if p.description:
                    print(f"      Info: {p.description}")
                print()
                continue

            # Check if command exists
            cmd_parts = p.command.split()[0]
            cmd_expanded = cmd_parts.replace("$HOME", str(Path.home())).replace("~", str(Path.home()))
            exists = shutil.which(cmd_expanded) is not None or Path(cmd_expanded).exists()
            status = "OK" if exists else "NOT FOUND"
            status_icon = "+" if exists else "-"

            print(f"  [{status_icon}] {p.name}")
            print(f"      Command: {p.command}")
            print(f"      Status: {status}")
            if p.description:
                print(f"      Info: {p.description}")
            print()
        return 0

    elif args.providers_cmd == "add":
        name = args.name
        command = args.command
        description = args.description or ""

        provider = Provider(name, command, description)
        add_provider(provider)
        print(f"Provider '{name}' added/updated.")
        return 0

    elif args.providers_cmd == "remove":
        name = args.name
        if delete_provider(name):
            print(f"Provider '{name}' removed.")
            return 0
        else:
            print(f"Provider '{name}' not found.")
            return 1

    elif args.providers_cmd == "test":
        name = args.name
        print(f"Testing provider '{name}'...")
        result = call_provider(name, "Say 'hello' and nothing else.", timeout=30)
        if result.success:
            print(f"SUCCESS: {result.text[:200]}...")
        else:
            print(f"FAILED: {result.error}")
        return 0 if result.success else 1

    elif args.providers_cmd == "check":
        providers = load_providers()
        print("Checking all providers...\n")
        available = []
        missing = []

        for p in providers:
            # Mock provider is always available (handled specially)
            if p.name.lower() == "mock":
                available.append(p.name)
                print(f"  [+] {p.name}: OK (built-in)")
                continue

            cmd_parts = p.command.split()[0]
            cmd_expanded = cmd_parts.replace("$HOME", str(Path.home())).replace("~", str(Path.home()))
            exists = shutil.which(cmd_expanded) is not None or Path(cmd_expanded).exists()

            if exists:
                available.append(p.name)
                print(f"  [+] {p.name}: OK")
            else:
                missing.append(p.name)
                print(f"  [-] {p.name}: NOT FOUND ({cmd_parts})")

        print(f"\nSummary: {len(available)} available, {len(missing)} missing")
        if len(available) == 1 and available[0] == "mock":
            print(f"\nNo real AI providers found. Install one of these:")
            print(f"  - claude: npm install -g @anthropic-ai/claude-cli")
            print(f"  - codex: pip install openai-codex")
            print(f"  - gemini: pip install google-generative-ai")
            print(f"\nMeanwhile, use mock provider for testing:")
            print(f"  echo 'test' | summarize --provider mock")
        elif missing:
            print(f"\nAvailable providers: {', '.join(available)}")
        return 0

    return 0


# -------------------------------------------------------------------------
# Registry Commands
# -------------------------------------------------------------------------

def cmd_registry(args):
    """Handle registry subcommands."""
    from .registry_client import (
        RegistryClient, RegistryError, RateLimitError,
        get_client, search, install_tool as registry_install
    )

    if args.registry_cmd == "search":
        try:
            client = get_client()
            results = client.search_tools(
                query=args.query,
                category=args.category,
                per_page=args.limit or 20
            )

            if not results.data:
                print(f"No tools found matching '{args.query}'")
                return 0

            print(f"Found {results.total} tools:\n")
            for tool in results.data:
                owner = tool.get("owner", "")
                name = tool.get("name", "")
                version = tool.get("version", "")
                desc = tool.get("description", "")
                downloads = tool.get("downloads", 0)

                print(f"  {owner}/{name} v{version}")
                print(f"    {desc[:60]}{'...' if len(desc) > 60 else ''}")
                print(f"    Downloads: {downloads}")
                print()

            if results.total_pages > 1:
                print(f"Showing page {results.page}/{results.total_pages}")

        except RegistryError as e:
            if e.code == "CONNECTION_ERROR":
                print("Could not connect to the registry.", file=sys.stderr)
                print("Check your internet connection or try again later.", file=sys.stderr)
            elif e.code == "RATE_LIMITED":
                print(f"Rate limited. Please wait and try again.", file=sys.stderr)
            else:
                print(f"Error: {e.message}", file=sys.stderr)
            return 1
        except Exception as e:
            print(f"Error searching registry: {e}", file=sys.stderr)
            print("If the problem persists, check: smarttools config show", file=sys.stderr)
            return 1

        return 0

    elif args.registry_cmd == "install":
        tool_spec = args.tool
        version = args.version

        print(f"Installing {tool_spec}...")

        try:
            resolved = install_from_registry(tool_spec, version)
            print(f"Installed: {resolved.full_name}@{resolved.version}")
            print(f"Location: {resolved.path}")

            # Show wrapper info
            from .tool import BIN_DIR
            wrapper_name = resolved.tool.name
            if resolved.owner:
                # Check for collision
                short_wrapper = BIN_DIR / resolved.tool.name
                if short_wrapper.exists():
                    wrapper_name = f"{resolved.owner}-{resolved.tool.name}"

            print(f"\nRun with: {wrapper_name}")

        except RegistryError as e:
            if e.code == "TOOL_NOT_FOUND":
                print(f"Tool '{tool_spec}' not found in the registry.", file=sys.stderr)
                print(f"Try: smarttools registry search {tool_spec.split('/')[-1]}", file=sys.stderr)
            elif e.code == "VERSION_NOT_FOUND" or e.code == "CONSTRAINT_UNSATISFIABLE":
                print(f"Error: {e.message}", file=sys.stderr)
                if e.details and "available_versions" in e.details:
                    versions = e.details["available_versions"]
                    print(f"Available versions: {', '.join(versions[:5])}", file=sys.stderr)
                    if e.details.get("latest_stable"):
                        print(f"Latest stable: {e.details['latest_stable']}", file=sys.stderr)
            elif e.code == "CONNECTION_ERROR":
                print("Could not connect to the registry.", file=sys.stderr)
                print("Check your internet connection or try again later.", file=sys.stderr)
            else:
                print(f"Error: {e.message}", file=sys.stderr)
            return 1
        except Exception as e:
            print(f"Error installing tool: {e}", file=sys.stderr)
            return 1

        return 0

    elif args.registry_cmd == "uninstall":
        tool_spec = args.tool

        print(f"Uninstalling {tool_spec}...")

        if uninstall_tool(tool_spec):
            print(f"Uninstalled: {tool_spec}")
        else:
            print(f"Tool '{tool_spec}' not found", file=sys.stderr)
            return 1

        return 0

    elif args.registry_cmd == "info":
        tool_spec = args.tool

        try:
            # Parse the tool spec
            parsed = ToolSpec.parse(tool_spec)
            owner = parsed.owner or "official"

            client = get_client()
            tool_info = client.get_tool(owner, parsed.name)

            print(f"{tool_info.owner}/{tool_info.name} v{tool_info.version}")
            print("=" * 50)
            print(f"Description: {tool_info.description}")
            print(f"Category: {tool_info.category}")
            print(f"Tags: {', '.join(tool_info.tags)}")
            print(f"Downloads: {tool_info.downloads}")
            print(f"Published: {tool_info.published_at}")

            if tool_info.deprecated:
                print()
                print(f"DEPRECATED: {tool_info.deprecated_message}")
                if tool_info.replacement:
                    print(f"Use instead: {tool_info.replacement}")

            # Show versions
            versions = client.get_tool_versions(owner, parsed.name)
            if versions:
                print(f"\nVersions: {', '.join(versions[:5])}")
                if len(versions) > 5:
                    print(f"  ...and {len(versions) - 5} more")

            print(f"\nInstall: smarttools registry install {tool_info.owner}/{tool_info.name}")

        except RegistryError as e:
            if e.code == "TOOL_NOT_FOUND":
                print(f"Tool '{tool_spec}' not found in the registry.", file=sys.stderr)
                print(f"Try: smarttools registry search {parsed.name}", file=sys.stderr)
            elif e.code == "CONNECTION_ERROR":
                print("Could not connect to the registry.", file=sys.stderr)
                print("Check your internet connection or try again later.", file=sys.stderr)
            else:
                print(f"Error: {e.message}", file=sys.stderr)
            return 1
        except Exception as e:
            print(f"Error fetching tool info: {e}", file=sys.stderr)
            return 1

        return 0

    elif args.registry_cmd == "update":
        print("Updating registry index...")

        try:
            client = get_client()
            index = client.get_index(force_refresh=True)

            tool_count = index.get("tool_count", len(index.get("tools", [])))
            generated = index.get("generated_at", "unknown")

            print(f"Index updated: {tool_count} tools")
            print(f"Generated: {generated}")

        except RegistryError as e:
            if e.code == "CONNECTION_ERROR":
                print("Could not connect to the registry.", file=sys.stderr)
                print("Check your internet connection or try again later.", file=sys.stderr)
            elif e.code == "RATE_LIMITED":
                print("Rate limited. Please wait a moment and try again.", file=sys.stderr)
            else:
                print(f"Error: {e.message}", file=sys.stderr)
            return 1
        except Exception as e:
            print(f"Error updating index: {e}", file=sys.stderr)
            return 1

        return 0

    elif args.registry_cmd == "publish":
        # Read tool from current directory or specified path
        tool_path = Path(args.path) if args.path else Path.cwd()

        if tool_path.is_dir():
            config_path = tool_path / "config.yaml"
        else:
            config_path = tool_path
            tool_path = config_path.parent

        if not config_path.exists():
            print(f"Error: config.yaml not found in {tool_path}", file=sys.stderr)
            return 1

        # Read config
        import yaml
        config_yaml = config_path.read_text()

        # Read README if exists
        readme_path = tool_path / "README.md"
        readme = readme_path.read_text() if readme_path.exists() else ""

        # Validate
        try:
            data = yaml.safe_load(config_yaml)
            name = data.get("name", "")
            version = data.get("version", "")
            if not name or not version:
                print("Error: config.yaml must have 'name' and 'version' fields", file=sys.stderr)
                return 1
        except yaml.YAMLError as e:
            print(f"Error: Invalid YAML in config.yaml: {e}", file=sys.stderr)
            return 1

        if args.dry_run:
            print("Dry run - validating only")
            print()
            print(f"Would publish:")
            print(f"  Name: {name}")
            print(f"  Version: {version}")
            print(f"  Config: {len(config_yaml)} bytes")
            print(f"  README: {len(readme)} bytes")
            return 0

        # Check for token
        config = load_config()
        if not config.registry.token:
            print("No registry token configured.")
            print()
            print("1. Register at: https://gitea.brrd.tech/registry/register")
            print("2. Generate a token from your dashboard")
            print("3. Enter your token below")
            print()

            try:
                token = input("Registry token: ").strip()
                if not token:
                    print("Cancelled.")
                    return 1
                set_registry_token(token)
                print("Token saved.")
            except (EOFError, KeyboardInterrupt):
                print("\nCancelled.")
                return 1

        print(f"Publishing {name}@{version}...")

        try:
            client = get_client()
            result = client.publish_tool(config_yaml, readme)

            pr_url = result.get("pr_url", "")
            status = result.get("status", "")

            if status == "published" or result.get("version"):
                print(f"Published: {result.get('owner', '')}/{result.get('name', '')}@{result.get('version', version)}")
            elif pr_url:
                print(f"PR created: {pr_url}")
                print("Your tool is pending review.")
            else:
                print("Published successfully!")

            # Show suggestions if provided (from Phase 6 smart features)
            suggestions = result.get("suggestions", {})
            if suggestions:
                print()

                # Category suggestion
                cat_suggestion = suggestions.get("category")
                if cat_suggestion and cat_suggestion.get("suggested"):
                    confidence = cat_suggestion.get("confidence", 0)
                    print(f"Suggested category: {cat_suggestion['suggested']} ({confidence:.0%} confidence)")

                # Similar tools warning
                similar = suggestions.get("similar_tools", [])
                if similar:
                    print("Similar existing tools:")
                    for tool in similar[:3]:
                        similarity = tool.get("similarity", 0)
                        print(f"  - {tool.get('name', 'unknown')} ({similarity:.0%} similar)")

        except RegistryError as e:
            if e.code == "UNAUTHORIZED":
                print("Authentication failed.", file=sys.stderr)
                print("Your token may have expired. Generate a new one from the registry.", file=sys.stderr)
            elif e.code == "INVALID_CONFIG":
                print(f"Invalid tool config: {e.message}", file=sys.stderr)
                print("Check your config.yaml for errors.", file=sys.stderr)
            elif e.code == "VERSION_EXISTS":
                print(f"Version already exists: {e.message}", file=sys.stderr)
                print("Bump the version in config.yaml and try again.", file=sys.stderr)
            elif e.code == "CONNECTION_ERROR":
                print("Could not connect to the registry.", file=sys.stderr)
                print("Check your internet connection or try again later.", file=sys.stderr)
            elif e.code == "RATE_LIMITED":
                print("Rate limited. Please wait a moment and try again.", file=sys.stderr)
            else:
                print(f"Error: {e.message}", file=sys.stderr)
            return 1
        except Exception as e:
            print(f"Error publishing: {e}", file=sys.stderr)
            return 1

        return 0

    elif args.registry_cmd == "my-tools":
        try:
            client = get_client()
            tools = client.get_my_tools()

            if not tools:
                print("You haven't published any tools yet.")
                print("Publish your first tool with: smarttools registry publish")
                return 0

            print(f"Your published tools ({len(tools)}):\n")
            for tool in tools:
                status = "[DEPRECATED]" if tool.deprecated else ""
                print(f"  {tool.owner}/{tool.name} v{tool.version} {status}")
                print(f"    Downloads: {tool.downloads}")
                print()

        except RegistryError as e:
            if e.code == "UNAUTHORIZED":
                print("Not logged in. Set your registry token first:", file=sys.stderr)
                print("  smarttools config set-token <token>", file=sys.stderr)
                print()
                print("Don't have a token? Register at the registry website.", file=sys.stderr)
            elif e.code == "CONNECTION_ERROR":
                print("Could not connect to the registry.", file=sys.stderr)
                print("Check your internet connection or try again later.", file=sys.stderr)
            elif e.code == "RATE_LIMITED":
                print("Rate limited. Please wait a moment and try again.", file=sys.stderr)
            else:
                print(f"Error: {e.message}", file=sys.stderr)
            return 1
        except Exception as e:
            print(f"Error: {e}", file=sys.stderr)
            return 1

        return 0

    elif args.registry_cmd == "browse":
        # Launch TUI browser
        try:
            from .ui_registry import run_registry_browser
            return run_registry_browser()
        except ImportError:
            print("TUI browser requires urwid. Install with:", file=sys.stderr)
            print("  pip install 'smarttools[tui]'", file=sys.stderr)
            print()
            print("Or search from command line:", file=sys.stderr)
            print("  smarttools registry search <query>", file=sys.stderr)
            return 1

    else:
        # Default: show registry help
        print("Registry commands:")
        print("  search <query>     Search for tools")
        print("  install <tool>     Install a tool")
        print("  uninstall <tool>   Uninstall a tool")
        print("  info <tool>        Show tool information")
        print("  update             Update local index cache")
        print("  publish [path]     Publish a tool")
        print("  my-tools           List your published tools")
        print("  browse             Browse tools (TUI)")
        return 0


# -------------------------------------------------------------------------
# Project Commands
# -------------------------------------------------------------------------

def cmd_deps(args):
    """Show project dependencies from smarttools.yaml."""
    manifest = load_manifest()

    if manifest is None:
        print("No smarttools.yaml found in current project.")
        print("Create one with: smarttools init")
        return 1

    print(f"Project: {manifest.name} v{manifest.version}")
    print()

    if not manifest.dependencies:
        print("No dependencies defined.")
        print("Add one with: smarttools add <owner/name>")
        return 0

    print(f"Dependencies ({len(manifest.dependencies)}):")
    print()

    for dep in manifest.dependencies:
        # Check if installed
        installed = find_tool(dep.name)
        status = "[installed]" if installed else "[not installed]"

        print(f"  {dep.name}")
        print(f"    Version: {dep.version}")
        print(f"    Status: {status}")
        print()

    if manifest.overrides:
        print("Overrides:")
        for name, override in manifest.overrides.items():
            if override.provider:
                print(f"  {name}: provider={override.provider}")

    return 0


def cmd_install_deps(args):
    """Install dependencies from smarttools.yaml."""
    from .registry_client import get_client, RegistryError

    manifest = load_manifest()

    if manifest is None:
        print("No smarttools.yaml found in current project.")
        print("Create one with: smarttools init")
        return 1

    if not manifest.dependencies:
        print("No dependencies to install.")
        return 0

    print(f"Installing dependencies for {manifest.name}...")
    print()

    failed = []
    installed = []

    for i, dep in enumerate(manifest.dependencies, 1):
        print(f"[{i}/{len(manifest.dependencies)}] {dep.name}@{dep.version}")

        # Check if already installed
        existing = find_tool(dep.name)
        if existing:
            print(f"  Already installed: {existing.full_name}")
            installed.append(dep.name)
            continue

        try:
            print(f"  Downloading...")
            resolved = install_from_registry(dep.name, dep.version)
            print(f"  Installed: {resolved.full_name}@{resolved.version}")
            installed.append(dep.name)
        except RegistryError as e:
            if e.code == "TOOL_NOT_FOUND":
                print(f"  Not found in registry")
            elif e.code == "VERSION_NOT_FOUND" or e.code == "CONSTRAINT_UNSATISFIABLE":
                print(f"  Version {dep.version} not available")
            elif e.code == "CONNECTION_ERROR":
                print(f"  Connection failed (check network)")
            else:
                print(f"  Failed: {e.message}")
            failed.append(dep.name)
        except Exception as e:
            print(f"  Failed: {e}")
            failed.append(dep.name)

    print()
    print(f"Installed {len(installed)} tools")

    if failed:
        print(f"Failed: {', '.join(failed)}")
        return 1

    return 0


def cmd_add(args):
    """Add a tool to project dependencies."""
    tool_spec = args.tool
    version = args.version or "*"

    # Find or create manifest
    manifest_path = find_manifest()
    if manifest_path:
        manifest = load_manifest(manifest_path)
    else:
        # Create in current directory
        manifest = create_manifest(name=Path.cwd().name)
        manifest_path = Path.cwd() / MANIFEST_FILENAME

    # Parse tool spec
    parsed = ToolSpec.parse(tool_spec)
    full_name = parsed.full_name

    # Add dependency
    manifest.add_dependency(full_name, version)

    # Save
    save_manifest(manifest, manifest_path)
    print(f"Added {full_name}@{version} to {manifest_path.name}")

    # Install if requested
    if not args.no_install:
        from .registry_client import RegistryError
        print(f"Installing {full_name}...")
        try:
            resolved = install_from_registry(tool_spec, version if version != "*" else None)
            print(f"Installed: {resolved.full_name}@{resolved.version}")
        except RegistryError as e:
            if e.code == "TOOL_NOT_FOUND":
                print(f"Tool not found in registry.", file=sys.stderr)
                print(f"It's been added to your dependencies - you can install it manually later.", file=sys.stderr)
            elif e.code == "CONNECTION_ERROR":
                print(f"Could not connect to registry.", file=sys.stderr)
                print("Run 'smarttools install' to try again later.", file=sys.stderr)
            else:
                print(f"Install failed: {e.message}", file=sys.stderr)
                print("Run 'smarttools install' to try again.", file=sys.stderr)
        except Exception as e:
            print(f"Install failed: {e}", file=sys.stderr)
            print("Run 'smarttools install' to try again.", file=sys.stderr)

    return 0


def cmd_init(args):
    """Initialize a new smarttools.yaml."""
    manifest_path = Path.cwd() / MANIFEST_FILENAME

    if manifest_path.exists() and not args.force:
        print(f"{MANIFEST_FILENAME} already exists. Use --force to overwrite.")
        return 1

    # Get project name
    default_name = Path.cwd().name
    if args.name:
        name = args.name
    else:
        try:
            name = input(f"Project name [{default_name}]: ").strip() or default_name
        except (EOFError, KeyboardInterrupt):
            print()
            name = default_name

    # Get version
    if args.version:
        version = args.version
    else:
        try:
            version = input("Version [1.0.0]: ").strip() or "1.0.0"
        except (EOFError, KeyboardInterrupt):
            print()
            version = "1.0.0"

    # Create manifest
    manifest = create_manifest(name=name, version=version)
    save_manifest(manifest, manifest_path)

    print(f"Created {MANIFEST_FILENAME}")
    print()
    print("Add dependencies with: smarttools add <owner/name>")
    print("Install them with: smarttools install")

    return 0


def cmd_config(args):
    """Manage SmartTools configuration."""
    if args.config_cmd == "show":
        config = load_config()
        print("SmartTools Configuration:")
        print(f"  Registry URL: {config.registry.url}")
        print(f"  Token: {'***' if config.registry.token else '(not set)'}")
        print(f"  Client ID: {config.client_id}")
        print(f"  Auto-fetch: {config.auto_fetch_from_registry}")
        if config.default_provider:
            print(f"  Default provider: {config.default_provider}")
        return 0

    elif args.config_cmd == "set-token":
        token = args.token
        set_registry_token(token)
        print("Registry token saved.")
        return 0

    elif args.config_cmd == "set":
        config = load_config()
        key = args.key
        value = args.value

        if key == "auto_fetch":
            config.auto_fetch_from_registry = value.lower() in ("true", "1", "yes")
        elif key == "default_provider":
            config.default_provider = value if value else None
        elif key == "registry_url":
            config.registry.url = value
        else:
            print(f"Unknown config key: {key}", file=sys.stderr)
            print("Available keys: auto_fetch, default_provider, registry_url")
            return 1

        save_config(config)
        print(f"Set {key} = {value}")
        return 0

    else:
        print("Config commands:")
        print("  show                 Show current configuration")
        print("  set-token <token>    Set registry authentication token")
        print("  set <key> <value>    Set a configuration value")
        return 0


def main():
    """Main CLI entry point."""
    parser = argparse.ArgumentParser(
        prog="smarttools",
        description="A lightweight personal tool builder for AI-powered CLI commands"
    )
    parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")

    subparsers = parser.add_subparsers(dest="command", help="Available commands")

    # No command = launch UI
    # 'list' command
    p_list = subparsers.add_parser("list", help="List all tools")
    p_list.set_defaults(func=cmd_list)

    # 'create' command
    p_create = subparsers.add_parser("create", help="Create a new tool")
    p_create.add_argument("name", help="Tool name")
    p_create.add_argument("-d", "--description", help="Tool description")
    p_create.add_argument("-p", "--prompt", help="Prompt template")
    p_create.add_argument("--provider", help="AI provider (default: mock)")
    p_create.add_argument("-f", "--force", action="store_true", help="Overwrite existing")
    p_create.set_defaults(func=cmd_create)

    # 'edit' command
    p_edit = subparsers.add_parser("edit", help="Edit a tool config")
    p_edit.add_argument("name", help="Tool name")
    p_edit.set_defaults(func=cmd_edit)

    # 'delete' command
    p_delete = subparsers.add_parser("delete", help="Delete a tool")
    p_delete.add_argument("name", help="Tool name")
    p_delete.add_argument("-f", "--force", action="store_true", help="Skip confirmation")
    p_delete.set_defaults(func=cmd_delete)

    # 'test' command
    p_test = subparsers.add_parser("test", help="Test a tool with mock provider")
    p_test.add_argument("name", help="Tool name")
    p_test.add_argument("-i", "--input", help="Input file for testing")
    p_test.add_argument("--dry-run", action="store_true", help="Show prompt only")
    p_test.set_defaults(func=cmd_test)

    # 'run' command
    p_run = subparsers.add_parser("run", help="Run a tool")
    p_run.add_argument("name", help="Tool name")
    p_run.add_argument("-i", "--input", help="Input file (reads from stdin if piped)")
    p_run.add_argument("-o", "--output", help="Output file (writes to stdout if omitted)")
    p_run.add_argument("--stdin", action="store_true", help="Read input interactively (type then Ctrl+D)")
    p_run.add_argument("-p", "--provider", help="Override provider")
    p_run.add_argument("--dry-run", action="store_true", help="Show what would happen without executing")
    p_run.add_argument("--show-prompt", action="store_true", help="Show prompts in addition to output")
    p_run.add_argument("-v", "--verbose", action="store_true", help="Show debug information")
    p_run.add_argument("tool_args", nargs="*", help="Additional tool-specific arguments")
    p_run.set_defaults(func=cmd_run)

    # 'ui' command (explicit)
    p_ui = subparsers.add_parser("ui", help="Launch interactive UI")
    p_ui.set_defaults(func=cmd_ui)

    # 'refresh' command
    p_refresh = subparsers.add_parser("refresh", help="Refresh all wrapper scripts")
    p_refresh.set_defaults(func=cmd_refresh)

    # 'docs' command
    p_docs = subparsers.add_parser("docs", help="View or edit tool documentation")
    p_docs.add_argument("name", help="Tool name")
    p_docs.add_argument("-e", "--edit", action="store_true", help="Edit/create README in $EDITOR")
    p_docs.set_defaults(func=cmd_docs)

    # 'providers' command
    p_providers = subparsers.add_parser("providers", help="Manage AI providers")
    providers_sub = p_providers.add_subparsers(dest="providers_cmd", help="Provider commands")

    # providers list
    p_prov_list = providers_sub.add_parser("list", help="List all providers and their status")
    p_prov_list.set_defaults(func=cmd_providers)

    # providers check
    p_prov_check = providers_sub.add_parser("check", help="Check which providers are available")
    p_prov_check.set_defaults(func=cmd_providers)

    # providers install
    p_prov_install = providers_sub.add_parser("install", help="Interactive guide to install AI providers")
    p_prov_install.set_defaults(func=cmd_providers)

    # providers add
    p_prov_add = providers_sub.add_parser("add", help="Add or update a provider")
    p_prov_add.add_argument("name", help="Provider name")
    p_prov_add.add_argument("command", help="Command to run (e.g., 'claude -p')")
    p_prov_add.add_argument("-d", "--description", help="Provider description")
    p_prov_add.set_defaults(func=cmd_providers)

    # providers remove
    p_prov_remove = providers_sub.add_parser("remove", help="Remove a provider")
    p_prov_remove.add_argument("name", help="Provider name")
    p_prov_remove.set_defaults(func=cmd_providers)

    # providers test
    p_prov_test = providers_sub.add_parser("test", help="Test a provider")
    p_prov_test.add_argument("name", help="Provider name")
    p_prov_test.set_defaults(func=cmd_providers)

    # Default for providers with no subcommand
    p_providers.set_defaults(func=lambda args: cmd_providers(args) if args.providers_cmd else (setattr(args, 'providers_cmd', 'list') or cmd_providers(args)))

    # -------------------------------------------------------------------------
    # Registry Commands
    # -------------------------------------------------------------------------
    p_registry = subparsers.add_parser("registry", help="Registry commands (search, install, publish)")
    registry_sub = p_registry.add_subparsers(dest="registry_cmd", help="Registry commands")

    # registry search
    p_reg_search = registry_sub.add_parser("search", help="Search for tools")
    p_reg_search.add_argument("query", help="Search query")
    p_reg_search.add_argument("-c", "--category", help="Filter by category")
    p_reg_search.add_argument("-l", "--limit", type=int, help="Max results (default: 20)")
    p_reg_search.set_defaults(func=cmd_registry)

    # registry install
    p_reg_install = registry_sub.add_parser("install", help="Install a tool from registry")
    p_reg_install.add_argument("tool", help="Tool to install (owner/name or name)")
    p_reg_install.add_argument("-v", "--version", help="Version constraint")
    p_reg_install.set_defaults(func=cmd_registry)

    # registry uninstall
    p_reg_uninstall = registry_sub.add_parser("uninstall", help="Uninstall a tool")
    p_reg_uninstall.add_argument("tool", help="Tool to uninstall (owner/name)")
    p_reg_uninstall.set_defaults(func=cmd_registry)

    # registry info
    p_reg_info = registry_sub.add_parser("info", help="Show tool information")
    p_reg_info.add_argument("tool", help="Tool name (owner/name)")
    p_reg_info.set_defaults(func=cmd_registry)

    # registry update
    p_reg_update = registry_sub.add_parser("update", help="Update local index cache")
    p_reg_update.set_defaults(func=cmd_registry)

    # registry publish
    p_reg_publish = registry_sub.add_parser("publish", help="Publish a tool to registry")
    p_reg_publish.add_argument("path", nargs="?", help="Path to tool directory (default: current dir)")
    p_reg_publish.add_argument("--dry-run", action="store_true", help="Validate without publishing")
    p_reg_publish.set_defaults(func=cmd_registry)

    # registry my-tools
    p_reg_mytools = registry_sub.add_parser("my-tools", help="List your published tools")
    p_reg_mytools.set_defaults(func=cmd_registry)

    # registry browse
    p_reg_browse = registry_sub.add_parser("browse", help="Browse tools (TUI)")
    p_reg_browse.set_defaults(func=cmd_registry)

    # Default for registry with no subcommand
    p_registry.set_defaults(func=lambda args: cmd_registry(args) if args.registry_cmd else (setattr(args, 'registry_cmd', None) or cmd_registry(args)))

    # -------------------------------------------------------------------------
    # Project Commands
    # -------------------------------------------------------------------------

    # 'deps' command
    p_deps = subparsers.add_parser("deps", help="Show project dependencies")
    p_deps.set_defaults(func=cmd_deps)

    # 'install' command (for dependencies)
    p_install = subparsers.add_parser("install", help="Install dependencies from smarttools.yaml")
    p_install.set_defaults(func=cmd_install_deps)

    # 'add' command
    p_add = subparsers.add_parser("add", help="Add a tool to project dependencies")
    p_add.add_argument("tool", help="Tool to add (owner/name)")
    p_add.add_argument("-v", "--version", help="Version constraint (default: *)")
    p_add.add_argument("--no-install", action="store_true", help="Don't install after adding")
    p_add.set_defaults(func=cmd_add)

    # 'init' command
    p_init = subparsers.add_parser("init", help="Initialize smarttools.yaml")
    p_init.add_argument("-n", "--name", help="Project name")
    p_init.add_argument("-v", "--version", help="Project version")
    p_init.add_argument("-f", "--force", action="store_true", help="Overwrite existing")
    p_init.set_defaults(func=cmd_init)

    # -------------------------------------------------------------------------
    # Config Commands
    # -------------------------------------------------------------------------
    p_config = subparsers.add_parser("config", help="Manage configuration")
    config_sub = p_config.add_subparsers(dest="config_cmd", help="Config commands")

    # config show
    p_cfg_show = config_sub.add_parser("show", help="Show current configuration")
    p_cfg_show.set_defaults(func=cmd_config)

    # config set-token
    p_cfg_token = config_sub.add_parser("set-token", help="Set registry authentication token")
    p_cfg_token.add_argument("token", help="Registry token")
    p_cfg_token.set_defaults(func=cmd_config)

    # config set
    p_cfg_set = config_sub.add_parser("set", help="Set a configuration value")
    p_cfg_set.add_argument("key", help="Config key")
    p_cfg_set.add_argument("value", help="Config value")
    p_cfg_set.set_defaults(func=cmd_config)

    # Default for config with no subcommand
    p_config.set_defaults(func=lambda args: cmd_config(args) if args.config_cmd else (setattr(args, 'config_cmd', 'show') or cmd_config(args)))

    args = parser.parse_args()

    # If no command, launch UI
    if args.command is None:
        return cmd_ui(args)

    return args.func(args)


if __name__ == "__main__":
    sys.exit(main())
