# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# ORCID: 0009-0006-4957-0540
# Email: Georgescottfoley@proton.me
# Licensed under the MIT License - see LICENSE file for details

"""
Familiar Plugin Marketplace

A simple plugin system for discovering, installing, and managing skills.

Features:
- Browse community plugins from a registry
- Install plugins from GitHub or local paths
- Version management and updates
- Dependency resolution
- Plugin ratings and reviews (via registry)

The registry is a JSON file (local or remote) listing available plugins.
Plugins are Git repos with a standard structure.
"""

import json
import logging
import shutil
import subprocess
import urllib.error
import urllib.request
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional

logger = logging.getLogger(__name__)

# Paths - use centralized paths


def _get_data_dir():
    """Get data directory (tenant-scoped in Reflection, default in standalone)."""
    try:
        from familiar.core import paths

        return paths.DATA_DIR
    except ImportError:
        return Path.home() / ".familiar" / "data"


try:
    from familiar.core.paths import DATA_DIR, PLUGINS_DIR
except ImportError:
    DATA_DIR = _get_data_dir()
    PLUGINS_DIR = Path.home() / ".familiar" / "plugins"

REGISTRY_CACHE = DATA_DIR / "plugin_registry.json"
INSTALLED_FILE = DATA_DIR / "installed_plugins.json"

# Default registry URL (would be hosted somewhere)
DEFAULT_REGISTRY_URL = (
    "https://raw.githubusercontent.com/familiar/plugin-registry/main/registry.json"
)

