# 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

"""
Jellyfin Skill

Browse libraries, search media, see what's playing, and control playback
on your Jellyfin media server.

Setup:
    JELLYFIN_URL=https://jellyfin.example.org
    JELLYFIN_TOKEN=your-api-key
    JELLYFIN_USER_ID=your-user-id
"""

import logging
import os

import httpx

from familiar.core.utils import format_http_error

logger = logging.getLogger(__name__)

REQUEST_TIMEOUT = 30


# ---------------------------------------------------------------------------
# Configuration helpers
# ---------------------------------------------------------------------------


def _get_jf_config() -> dict:
    return {
        "url": os.environ.get("JELLYFIN_URL", "").rstrip("/"),
        "token": os.environ.get("JELLYFIN_TOKEN", ""),
        "user_id": os.environ.get("JELLYFIN_USER_ID", ""),
    }


def _jf_configured() -> bool:
    cfg = _get_jf_config()
    return bool(cfg["url"] and cfg["token"] and cfg["user_id"])


def _jf_headers() -> dict:
    cfg = _get_jf_config()
    return {"X-Emby-Token": cfg["token"]}


# ---------------------------------------------------------------------------
# Tool handlers
# ---------------------------------------------------------------------------


def jf_search_media(data: dict) -> str:
    """Search for media items on Jellyfin."""
    if not _jf_configured():
        return "Jellyfin not configured. Run /connect jellyfin to set up, or set JELLYFIN_URL, JELLYFIN_TOKEN, and JELLYFIN_USER_ID."

    query = data.get("query", "").strip()
    if not query:
        return "Please provide a search query."

    cfg = _get_jf_config()
    url = f"{cfg['url']}/Items"
    params = {
        "searchTerm": query,
        "Recursive": "true",
        "UserId": cfg["user_id"],
    }

    try:
        resp = httpx.get(url, headers=_jf_headers(), params=params, timeout=REQUEST_TIMEOUT)
        if resp.status_code != 200:
            return format_http_error(
                "Jellyfin", status_code=resp.status_code, connect_cmd="jellyfin"
            )

        data_resp = resp.json()
        items = data_resp.get("Items", [])
        if not items:
            return f"No results for '{query}'."

        lines = [f"Search results for '{query}':"]
        for item in items[:20]:
            title = item.get("Name", "(untitled)")
            item_type = item.get("Type", "Unknown")
            year = item.get("ProductionYear", "")
            year_str = f" ({year})" if year else ""
            lines.append(f"  [{item_type}] {title}{year_str}")
        if len(items) > 20:
            lines.append(f"  ... and {len(items) - 20} more")
        return "\n".join(lines)

    except httpx.HTTPError as e:
        logger.error(f"Jellyfin search error: {e}")
        return format_http_error("Jellyfin", exception=e, connect_cmd="jellyfin")


def jf_now_playing(data: dict) -> str:
    """Show currently playing sessions on Jellyfin."""
    if not _jf_configured():
        return "Jellyfin not configured. Run /connect jellyfin to set up, or set JELLYFIN_URL, JELLYFIN_TOKEN, and JELLYFIN_USER_ID."

    cfg = _get_jf_config()
    url = f"{cfg['url']}/Sessions"

    try:
        resp = httpx.get(url, headers=_jf_headers(), timeout=REQUEST_TIMEOUT)
        if resp.status_code != 200:
            return format_http_error(
                "Jellyfin", status_code=resp.status_code, connect_cmd="jellyfin"
            )

        sessions = resp.json()
        playing = [s for s in sessions if s.get("NowPlayingItem")]

        if not playing:
            return "Nothing is currently playing."

        lines = ["Now playing:"]
        for session in playing:
            item = session["NowPlayingItem"]
            title = item.get("Name", "(untitled)")
            item_type = item.get("Type", "Unknown")
            user = session.get("UserName", "Unknown user")
            device = session.get("DeviceName", "Unknown device")
            lines.append(f"  {user} on {device}: [{item_type}] {title}")
        return "\n".join(lines)

    except httpx.HTTPError as e:
        logger.error(f"Jellyfin sessions error: {e}")
        return format_http_error("Jellyfin", exception=e, connect_cmd="jellyfin")


def jf_play_media(data: dict) -> str:
    """Start playing media on a session."""
    if not _jf_configured():
        return "Jellyfin not configured. Run /connect jellyfin to set up, or set JELLYFIN_URL, JELLYFIN_TOKEN, and JELLYFIN_USER_ID."

    session_id = data.get("session_id", "").strip()
    item_id = data.get("item_id", "").strip()
    if not session_id:
        return "Please provide a session_id."
    if not item_id:
        return "Please provide an item_id."

    cfg = _get_jf_config()
    url = f"{cfg['url']}/Sessions/{session_id}/Playing"
    body = {
        "ItemIds": [item_id],
        "PlayCommand": "PlayNow",
    }

    try:
        resp = httpx.post(url, headers=_jf_headers(), json=body, timeout=REQUEST_TIMEOUT)
        if resp.status_code in (200, 204):
            return f"Playing item {item_id} on session {session_id}."
        return format_http_error("Jellyfin", status_code=resp.status_code, connect_cmd="jellyfin")

    except httpx.HTTPError as e:
        logger.error(f"Jellyfin play error: {e}")
        return format_http_error("Jellyfin", exception=e, connect_cmd="jellyfin")


