# 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

"""
Mealie Integration Skill

Self-hosted recipe manager integration. Browse recipes, meal plans,
and shopping lists from your Mealie instance.

Setup:
    MEALIE_URL=https://your-mealie-instance.com
    MEALIE_TOKEN=your_api_token

Get an API token from: Mealie → User Profile → Manage API Tokens

API: https://docs.mealie.io/
"""

import logging

from familiar.core.api_cache import cached
from familiar.skills._api_utils import (
    APIError,
    api_get,
    api_post,
    get_env,
)

logger = logging.getLogger(__name__)


def _base_url() -> str:
    url = get_env("MEALIE_URL") or ""
    return url.rstrip("/")


def _token() -> str:
    return get_env("MEALIE_TOKEN") or ""


def _headers() -> dict:
    return {
        "Authorization": f"Bearer {_token()}",
        "Accept": "application/json",
    }


def _check_config() -> str:
    """Return error message if not configured, empty string if OK."""
    if not _base_url():
        return (
            "Mealie URL not configured.\n"
            "Set MEALIE_URL to your Mealie instance URL.\n"
            "Example: MEALIE_URL=https://mealie.example.com"
        )
    if not _token():
        return (
            "Mealie API token not configured.\n"
            "Set MEALIE_TOKEN with your API token.\n"
            "Get one from: Mealie → User Profile → Manage API Tokens"
        )
    return ""


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


def mealie_recipes(data: dict) -> str:
    """List or search recipes from Mealie."""
    err = _check_config()
    if err:
        return err

    query = data.get("query", "").strip()
    page = int(data.get("page", 1))
    per_page = min(int(data.get("per_page", 20)), 50)

    try:
        params = {
            "page": page,
            "perPage": per_page,
            "orderBy": "created_at",
            "orderDirection": "desc",
        }
        if query:
            params["search"] = query

        result = _recipes_cached(query, page, per_page)

        items = result.get("items", [])
        total = result.get("total", len(items))

        if not items:
            if query:
                return f"No recipes found matching '{query}'."
            return "No recipes in your Mealie instance yet."

        lines = []
        if query:
            lines.append(f"**Recipes matching '{query}'** ({total} total):")
        else:
            lines.append(f"**Your Recipes** ({total} total, page {page}):")
        lines.append("")

        for i, r in enumerate(items, 1):
            name = r.get("name", "Untitled")
            slug = r.get("slug", "")
            recipe_id = r.get("id", slug)
            total_time = r.get("totalTime", "")
            rating = r.get("rating", 0)

            lines.append(f"{i}. **{name}** (ID: {recipe_id})")
            details = []
            if total_time:
                details.append(total_time)
            if rating:
                stars = int(rating)
                details.append(f"{'*' * stars}")
            if details:
                lines.append(f"   {' | '.join(details)}")

            # Tags
            tags = r.get("tags", [])
            if tags:
                tag_names = [t.get("name", t) if isinstance(t, dict) else str(t) for t in tags[:5]]
                lines.append(f"   Tags: {', '.join(tag_names)}")
            lines.append("")

        return "\n".join(lines)

    except APIError as e:
        return str(e)
    except Exception as e:
        logger.error("Mealie recipes error: %s", e)
        return f"Error fetching recipes from Mealie: {e}"


@cached(prefix="mealie_recipes", ttl=300)
def _recipes_cached(query: str, page: int, per_page: int) -> dict:
    base = _base_url()
    params = {"page": page, "perPage": per_page, "orderBy": "created_at", "orderDirection": "desc"}
    if query:
        params["search"] = query
    return api_get(
        f"{base}/api/recipes",
        params=params,
        headers=_headers(),
        service_name="Mealie",
    )