# Local fallback registry with built-in plugins
LOCAL_REGISTRY = {
    "version": "1.0",
    "updated": "2025-01-01",
    "plugins": [
        {
            "id": "home-assistant",
            "name": "Home Assistant",
            "description": "Control smart home devices via Home Assistant",
            "author": "familiar",
            "version": "1.0.0",
            "repo": "https://github.com/familiar/skill-home-assistant",
            "category": "smart-home",
            "tags": ["iot", "automation", "lights", "thermostat"],
            "rating": 4.8,
            "downloads": 1250,
            "requires": [],
        },
        {
            "id": "spotify",
            "name": "Spotify Control",
            "description": "Control Spotify playback, search music, manage playlists",
            "author": "familiar",
            "version": "1.2.0",
            "repo": "https://github.com/familiar/skill-spotify",
            "category": "media",
            "tags": ["music", "spotify", "playlists"],
            "rating": 4.6,
            "downloads": 890,
            "requires": ["spotipy"],
        },
        {
            "id": "notion",
            "name": "Notion Integration",
            "description": "Create pages, manage databases, and sync with Notion",
            "author": "familiar",
            "version": "1.1.0",
            "repo": "https://github.com/familiar/skill-notion",
            "category": "productivity",
            "tags": ["notes", "databases", "wiki"],
            "rating": 4.7,
            "downloads": 750,
            "requires": ["notion-client"],
        },
        {
            "id": "linear",
            "name": "Linear Issues",
            "description": "Manage Linear issues, projects, and sprints",
            "author": "community",
            "version": "1.0.0",
            "repo": "https://github.com/familiar/skill-linear",
            "category": "productivity",
            "tags": ["issues", "projects", "agile"],
            "rating": 4.5,
            "downloads": 320,
            "requires": [],
        },
        {
            "id": "github",
            "name": "GitHub",
            "description": "Manage repos, issues, PRs, and GitHub Actions",
            "author": "familiar",
            "version": "2.0.0",
            "repo": "https://github.com/familiar/skill-github",
            "category": "development",
            "tags": ["git", "repos", "issues", "ci"],
            "rating": 4.9,
            "downloads": 2100,
            "requires": ["PyGithub"],
        },
        {
            "id": "slack",
            "name": "Slack",
            "description": "Send messages, manage channels, search Slack",
            "author": "familiar",
            "version": "1.3.0",
            "repo": "https://github.com/familiar/skill-slack",
            "category": "communication",
            "tags": ["chat", "team", "messaging"],
            "rating": 4.7,
            "downloads": 1800,
            "requires": ["slack-sdk"],
        },
        {
            "id": "openai-images",
            "name": "AI Image Generation",
            "description": "Generate images with DALL-E, edit and analyze images",
            "author": "familiar",
            "version": "1.0.0",
            "repo": "https://github.com/familiar/skill-openai-images",
            "category": "ai",
            "tags": ["images", "dalle", "generation"],
            "rating": 4.4,
            "downloads": 670,
            "requires": ["openai"],
        },
        {
            "id": "stock-market",
            "name": "Stock Market",
            "description": "Get stock quotes, charts, news, and portfolio tracking",
            "author": "community",
            "version": "1.1.0",
            "repo": "https://github.com/familiar/skill-stocks",
            "category": "finance",
            "tags": ["stocks", "investing", "market"],
            "rating": 4.3,
            "downloads": 540,
            "requires": ["yfinance"],
        },
        {
            "id": "fitness",
            "name": "Fitness Tracker",
            "description": "Log workouts, track progress, set fitness goals",
            "author": "community",
            "version": "1.0.0",
            "repo": "https://github.com/familiar/skill-fitness",
            "category": "health",
            "tags": ["exercise", "workouts", "health"],
            "rating": 4.2,
            "downloads": 380,
            "requires": [],
        },
        {
            "id": "recipes",
            "name": "Recipe Manager",
            "description": "Search recipes, meal planning, grocery lists",
            "author": "community",
            "version": "1.0.0",
            "repo": "https://github.com/familiar/skill-recipes",
            "category": "lifestyle",
            "tags": ["cooking", "food", "meals"],
            "rating": 4.5,
            "downloads": 620,
            "requires": [],
        },
        {
            "id": "travel",
            "name": "Travel Planner",
            "description": "Search flights, hotels, create itineraries",
            "author": "community",
            "version": "1.0.0",
            "repo": "https://github.com/familiar/skill-travel",
            "category": "lifestyle",
            "tags": ["flights", "hotels", "vacation"],
            "rating": 4.1,
            "downloads": 290,
            "requires": [],
        },
        {
            "id": "todoist",
            "name": "Todoist",
            "description": "Sync tasks with Todoist, manage projects and labels",
            "author": "familiar",
            "version": "1.2.0",
            "repo": "https://github.com/familiar/skill-todoist",
            "category": "productivity",
            "tags": ["tasks", "todos", "projects"],
            "rating": 4.6,
            "downloads": 920,
            "requires": ["todoist-api-python"],
        },
        {
            "id": "mqtt",
            "name": "MQTT",
            "description": "Publish and subscribe to MQTT topics for IoT",
            "author": "community",
            "version": "1.0.0",
            "repo": "https://github.com/familiar/skill-mqtt",
            "category": "smart-home",
            "tags": ["iot", "mqtt", "sensors"],
            "rating": 4.4,
            "downloads": 410,
            "requires": ["paho-mqtt"],
        },
        {
            "id": "crypto",
            "name": "Cryptocurrency",
            "description": "Track crypto prices, portfolio, and market data",
            "author": "community",
            "version": "1.0.0",
            "repo": "https://github.com/familiar/skill-crypto",
            "category": "finance",
            "tags": ["bitcoin", "ethereum", "trading"],
            "rating": 4.2,
            "downloads": 350,
            "requires": [],
        },
    ],
}

PLUGINS_DIR.mkdir(parents=True, exist_ok=True)
DATA_DIR.mkdir(parents=True, exist_ok=True)


