# 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

"""
Media Skill — Online Radio & Podcasts

Stream radio stations via Radio Browser API (free, no auth)
and browse podcasts via iTunes Search + RSS feeds.
"""

from __future__ import annotations

import json
import logging
from datetime import datetime, timezone
from pathlib import Path

logger = logging.getLogger(__name__)


def _get_data_dir() -> Path:
    """Get the media data directory."""
    try:
        from familiar.core.paths import DATA_DIR

        d = DATA_DIR / "media"
    except ImportError:
        d = Path.home() / ".familiar" / "data" / "media"
    d.mkdir(parents=True, exist_ok=True)
    return d


def _load_json(filename: str) -> list:
    """Load a JSON list from the media data directory."""
    path = _get_data_dir() / filename
    if not path.exists():
        return []
    try:
        with open(path, encoding="utf-8") as f:
            data = json.load(f)
            return data if isinstance(data, list) else []
    except (json.JSONDecodeError, IOError, OSError) as e:
        logger.debug("JSON load error for %s: %s", filename, e)
        return []


def _save_json(filename: str, data: list) -> None:
    """Save a JSON list to the media data directory."""
    path = _get_data_dir() / filename
    path.parent.mkdir(parents=True, exist_ok=True)
    with open(path, "w", encoding="utf-8") as f:
        json.dump(data, f, indent=2)


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


def radio_search(data: dict) -> str:
    """Search radio stations by name, genre, or country."""
    from familiar.skills.media.radio import search_stations

    name = data.get("name", "").strip()
    tag = data.get("genre", "").strip() or data.get("tag", "").strip()
    country = data.get("country", "").strip()
    limit = min(int(data.get("limit", 25)), 50)

    if not name and not tag and not country:
        # Default to popular stations
        from familiar.skills.media.radio import get_popular

        stations = get_popular(limit=limit)
    else:
        stations = search_stations(name=name, tag=tag, country=country, limit=limit)

    if not stations:
        return "No radio stations found. Try a different search."

    lines = [f"**Radio Stations** ({len(stations)} results):", ""]
    for i, s in enumerate(stations[:limit], 1):
        tags = ", ".join(s.get("tags", [])[:3])
        meta = []
        if s.get("country"):
            meta.append(s["country"])
        if tags:
            meta.append(tags)
        if s.get("bitrate"):
            meta.append(f"{s['bitrate']}kbps")
        meta_str = f" — {', '.join(meta)}" if meta else ""

        lines.append(f"{i}. **{s['name']}**{meta_str}")
        if s.get("stream_url"):
            lines.append(f"   Stream: {s['stream_url']}")
        lines.append("")

    return "\n".join(lines)


def radio_genres(data: dict) -> str:
    """List popular radio genres with station counts."""
    from familiar.skills.media.radio import get_genres

    limit = min(int(data.get("limit", 30)), 100)
    genres = get_genres(limit=limit)

    if not genres:
        return "Could not fetch genre list."

    lines = ["**Popular Radio Genres:**", ""]
    for g in genres:
        lines.append(f"  - **{g['name']}** ({g['stationcount']} stations)")

    return "\n".join(lines)


def podcast_search(data: dict) -> str:
    """Search iTunes for podcasts."""
    from familiar.skills.media.podcasts import search_podcasts

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

    limit = min(int(data.get("limit", 25)), 50)
    podcasts = search_podcasts(term=term, limit=limit)

    if not podcasts:
        return f"No podcasts found for '{term}'."

    lines = [f"**Podcasts: {term}** ({len(podcasts)} results):", ""]
    for i, p in enumerate(podcasts[:limit], 1):
        genre = p.get("primary_genre", "")
        meta = f" — {p['artist']}" if p.get("artist") else ""
        if genre:
            meta += f" [{genre}]"

        lines.append(f"{i}. **{p['name']}**{meta}")
        if p.get("feed_url"):
            lines.append(f"   Feed: {p['feed_url']}")
        lines.append("")

    return "\n".join(lines)


def podcast_episodes(data: dict) -> str:
    """Get episodes from a podcast RSS feed."""
    from familiar.skills.media.podcasts import get_episodes

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

    limit = min(int(data.get("limit", 20)), 50)
    episodes = get_episodes(feed_url=feed_url, limit=limit)

    if not episodes:
        return "No episodes found for this feed."

    lines = [f"**Episodes** ({len(episodes)} found):", ""]
    for i, ep in enumerate(episodes, 1):
        dur = f" [{ep['duration']}]" if ep.get("duration") else ""
        pub = f" — {ep['published'][:16]}" if ep.get("published") else ""

        lines.append(f"{i}. **{ep['title']}**{dur}{pub}")
        if ep.get("description"):
            lines.append(f"   {ep['description'][:200]}")
        if ep.get("audio_url"):
            lines.append(f"   Audio: {ep['audio_url']}")
        lines.append("")

    return "\n".join(lines)


def media_favorites(data: dict) -> str:
    """List saved media favorites."""
    favorites = _load_json("favorites.json")

    if not favorites:
        return "No favorites saved yet. Use the dashboard media panel to save stations and podcasts."

    radio = [f for f in favorites if f.get("type") == "radio"]
    podcasts = [f for f in favorites if f.get("type") == "podcast"]

    lines = [f"**Media Favorites** ({len(favorites)} saved):", ""]

    if radio:
        lines.append(f"**Radio Stations ({len(radio)}):**")
        for s in radio:
            lines.append(f"  - {s.get('name', 'Unknown')}")
        lines.append("")

    if podcasts:
        lines.append(f"**Podcasts ({len(podcasts)}):**")
        for p in podcasts:
            lines.append(f"  - {p.get('name', 'Unknown')}")
        lines.append("")

    return "\n".join(lines)