def mealie_recipe_detail(data: dict) -> str:
    """Get full recipe details from Mealie."""
    err = _check_config()
    if err:
        return err

    recipe_id = data.get("recipe_id", "").strip()
    if not recipe_id:
        return "Please provide a recipe ID or slug."

    try:
        result = _recipe_detail_cached(recipe_id)

        name = result.get("name", "Untitled")
        description = result.get("description", "")
        total_time = result.get("totalTime", "")
        prep_time = result.get("prepTime", "")
        cook_time = result.get("performTime", "")
        servings = result.get("recipeYield", "")

        lines = [f"**{name}**", ""]
        if description:
            lines.append(description[:500])
            lines.append("")

        timing = []
        if prep_time:
            timing.append(f"Prep: {prep_time}")
        if cook_time:
            timing.append(f"Cook: {cook_time}")
        if total_time:
            timing.append(f"Total: {total_time}")
        if timing:
            lines.append(" | ".join(timing))
        if servings:
            lines.append(f"Servings: {servings}")
        lines.append("")

        # Ingredients
        ingredients = result.get("recipeIngredient", [])
        if ingredients:
            lines.append("**Ingredients:**")
            for ing in ingredients:
                if isinstance(ing, dict):
                    note = ing.get("note", ing.get("display", ""))
                    quantity = ing.get("quantity", "")
                    unit = ing.get("unit", {})
                    unit_name = unit.get("name", "") if isinstance(unit, dict) else str(unit)
                    food = ing.get("food", {})
                    food_name = food.get("name", "") if isinstance(food, dict) else str(food)

                    if note:
                        lines.append(f"  - {note}")
                    elif food_name:
                        parts = []
                        if quantity:
                            parts.append(str(quantity))
                        if unit_name:
                            parts.append(unit_name)
                        parts.append(food_name)
                        lines.append(f"  - {' '.join(parts)}")
                else:
                    lines.append(f"  - {ing}")
            lines.append("")

        # Instructions
        instructions = result.get("recipeInstructions", [])
        if instructions:
            lines.append("**Instructions:**")
            for j, step in enumerate(instructions, 1):
                if isinstance(step, dict):
                    text = step.get("text", "")
                else:
                    text = str(step)
                if text:
                    lines.append(f"  {j}. {text}")
            lines.append("")

        # Notes
        notes = result.get("notes", [])
        if notes:
            lines.append("**Notes:**")
            for note in notes[:5]:
                if isinstance(note, dict):
                    title = note.get("title", "")
                    text = note.get("text", "")
                    if title:
                        lines.append(f"  **{title}:** {text}")
                    else:
                        lines.append(f"  - {text}")
                else:
                    lines.append(f"  - {note}")

        return "\n".join(lines)

    except APIError as e:
        return str(e)
    except Exception as e:
        logger.error("Mealie recipe detail error: %s", e)
        return f"Error fetching recipe: {e}"


@cached(prefix="mealie_detail", ttl=300)
def _recipe_detail_cached(recipe_id: str) -> dict:
    base = _base_url()
    return api_get(
        f"{base}/api/recipes/{recipe_id}",
        headers=_headers(),
        service_name="Mealie",
    )


def mealie_add_recipe(data: dict) -> str:
    """Add a recipe to Mealie by URL (auto-import)."""
    err = _check_config()
    if err:
        return err

    url = data.get("url", "").strip()
    if not url:
        return "Please provide a recipe URL to import."

    try:
        base = _base_url()
        result = api_post(
            f"{base}/api/recipes/create-url",
            json={"url": url, "includeTags": True},
            headers=_headers(),
            service_name="Mealie",
        )

        if isinstance(result, str):
            slug = result.strip('"')
            return f"Recipe imported successfully! Slug: **{slug}**\nUse `mealie_recipe_detail` to view it."

        slug = result.get("slug", result.get("id", ""))
        return f"Recipe imported successfully! ID: **{slug}**"

    except APIError as e:
        return str(e)
    except Exception as e:
        logger.error("Mealie add recipe error: %s", e)
        return f"Error importing recipe: {e}"


def mealie_meal_plan(data: dict) -> str:
    """View the current meal plan from Mealie."""
    err = _check_config()
    if err:
        return err

    try:
        result = _meal_plan_cached()

        items = result.get("items", [])
        if not items:
            return "No meal plan entries found. Add meals through Mealie or ask me to plan one."

        lines = ["**Meal Plan:**", ""]

        current_date = ""
        for entry in items:
            date = entry.get("date", "")
            entry_type = entry.get("entryType", "")
            title = entry.get("title", "")
            recipe = entry.get("recipe", {})
            recipe_name = recipe.get("name", title) if recipe else title

            if date != current_date:
                current_date = date
                lines.append(f"**{date}:**")

            type_label = entry_type.title() if entry_type else "Meal"
            lines.append(f"  {type_label}: {recipe_name}")

        return "\n".join(lines)

    except APIError as e:
        return str(e)
    except Exception as e:
        logger.error("Mealie meal plan error: %s", e)
        return f"Error fetching meal plan: {e}"


@cached(prefix="mealie_mealplan", ttl=300)
def _meal_plan_cached() -> dict:
    base = _base_url()
    return api_get(
        f"{base}/api/households/mealplans",
        params={"perPage": 21, "orderBy": "date", "orderDirection": "asc"},
        headers=_headers(),
        service_name="Mealie",
    )