class PluginMarketplace:
    """
    Plugin marketplace for discovering and managing skills.
    """

    def __init__(self, registry_url: str = None):
        self.registry_url = registry_url or DEFAULT_REGISTRY_URL
        self._registry = None
        self._installed = self._load_installed()

    def _load_installed(self) -> Dict:
        """Load list of installed plugins."""
        if INSTALLED_FILE.exists():
            try:
                return json.loads(INSTALLED_FILE.read_text())
            except (json.JSONDecodeError, IOError, OSError) as e:
                logger.warning(f"Failed to load installed plugins: {e}")
        return {"plugins": {}}

    def _save_installed(self):
        """Save installed plugins list."""
        INSTALLED_FILE.write_text(json.dumps(self._installed, indent=2))

    def refresh_registry(self, force: bool = False) -> bool:
        """Fetch the latest plugin registry."""
        # Check cache age
        if not force and REGISTRY_CACHE.exists():
            try:
                cache = json.loads(REGISTRY_CACHE.read_text())
                cached_time = datetime.fromisoformat(cache.get("_cached_at", "2000-01-01"))
                if (datetime.now() - cached_time).total_seconds() < 3600:  # 1 hour cache
                    self._registry = cache
                    return True
            except (json.JSONDecodeError, IOError, OSError, ValueError) as e:
                logger.debug(f"Registry cache invalid: {e}")

        # Try to fetch from remote
        try:
            logger.info(f"Fetching registry from {self.registry_url}")
            req = urllib.request.Request(self.registry_url, headers={"User-Agent": "PiAgent/1.0"})
            with urllib.request.urlopen(req, timeout=10) as response:
                data = json.loads(response.read().decode())
                data["_cached_at"] = datetime.now().isoformat()
                REGISTRY_CACHE.write_text(json.dumps(data, indent=2))
                self._registry = data
                return True
        except Exception as e:
            logger.warning(f"Failed to fetch remote registry: {e}")

        # Fall back to local registry
        self._registry = LOCAL_REGISTRY.copy()
        self._registry["_cached_at"] = datetime.now().isoformat()
        return True

    def get_registry(self) -> Dict:
        """Get the plugin registry."""
        if self._registry is None:
            self.refresh_registry()
        return self._registry

    def list_plugins(self, category: str = None, search: str = None) -> List[Dict]:
        """List available plugins with optional filtering."""
        registry = self.get_registry()
        plugins = registry.get("plugins", [])

        if category:
            plugins = [p for p in plugins if p.get("category") == category]

        if search:
            search_lower = search.lower()
            plugins = [
                p
                for p in plugins
                if search_lower in p.get("name", "").lower()
                or search_lower in p.get("description", "").lower()
                or any(search_lower in tag for tag in p.get("tags", []))
            ]

        # Mark installed plugins
        for p in plugins:
            p["installed"] = p["id"] in self._installed.get("plugins", {})
            if p["installed"]:
                p["installed_version"] = self._installed["plugins"][p["id"]].get("version")

        return plugins

    def get_plugin(self, plugin_id: str) -> Optional[Dict]:
        """Get details about a specific plugin."""
        registry = self.get_registry()
        for plugin in registry.get("plugins", []):
            if plugin["id"] == plugin_id:
                plugin["installed"] = plugin_id in self._installed.get("plugins", {})
                return plugin
        return None

    def get_categories(self) -> List[str]:
        """Get list of plugin categories."""
        registry = self.get_registry()
        categories = set()
        for plugin in registry.get("plugins", []):
            if plugin.get("category"):
                categories.add(plugin["category"])
        return sorted(categories)

    def install(self, plugin_id: str, version: str = None) -> str:
        """Install a plugin."""
        plugin = self.get_plugin(plugin_id)

        if not plugin:
            return f"❌ Plugin not found: {plugin_id}"

        if plugin.get("installed"):
            return f"✓ {plugin['name']} is already installed"

        repo_url = plugin.get("repo", "")
        if not repo_url:
            return f"❌ No repository URL for {plugin_id}"

        # Install directory
        install_path = PLUGINS_DIR / plugin_id

        # Validate repo URL before cloning — only allow known trusted hosts.
        # This prevents a compromised or malicious registry entry from executing
        # arbitrary code on the user's machine via git clone.
        _ALLOWED_HOSTS = (
            "github.com/",
            "gitlab.com/",
            "codeberg.org/",
            "git.sr.ht/",
            "bitbucket.org/",
        )
        if not any(h in repo_url for h in _ALLOWED_HOSTS):
            return (
                f"❌ Installation refused: repo URL {repo_url!r} is not from a trusted host.\n"
                f"Allowed hosts: {', '.join(h.rstrip('/') for h in _ALLOWED_HOSTS)}"
            )

        try:
            # Clone the repo
            logger.info(f"Installing {plugin['name']} from {repo_url}")

            if install_path.exists():
                shutil.rmtree(install_path)

            result = subprocess.run(
                ["git", "clone", "--depth", "1", repo_url, str(install_path)],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
                timeout=60,
            )

            if result.returncode != 0:
                return f"❌ Failed to clone: {result.stderr}"

            # Install Python dependencies
            requirements_file = install_path / "requirements.txt"
            if requirements_file.exists():
                logger.info("Installing dependencies...")
                result = subprocess.run(
                    [
                        "pip",
                        "install",
                        "-r",
                        str(requirements_file),
                        "--quiet",
                        "--break-system-packages",
                    ],
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    text=True,
                    timeout=120,
                )
                if result.returncode != 0:
                    logger.warning(f"Plugin deps install failed: {result.stderr.strip()[-300:]}")

            # Also install from plugin metadata
            for dep in plugin.get("requires", []):
                result = subprocess.run(
                    ["pip", "install", dep, "--quiet", "--break-system-packages"],
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    text=True,
                    timeout=60,
                )
                if result.returncode != 0:
                    logger.warning(f"Failed to install {dep}: {result.stderr.strip()[-200:]}")

            # Create symlink in skills directory
            skills_dir = Path(__file__).parent.parent / "skills"
            skill_link = skills_dir / plugin_id

            if skill_link.exists():
                skill_link.unlink()

            # Look for skill.py in plugin
            if (install_path / "skill.py").exists():
                skill_link.symlink_to(install_path)
            elif (install_path / "src").exists():
                skill_link.symlink_to(install_path / "src")

            # Record installation
            self._installed.setdefault("plugins", {})[plugin_id] = {
                "version": plugin.get("version", "unknown"),
                "installed_at": datetime.now().isoformat(),
                "path": str(install_path),
            }
            self._save_installed()

            return f"✅ Installed {plugin['name']} v{plugin.get('version', '?')}"

        except subprocess.TimeoutExpired:
            return "❌ Installation timed out"
        except Exception as e:
            logger.error(f"Installation error: {e}")
            logger.error(f"Installation failed for {plugin_id}: {e}")
            return f"❌ Installation failed for {plugin_id}"

    def uninstall(self, plugin_id: str) -> str:
        """Uninstall a plugin."""
        if plugin_id not in self._installed.get("plugins", {}):
            return f"❌ {plugin_id} is not installed"

        install_info = self._installed["plugins"][plugin_id]
        install_path = Path(install_info.get("path", PLUGINS_DIR / plugin_id))

        try:
            # Remove symlink from skills
            skills_dir = Path(__file__).parent.parent / "skills"
            skill_link = skills_dir / plugin_id
            if skill_link.exists() or skill_link.is_symlink():
                skill_link.unlink()

            # Remove plugin directory
            if install_path.exists():
                shutil.rmtree(install_path)

            # Update installed list
            del self._installed["plugins"][plugin_id]
            self._save_installed()

            return f"✅ Uninstalled {plugin_id}"

        except Exception as e:
            logger.error(f"Uninstall failed for {plugin_id}: {e}")
            return f"❌ Uninstall failed for {plugin_id}"

    def update(self, plugin_id: str = None) -> str:
        """Update a plugin or all plugins."""
        if plugin_id:
            plugins = [plugin_id]
        else:
            plugins = list(self._installed.get("plugins", {}).keys())

        if not plugins:
            return "No plugins installed"

        results = []
        for pid in plugins:
            if pid not in self._installed.get("plugins", {}):
                continue

            install_info = self._installed["plugins"][pid]
            install_path = Path(install_info.get("path", PLUGINS_DIR / pid))

            if not install_path.exists():
                continue

            try:
                # Git pull
                result = subprocess.run(
                    ["git", "pull"],
                    cwd=str(install_path),
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    text=True,
                    timeout=30,
                )

                if "Already up to date" in result.stdout:
                    results.append(f"✓ {pid}: up to date")
                else:
                    # Reinstall dependencies
                    req_file = install_path / "requirements.txt"
                    if req_file.exists():
                        dep_result = subprocess.run(
                            [
                                "pip",
                                "install",
                                "-r",
                                str(req_file),
                                "--quiet",
                                "--break-system-packages",
                            ],
                            stdout=subprocess.PIPE,
                            stderr=subprocess.PIPE,
                            text=True,
                            timeout=120,
                        )
                        if dep_result.returncode != 0:
                            logger.warning(
                                f"Dep reinstall failed for {pid}: {dep_result.stderr.strip()[-200:]}"
                            )

                    results.append(f"✅ {pid}: updated")

            except Exception as e:
                results.append(f"❌ {pid}: {e}")

        return "\n".join(results) if results else "No plugins to update"

    def installed(self) -> List[Dict]:
        """Get list of installed plugins."""
        result = []
        for plugin_id, info in self._installed.get("plugins", {}).items():
            plugin = self.get_plugin(plugin_id)
            if plugin:
                plugin["installed_at"] = info.get("installed_at")
                plugin["installed_version"] = info.get("version")
                result.append(plugin)
            else:
                result.append(
                    {
                        "id": plugin_id,
                        "name": plugin_id,
                        "installed_at": info.get("installed_at"),
                        "installed_version": info.get("version"),
                        "installed": True,
                    }
                )
        return result