def add_favorite(item: dict) -> bool:
    """Add an item to favorites (used by dashboard endpoints)."""
    favorites = _load_json("favorites.json")
    # Deduplicate by stream_url or feed_url
    key = item.get("stream_url") or item.get("feed_url") or item.get("name", "")
    for f in favorites:
        existing_key = f.get("stream_url") or f.get("feed_url") or f.get("name", "")
        if existing_key == key:
            return False  # Already exists

    item["added_at"] = datetime.now(timezone.utc).isoformat()
    favorites.append(item)
    _save_json("favorites.json", favorites)
    return True


def remove_favorite(item: dict) -> bool:
    """Remove an item from favorites (used by dashboard endpoints)."""
    favorites = _load_json("favorites.json")
    key = item.get("stream_url") or item.get("feed_url") or item.get("name", "")
    original_len = len(favorites)
    favorites = [
        f for f in favorites
        if (f.get("stream_url") or f.get("feed_url") or f.get("name", "")) != key
    ]
    if len(favorites) < original_len:
        _save_json("favorites.json", favorites)
        return True
    return False


def get_favorites() -> list:
    """Get all favorites (used by dashboard endpoints)."""
    return _load_json("favorites.json")


def add_history(item: dict) -> None:
    """Record a played item to history (used by dashboard endpoints)."""
    history = _load_json("history.json")
    item["played_at"] = datetime.now(timezone.utc).isoformat()
    history.insert(0, item)
    # Keep last 100 items
    if len(history) > 100:
        history = history[:100]
    _save_json("history.json", history)


def get_history() -> list:
    """Get play history (used by dashboard endpoints)."""
    return _load_json("history.json")


# ---------------------------------------------------------------------------
# Music vault helpers (used by dashboard music endpoints)
# ---------------------------------------------------------------------------


def _get_music_dir() -> Path:
    """Get the music tracks storage directory, creating if needed."""
    d = _get_data_dir() / "music" / "tracks"
    d.mkdir(parents=True, exist_ok=True)
    return d


def get_music_library(search: str | None = None) -> list:
    """Read the music library catalog, optionally filtering by search term."""
    tracks = _load_json("music/library.json")
    if search:
        term = search.lower()
        tracks = [t for t in tracks if term in t.get("filename", "").lower()]
    return tracks


def add_to_music_library(track: dict) -> bool:
    """Append a track dict to library.json, deduplicating by track id."""
    tracks = _load_json("music/library.json")
    for t in tracks:
        if t.get("id") == track.get("id"):
            return False
    tracks.append(track)
    _save_json("music/library.json", tracks)
    return True


def remove_from_music_library(track_id: str) -> bool:
    """Remove a track by id from the catalog and delete the file from disk."""
    tracks = _load_json("music/library.json")
    removed = None
    remaining = []
    for t in tracks:
        if t.get("id") == track_id:
            removed = t
        else:
            remaining.append(t)
    if not removed:
        return False
    _save_json("music/library.json", remaining)
    # Delete the file on disk
    file_path = Path(removed.get("path", ""))
    if file_path.is_file():
        try:
            file_path.unlink()
        except OSError as e:
            logger.debug("Failed to delete music file %s: %s", file_path, e)
    return True


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

TOOLS = [
    {
        "name": "radio_search",
        "description": (
            "Search online radio stations by name, genre, or country."
            " Uses the free Radio Browser API. Returns station names,"
            " stream URLs, genres, and bitrates."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "name": {
                    "type": "string",
                    "description": "Station name to search for",
                },
                "genre": {
                    "type": "string",
                    "description": "Genre/tag to filter by (e.g., 'jazz', 'rock', 'classical')",
                },
                "country": {
                    "type": "string",
                    "description": "Country name to filter by",
                },
                "limit": {
                    "type": "integer",
                    "description": "Max results (default 25)",
                    "default": 25,
                },
            },
        },
        "handler": radio_search,
        "category": "media",
    },
    {
        "name": "radio_genres",
        "description": (
            "List popular radio genres/tags with station counts."
            " Useful for discovering what types of radio stations are available."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "limit": {
                    "type": "integer",
                    "description": "Max genres to return (default 30)",
                    "default": 30,
                },
            },
        },
        "handler": radio_genres,
        "category": "media",
    },
    {
        "name": "podcast_search",
        "description": (
            "Search for podcasts on iTunes. Returns podcast names, artists,"
            " feed URLs, artwork, and genres."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "term": {
                    "type": "string",
                    "description": "Search term for podcasts",
                },
                "limit": {
                    "type": "integer",
                    "description": "Max results (default 25)",
                    "default": 25,
                },
            },
            "required": ["term"],
        },
        "handler": podcast_search,
        "category": "media",
    },
    {
        "name": "podcast_episodes",
        "description": (
            "Get episodes from a podcast RSS feed URL."
            " Returns episode titles, descriptions, audio URLs, and durations."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "feed_url": {
                    "type": "string",
                    "description": "RSS feed URL of the podcast",
                },
                "limit": {
                    "type": "integer",
                    "description": "Max episodes (default 20)",
                    "default": 20,
                },
            },
            "required": ["feed_url"],
        },
        "handler": podcast_episodes,
        "category": "media",
    },
    {
        "name": "media_favorites",
        "description": (
            "List your saved media favorites — radio stations and podcasts"
            " you've bookmarked from the media panel."
        ),
        "input_schema": {
            "type": "object",
            "properties": {},
        },
        "handler": media_favorites,
        "category": "media",
    },
]