def mealie_shopping_list(data: dict) -> str:
    """View shopping lists from Mealie."""
    err = _check_config()
    if err:
        return err

    list_id = data.get("list_id", "").strip()

    try:
        base = _base_url()

        if list_id:
            result = api_get(
                f"{base}/api/households/shopping/lists/{list_id}",
                headers=_headers(),
                service_name="Mealie",
            )
            return _format_shopping_list(result)

        # List all shopping lists
        result = _shopping_lists_cached()

        items = result.get("items", [])
        if not items:
            return "No shopping lists found."

        lines = ["**Shopping Lists:**", ""]
        for sl in items:
            name = sl.get("name", "Untitled")
            sl_id = sl.get("id", "")
            item_count = len(sl.get("listItems", []))
            lines.append(f"  - **{name}** (ID: {sl_id}) — {item_count} items")

        lines.append("")
        lines.append("Use `mealie_shopping_list` with a list_id for full details.")
        return "\n".join(lines)

    except APIError as e:
        return str(e)
    except Exception as e:
        logger.error("Mealie shopping list error: %s", e)
        return f"Error fetching shopping lists: {e}"


@cached(prefix="mealie_shopping", ttl=300)
def _shopping_lists_cached() -> dict:
    base = _base_url()
    return api_get(
        f"{base}/api/households/shopping/lists",
        headers=_headers(),
        service_name="Mealie",
    )


def _format_shopping_list(sl: dict) -> str:
    """Format a single shopping list."""
    name = sl.get("name", "Shopping List")
    items = sl.get("listItems", [])

    lines = [f"**{name}:**", ""]

    checked = []
    unchecked = []

    for item in items:
        display = item.get("display", "")
        note = item.get("note", "")
        food = item.get("food", {})
        food_name = food.get("name", "") if isinstance(food, dict) else ""
        quantity = item.get("quantity", "")
        unit = item.get("unit", {})
        unit_name = unit.get("name", "") if isinstance(unit, dict) else ""
        is_checked = item.get("checked", False)

        if display:
            text = display
        elif food_name:
            parts = []
            if quantity:
                parts.append(str(quantity))
            if unit_name:
                parts.append(unit_name)
            parts.append(food_name)
            text = " ".join(parts)
        elif note:
            text = note
        else:
            continue

        if is_checked:
            checked.append(text)
        else:
            unchecked.append(text)

    if unchecked:
        lines.append("**Need to buy:**")
        for item in unchecked:
            lines.append(f"  - [ ] {item}")
        lines.append("")

    if checked:
        lines.append(f"**Already got:** ({len(checked)} items)")
        for item in checked[:5]:
            lines.append(f"  - [x] {item}")
        if len(checked) > 5:
            lines.append(f"  ... and {len(checked) - 5} more")

    return "\n".join(lines)


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

TOOLS = [
    {
        "name": "mealie_recipes",
        "description": (
            "List or search recipes from your self-hosted Mealie instance."
            " Returns recipe names, tags, ratings, and IDs."
            " Requires MEALIE_URL and MEALIE_TOKEN."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "Search query (leave empty to list all)",
                    "default": "",
                },
                "page": {
                    "type": "integer",
                    "description": "Page number (default 1)",
                    "default": 1,
                },
                "per_page": {
                    "type": "integer",
                    "description": "Results per page (1-50, default 20)",
                    "default": 20,
                },
            },
        },
        "handler": mealie_recipes,
        "category": "mealie",
    },
    {
        "name": "mealie_recipe_detail",
        "description": (
            "Get full recipe details from Mealie including ingredients,"
            " instructions, timing, and notes."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "recipe_id": {
                    "type": "string",
                    "description": "Recipe ID or slug from mealie_recipes",
                },
            },
            "required": ["recipe_id"],
        },
        "handler": mealie_recipe_detail,
        "category": "mealie",
    },
    {
        "name": "mealie_add_recipe",
        "description": (
            "Import a recipe into Mealie from a URL."
            " Mealie auto-extracts ingredients, instructions, and images."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "url": {
                    "type": "string",
                    "description": "URL of the recipe page to import",
                },
            },
            "required": ["url"],
        },
        "handler": mealie_add_recipe,
        "category": "mealie",
    },
    {
        "name": "mealie_meal_plan",
        "description": (
            "View the current meal plan from your Mealie instance."
            " Shows planned meals organized by date."
        ),
        "input_schema": {
            "type": "object",
            "properties": {},
        },
        "handler": mealie_meal_plan,
        "category": "mealie",
    },
    {
        "name": "mealie_shopping_list",
        "description": (
            "View shopping lists from Mealie."
            " Shows items grouped by checked/unchecked status."
            " Provide a list_id for a specific list, or omit to see all lists."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "list_id": {
                    "type": "string",
                    "description": "Shopping list ID (omit to list all)",
                    "default": "",
                },
            },
        },
        "handler": mealie_shopping_list,
        "category": "mealie",
    },
]