def jf_get_library(data: dict) -> str:
    """Browse a library's contents."""
    if not _jf_configured():
        return "Jellyfin not configured. Run /connect jellyfin to set up, or set JELLYFIN_URL, JELLYFIN_TOKEN, and JELLYFIN_USER_ID."

    cfg = _get_jf_config()
    parent_id = data.get("parent_id", "").strip()
    url = f"{cfg['url']}/Items"
    params = {"UserId": cfg["user_id"]}
    if parent_id:
        params["ParentId"] = parent_id

    try:
        resp = httpx.get(url, headers=_jf_headers(), params=params, timeout=REQUEST_TIMEOUT)
        if resp.status_code != 200:
            return format_http_error(
                "Jellyfin", status_code=resp.status_code, connect_cmd="jellyfin"
            )

        data_resp = resp.json()
        items = data_resp.get("Items", [])
        if not items:
            return "No items found."

        lines = ["Library contents:"]
        for item in items[:30]:
            title = item.get("Name", "(untitled)")
            item_type = item.get("Type", "Unknown")
            year = item.get("ProductionYear", "")
            year_str = f" ({year})" if year else ""
            lines.append(f"  [{item_type}] {title}{year_str}")
        if len(items) > 30:
            lines.append(f"  ... and {len(items) - 30} more")
        return "\n".join(lines)

    except httpx.HTTPError as e:
        logger.error(f"Jellyfin library error: {e}")
        return format_http_error("Jellyfin", exception=e, connect_cmd="jellyfin")


def jf_list_users(data: dict) -> str:
    """List Jellyfin users."""
    if not _jf_configured():
        return "Jellyfin not configured. Run /connect jellyfin to set up, or set JELLYFIN_URL, JELLYFIN_TOKEN, and JELLYFIN_USER_ID."

    cfg = _get_jf_config()
    url = f"{cfg['url']}/Users"

    try:
        resp = httpx.get(url, headers=_jf_headers(), timeout=REQUEST_TIMEOUT)
        if resp.status_code != 200:
            return format_http_error(
                "Jellyfin", status_code=resp.status_code, connect_cmd="jellyfin"
            )

        users = resp.json()
        if not users:
            return "No users found."

        lines = [f"Jellyfin users ({len(users)}):"]
        for user in users:
            name = user.get("Name", "(unnamed)")
            user_id = user.get("Id", "")
            is_admin = user.get("Policy", {}).get("IsAdministrator", False)
            admin_str = " [admin]" if is_admin else ""
            lines.append(f"  {name}{admin_str} (ID: {user_id})")
        return "\n".join(lines)

    except httpx.HTTPError as e:
        logger.error(f"Jellyfin users error: {e}")
        return format_http_error("Jellyfin", exception=e, connect_cmd="jellyfin")


# ---------------------------------------------------------------------------
# Tool definitions
# ---------------------------------------------------------------------------

TOOLS = [
    {
        "name": "jf_search_media",
        "description": (
            "Search for media items on Jellyfin including movies, series, music, and other content types. "
            "Performs a recursive search across all libraries. "
            "Returns item type, title, and production year for up to 20 results. "
            "Requires JELLYFIN_URL, JELLYFIN_TOKEN, and JELLYFIN_USER_ID to be configured."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "Search term",
                },
            },
            "required": ["query"],
        },
        "handler": jf_search_media,
        "category": "jellyfin",
    },
    {
        "name": "jf_now_playing",
        "description": (
            "Show all currently active playback sessions on Jellyfin. "
            "Displays the user, device, item type, and title for each playing session. "
            "Returns a message if nothing is currently playing."
        ),
        "input_schema": {"type": "object", "properties": {}},
        "handler": jf_now_playing,
        "category": "jellyfin",
    },
    {
        "name": "jf_play_media",
        "description": (
            "Start playing a media item on a specific Jellyfin session. "
            "Requires both a session ID (from jf_now_playing) and an item ID (from jf_search_media or jf_get_library). "
            "This action requires user confirmation before execution. "
            "Sends a PlayNow command to the target session."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "session_id": {
                    "type": "string",
                    "description": "Target session ID",
                },
                "item_id": {
                    "type": "string",
                    "description": "Media item ID to play",
                },
            },
            "required": ["session_id", "item_id"],
        },
        "handler": jf_play_media,
        "confirm": True,
        "confirm_message": "Play media on Jellyfin session?",
        "risk_level": "medium",
        "category": "jellyfin",
    },
    {
        "name": "jf_get_library",
        "description": (
            "Browse the contents of a Jellyfin library or folder. "
            "Omit parent_id to see root-level libraries; provide a parent_id to browse inside a specific library or folder. "
            "Returns up to 30 items with type, title, and production year."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "parent_id": {
                    "type": "string",
                    "description": "Library or folder ID to browse (omit for root libraries)",
                },
            },
        },
        "handler": jf_get_library,
        "category": "jellyfin",
    },
    {
        "name": "jf_list_users",
        "description": (
            "List all user accounts on the Jellyfin server. "
            "Shows each user's name, admin status, and user ID. "
            "Useful for finding user IDs needed for other Jellyfin operations."
        ),
        "input_schema": {"type": "object", "properties": {}},
        "handler": jf_list_users,
        "category": "jellyfin",
    },
]
