# 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

"""
Connected Service Search Skill

Agent-facing tool for searching across all connected services (Drive,
Email, Nextcloud, Gitea, Jellyfin, Video library, GitHub).  Wraps the
existing ``search_service()`` dispatcher from ``_browse.py`` so the
agent can reason about queries and present intelligent results.
"""

import json
import logging

logger = logging.getLogger(__name__)


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def _build_text_and_json(items, summary, service, query):
    """Build the dual-format return string: human text + JSON marker."""
    lines = [f"Found {len(items)} results in {service} for: {query}\n"]
    for i, item in enumerate(items, 1):
        lines.append(f"  {i}. {item.get('name', '(untitled)')}")
        detail = item.get("detail", "")
        if detail:
            lines.append(f"     {detail}")
        url = item.get("url", "")
        if url:
            lines.append(f"     {url}")
        lines.append("")

    payload = {"items": items, "summary": summary}
    lines.append("SEARCH_RESULTS_JSON:")
    lines.append(json.dumps(payload))
    return "\n".join(lines)


# ---------------------------------------------------------------------------
# Tool handler
# ---------------------------------------------------------------------------

# Services accepted by the dispatcher
_VALID_SERVICES = ("drive", "email", "nextcloud", "gitea", "jellyfin", "video", "github")


def service_search(data: dict) -> str:
    """Search a connected service by name and query."""
    from familiar.channels.connect_wizard._browse import search_service

    service = data.get("service", "").strip().lower()
    query = data.get("query", "").strip()

    if not service:
        return "Please specify a service to search (drive, email, nextcloud, gitea, jellyfin, video, github)."
    if service not in _VALID_SERVICES:
        return f"Unknown service: {service}. Available services: {', '.join(_VALID_SERVICES)}"
    if not query:
        return "Please provide a search query."

    result = search_service(service, query)
    items = result.get("items", [])
    summary = result.get("summary", f"{len(items)} results")

    if result.get("error"):
        return f"Search failed for {service}: {result['error']}"

    if not items:
        return f"No results found in {service} for: {query}"

    return _build_text_and_json(items, summary, service, query)


# ---------------------------------------------------------------------------
# Server management tools
# ---------------------------------------------------------------------------


def server_status(data: dict) -> str:
    """Check the status of self-hosted services and the container runtime."""
    try:
        from familiar.services.manager import get_service_manager

        mgr = get_service_manager()
        rt_info = mgr.get_runtime_info()
        statuses = mgr.get_all_statuses()
    except Exception as e:
        return f"Could not check server status: {e}"

    lines = [f"Container runtime: {rt_info.get('runtime', 'none')}"]
    if rt_info.get("version"):
        lines[0] += f" v{rt_info['version']}"
    if rt_info.get("rootless"):
        lines[0] += " (rootless)"

    running = [s for s in statuses if s.get("status") == "running"]
    stopped = [s for s in statuses if s.get("status") == "stopped"]
    not_installed = [s for s in statuses if s.get("status") == "not_installed"]

    lines.append(
        f"\n{len(running)} running, {len(stopped)} stopped, {len(not_installed)} not installed\n"
    )

    if running:
        lines.append("Running:")
        for s in running:
            url = s.get("url", "")
            lines.append(
                f"  - {s.get('display_name', s.get('key', '?'))}" + (f" ({url})" if url else "")
            )

    if stopped:
        lines.append("\nStopped:")
        for s in stopped:
            lines.append(f"  - {s.get('display_name', s.get('key', '?'))}")

    if not_installed:
        lines.append(f"\nNot installed ({len(not_installed)}):")
        for s in not_installed[:8]:
            lines.append(f"  - {s.get('display_name', s.get('key', '?'))}")
        if len(not_installed) > 8:
            lines.append(f"  ... and {len(not_installed) - 8} more")

    return "\n".join(lines)


def server_setup(data: dict) -> str:
    """Set up the self-hosted server stack from scratch.

    This is the one-button server setup: installs the container runtime
    (Podman) if missing, then provisions and starts all services
    recommended for the user's job class.

    Optionally accepts a list of specific service keys to set up.
    """
    try:
        from familiar.services.manager import get_service_manager

        mgr = get_service_manager()
    except Exception as e:
        return f"Could not initialize service manager: {e}"

    # Determine services
    service_keys = data.get("services")
    if isinstance(service_keys, str):
        service_keys = [s.strip() for s in service_keys.split(",") if s.strip()]

    if not service_keys:
        try:
            from familiar.onboard_engine import OnboardingEngine

            engine = OnboardingEngine()
            result = engine.get_server_recommendations()
            service_keys = [r["key"] for r in result.data.get("recommendations", [])]
        except Exception:
            pass

    if not service_keys:
        return (
            "No services to set up. Please set a job class first, "
            "or specify services (e.g. services: 'mealie, gitea')."
        )

    result = mgr.full_server_setup(service_keys)

    # Format result for the agent to relay to the user
    lines = []
    bs = result.get("bootstrap", {})
    if bs and not bs.get("already_installed"):
        if bs.get("success"):
            lines.append(f"Installed {bs.get('runtime', 'Podman')} container runtime.")
        else:
            return f"Failed to install container runtime: {bs.get('error', 'unknown')}"
    elif bs and bs.get("already_installed"):
        lines.append(f"Container runtime: {bs.get('runtime', 'unknown')} (already installed)")

    summary = result.get("summary", {})
    lines.append(
        f"\n{summary.get('ok', 0)} services started, "
        f"{summary.get('failed', 0)} failed, "
        f"{summary.get('skipped', 0)} skipped (native)"
    )

    for svc in result.get("services", []):
        name = svc.get("display_name") or svc.get("service", "?")
        status = svc.get("status", "?")
        if svc.get("ok"):
            url = svc.get("url", "")
            lines.append(f"  OK  {name} — {status}" + (f" ({url})" if url else ""))
        else:
            error = svc.get("error", "")
            lines.append(f"  FAIL  {name} — {status}: {error}")

    return "\n".join(lines)