# Global marketplace instance
_marketplace = None


def get_marketplace() -> PluginMarketplace:
    global _marketplace
    if _marketplace is None:
        _marketplace = PluginMarketplace()
    return _marketplace


# ============================================================
# Tool Functions
# ============================================================


def browse_plugins(data: dict) -> str:
    """Browse available plugins."""
    marketplace = get_marketplace()

    category = data.get("category")
    search = data.get("search")

    plugins = marketplace.list_plugins(category=category, search=search)

    if not plugins:
        if search:
            return f"No plugins found matching '{search}'"
        return "No plugins available"

    lines = ["🔌 Available Plugins:\n"]

    # Group by category
    by_category = {}
    for p in plugins:
        cat = p.get("category", "other")
        by_category.setdefault(cat, []).append(p)

    for cat, cat_plugins in sorted(by_category.items()):
        lines.append(f"\n**{cat.title()}**")
        for p in sorted(cat_plugins, key=lambda x: -x.get("downloads", 0)):
            installed = "✓" if p.get("installed") else " "
            rating = "⭐" * int(p.get("rating", 0))
            lines.append(f"  [{installed}] {p['name']} - {p['description'][:40]}...")
            lines.append(f"      {rating} ({p.get('downloads', 0)} downloads)")

    return "\n".join(lines)


def search_plugins(data: dict) -> str:
    """Search for plugins."""
    query = data.get("query", "")
    if not query:
        return "Please provide a search query"

    marketplace = get_marketplace()
    plugins = marketplace.list_plugins(search=query)

    if not plugins:
        return f"No plugins found for '{query}'"

    lines = [f"🔍 Search results for '{query}':\n"]

    for p in plugins[:10]:
        installed = " ✓" if p.get("installed") else ""
        lines.append(f"• **{p['name']}**{installed}")
        lines.append(f"  {p['description']}")
        lines.append(
            f"  ID: {p['id']} | ⭐ {p.get('rating', '?')} | {p.get('downloads', 0)} downloads\n"
        )

    return "\n".join(lines)


def install_plugin(data: dict) -> str:
    """Install a plugin."""
    plugin_id = data.get("plugin_id", "")
    if not plugin_id:
        return "Please provide a plugin ID"

    marketplace = get_marketplace()
    return marketplace.install(plugin_id)


def uninstall_plugin(data: dict) -> str:
    """Uninstall a plugin."""
    plugin_id = data.get("plugin_id", "")
    if not plugin_id:
        return "Please provide a plugin ID"

    marketplace = get_marketplace()
    return marketplace.uninstall(plugin_id)


def update_plugins(data: dict) -> str:
    """Update installed plugins."""
    plugin_id = data.get("plugin_id")  # Optional, updates all if not specified

    marketplace = get_marketplace()
    return marketplace.update(plugin_id)


def list_installed(data: dict) -> str:
    """List installed plugins."""
    marketplace = get_marketplace()
    plugins = marketplace.installed()

    if not plugins:
        return "No plugins installed. Use 'browse plugins' to discover new ones!"

    lines = ["📦 Installed Plugins:\n"]

    for p in plugins:
        lines.append(f"• **{p['name']}** v{p.get('installed_version', '?')}")
        if p.get("description"):
            lines.append(f"  {p['description'][:50]}...")
        lines.append(f"  Installed: {p.get('installed_at', 'unknown')[:10]}\n")

    return "\n".join(lines)


def plugin_info(data: dict) -> str:
    """Get detailed info about a plugin."""
    plugin_id = data.get("plugin_id", "")
    if not plugin_id:
        return "Please provide a plugin ID"

    marketplace = get_marketplace()
    plugin = marketplace.get_plugin(plugin_id)

    if not plugin:
        return f"Plugin not found: {plugin_id}"

    status = "✅ Installed" if plugin.get("installed") else "Not installed"

    return f"""
**{plugin["name"]}** v{plugin.get("version", "?")}
{status}

{plugin.get("description", "")}

Author: {plugin.get("author", "Unknown")}
Category: {plugin.get("category", "other")}
Tags: {", ".join(plugin.get("tags", []))}
Rating: ⭐ {plugin.get("rating", "?")} ({plugin.get("downloads", 0)} downloads)

Repository: {plugin.get("repo", "N/A")}
Dependencies: {", ".join(plugin.get("requires", [])) or "None"}
"""