def server_deploy_service(data: dict) -> str:
    """Deploy (provision + start) a single self-hosted service."""
    service = (data.get("service") or data.get("key") or "").strip().lower()
    if not service:
        return "Please specify which service to deploy (e.g. 'mealie', 'gitea', 'nextcloud')."

    # Map friendly names to keys
    _name_map = {
        "recipes": "mealie",
        "recipe": "mealie",
        "git": "gitea",
        "forgejo": "gitea",
        "cloud": "nextcloud",
        "files": "nextcloud",
        "media": "jellyfin",
        "media server": "jellyfin",
        "notes": "joplin",
        "dns": "pihole",
        "pi-hole": "pihole",
        "search": "searxng",
        "searx": "searxng",
        "siem": "wazuh_manager",
        "monitoring": "netdata",
        "uptime": "uptime_kuma",
        "mastodon": "gotosocial",
        "fediverse": "gotosocial",
        "bluesky": "bluesky_pds",
    }
    key = _name_map.get(service, service.replace(" ", "_").replace("-", "_"))

    try:
        from familiar.services.manager import get_service_manager
        from familiar.services.specs import SERVICE_SPECS

        if key not in SERVICE_SPECS:
            available = ", ".join(sorted(SERVICE_SPECS.keys()))
            return f"Unknown service: {key}. Available: {available}"

        mgr = get_service_manager()
        result = mgr.full_server_setup([key])

        svc = result.get("services", [{}])[0]
        name = svc.get("display_name", key)
        if svc.get("ok"):
            url = svc.get("url", "")
            return f"{name} is {svc['status']}." + (f" Available at {url}" if url else "")
        else:
            return f"Failed to deploy {name}: {svc.get('error', svc.get('status', 'unknown'))}"
    except Exception as e:
        return f"Deploy failed: {e}"


# ---------------------------------------------------------------------------
# Tool definitions (auto-discovered by skill loader)
# ---------------------------------------------------------------------------

TOOLS = [
    {
        "name": "service_search",
        "description": (
            "Search a connected service (Google Drive, Email, Nextcloud, Gitea, "
            "Jellyfin, local video library, or GitHub). Use this when the user "
            "wants to find files, emails, repos, media, or other items in their "
            "connected services."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "service": {
                    "type": "string",
                    "enum": list(_VALID_SERVICES),
                    "description": (
                        "Which service to search: drive, email, nextcloud, "
                        "gitea, jellyfin, video, or github"
                    ),
                },
                "query": {
                    "type": "string",
                    "description": "The search query",
                },
            },
            "required": ["service", "query"],
        },
        "handler": service_search,
        "category": "services",
    },
    {
        "name": "server_status",
        "description": (
            "Check the status of self-hosted services (Mealie, Gitea, Nextcloud, "
            "Jellyfin, SearXNG, etc.) and the container runtime (Podman/Docker). "
            "Shows which services are running, stopped, or not installed."
        ),
        "input_schema": {
            "type": "object",
            "properties": {},
        },
        "handler": server_status,
        "category": "services",
    },
    {
        "name": "server_setup",
        "description": (
            "Set up the entire self-hosted server stack from scratch. Installs "
            "the container runtime (Podman) if not present, then provisions and "
            "starts all services recommended for the user's job class. Call this "
            "when the user wants to set up their server, deploy all services, or "
            "asks about self-hosting. Optionally pass specific service names."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "services": {
                    "type": "string",
                    "description": (
                        "Comma-separated list of service keys to deploy "
                        "(e.g. 'mealie, gitea'). Leave empty to deploy all "
                        "recommended services for the user's job class."
                    ),
                },
            },
        },
        "handler": server_setup,
        "category": "services",
    },
    {
        "name": "server_deploy_service",
        "description": (
            "Deploy a single self-hosted service by name. Provisions the "
            "container, starts it, and reports the URL. Use this when the "
            "user wants to install/deploy/start a specific service like "
            "Mealie, Gitea, Nextcloud, Jellyfin, etc."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "service": {
                    "type": "string",
                    "description": (
                        "Service to deploy: mealie, gitea, nextcloud, jellyfin, "
                        "joplin, pihole, searxng, wazuh_manager, crowdsec, "
                        "spiderfoot, netdata, uptime_kuma, cockpit, gotosocial, bluesky_pds"
                    ),
                },
            },
            "required": ["service"],
        },
        "handler": server_deploy_service,
        "category": "services",
    },
]