def list_categories(data: dict) -> str:
    """List plugin categories."""
    marketplace = get_marketplace()
    categories = marketplace.get_categories()

    return "📂 Plugin Categories:\n\n" + "\n".join(f"  • {cat}" for cat in categories)


# Tool definitions
TOOLS = [
    {
        "name": "browse_plugins",
        "description": (
            "Browse available plugins in the Familiar marketplace with optional filtering. "
            "Use this when the user wants to discover new capabilities or explore what is available. "
            "Results are grouped by category and sorted by download count. "
            "Shows installation status, ratings, and download counts for each plugin."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "category": {
                    "type": "string",
                    "description": "Filter by category (productivity, smart-home, media, etc.)",
                },
                "search": {"type": "string", "description": "Search term"},
            },
        },
        "handler": browse_plugins,
        "category": "marketplace",
    },
    {
        "name": "search_plugins",
        "description": (
            "Search for plugins by name, description, or tags. "
            "Use this when looking for a specific integration or capability. "
            "Matches against plugin names, descriptions, and tag keywords. "
            "Returns up to 10 results with IDs, ratings, and installation status."
        ),
        "input_schema": {
            "type": "object",
            "properties": {"query": {"type": "string", "description": "Search query"}},
            "required": ["query"],
        },
        "handler": search_plugins,
        "category": "marketplace",
    },
    {
        "name": "install_plugin",
        "description": (
            "Install a plugin from the marketplace by its plugin ID. "
            "Clones the plugin repository and installs Python dependencies automatically. "
            "Only allows installation from trusted hosts (GitHub, GitLab, Codeberg, etc.). "
            "The plugin becomes available as a new skill after installation."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "plugin_id": {
                    "type": "string",
                    "description": "Plugin ID (e.g., 'spotify', 'home-assistant')",
                }
            },
            "required": ["plugin_id"],
        },
        "handler": install_plugin,
        "category": "marketplace",
    },
    {
        "name": "uninstall_plugin",
        "description": (
            "Uninstall a previously installed plugin by its plugin ID. "
            "Removes the plugin directory, symlinks, and installation record. "
            "Does not remove Python dependencies that may be shared with other plugins. "
            "The plugin's tools will no longer be available after uninstallation."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "plugin_id": {
                    "type": "string",
                    "description": "ID of the installed plugin to remove",
                },
            },
            "required": ["plugin_id"],
        },
        "handler": uninstall_plugin,
        "category": "marketplace",
    },
    {
        "name": "update_plugins",
        "description": (
            "Update one or all installed plugins to their latest versions. "
            "Runs git pull on plugin repositories and reinstalls dependencies if changed. "
            "Specify a plugin_id to update just one, or omit to update all. "
            "Reports whether each plugin was already up to date or successfully updated."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "plugin_id": {
                    "type": "string",
                    "description": "Specific plugin to update (optional, updates all if not specified)",
                }
            },
        },
        "handler": update_plugins,
        "category": "marketplace",
    },
    {
        "name": "list_installed_plugins",
        "description": (
            "List all currently installed plugins with version and install date. "
            "Use this to see what plugins are active in the current Familiar instance. "
            "Shows plugin name, installed version, description, and installation date. "
            "Returns an empty message with a suggestion if no plugins are installed."
        ),
        "input_schema": {"type": "object", "properties": {}},
        "handler": list_installed,
        "category": "marketplace",
    },
    {
        "name": "plugin_info",
        "description": (
            "Get detailed information about a specific plugin by its ID. "
            "Use this to review a plugin before installing it. "
            "Returns name, version, description, author, category, tags, rating, downloads, "
            "repository URL, dependencies, and installation status."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "plugin_id": {
                    "type": "string",
                    "description": "Plugin ID to look up (e.g. 'spotify', 'home-assistant')",
                },
            },
            "required": ["plugin_id"],
        },
        "handler": plugin_info,
        "category": "marketplace",
    },
    {
        "name": "plugin_categories",
        "description": (
            "List all available plugin categories in the marketplace. "
            "Use this to help the user explore what types of plugins exist. "
            "Categories include productivity, smart-home, media, finance, and more. "
            "Use browse_plugins with a category filter to see plugins in a specific category."
        ),
        "input_schema": {"type": "object", "properties": {}},
        "handler": list_categories,
        "category": "marketplace",
    },
]
