# 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

"""
USDA FoodData Central Skill

Search 380K+ foods for nutrition data, ingredient costs, and dietary analysis.
Powers the Chef job class with per-ingredient nutrition and cost breakdowns.

Setup:
    FOODDATA_API_KEY=your_api_key   (get free at https://fdc.nal.usda.gov/api-key-signup/)
    Falls back to DEMO_KEY with lower rate limits if not set.
"""

import logging

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

logger = logging.getLogger(__name__)

BASE_URL = "https://api.nal.usda.gov/fdc/v1"
DEMO_KEY = "DEMO_KEY"

# 1000 req/hour with real key, much lower with DEMO_KEY
_limiter = RateLimiter(calls=900, period=3600)


def _api_key() -> str:
    return get_env("FOODDATA_API_KEY") or DEMO_KEY


def _params(**extra) -> dict:
    p = {"api_key": _api_key()}
    p.update(extra)
    return p


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


def food_search(data: dict) -> str:
    """Search for foods by keyword."""
    query = data.get("query", "").strip()
    if not query:
        return "Please provide a search query (e.g., 'chicken breast', 'all-purpose flour')."

    page_size = min(int(data.get("limit", 10)), 25)
    data_type = data.get("data_type", "")

    if not _limiter.acquire():
        return f"USDA FoodData rate limit reached. Try again in {_limiter.wait_time():.0f} seconds."

    try:
        params = _params(query=query, pageSize=page_size)
        if data_type:
            params["dataType"] = data_type

        result = _food_search_cached(query, page_size, data_type)

        foods = result.get("foods", [])
        total = result.get("totalHits", 0)

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

        lines = [f"Found {total} results for '{query}' (showing {len(foods)}):"]
        lines.append("")
        for f in foods:
            fdc_id = f.get("fdcId", "")
            desc = f.get("description", "Unknown")
            dtype = f.get("dataType", "")
            brand = f.get("brandOwner", "")
            label = f"{desc}"
            if brand:
                label += f" ({brand})"
            if dtype:
                label += f" [{dtype}]"
            lines.append(f"  - **{label}** — FDC ID: {fdc_id}")

        lines.append("")
        lines.append("Use `nutrition_lookup` with an FDC ID for full nutrition details.")
        return "\n".join(lines)

    except APIError as e:
        return str(e)
    except Exception as e:
        logger.error("FoodData search error: %s", e)
        return f"Error searching FoodData Central: {e}"


@cached(prefix="fooddata", ttl=86400)
def _food_search_cached(query: str, page_size: int, data_type: str) -> dict:
    params = _params(query=query, pageSize=page_size)
    if data_type:
        params["dataType"] = data_type
    return api_get(
        f"{BASE_URL}/foods/search",
        params=params,
        service_name="USDA FoodData",
    )


def nutrition_lookup(data: dict) -> str:
    """Get detailed nutrition for a food by FDC ID."""
    fdc_id = data.get("fdc_id", "")
    if not fdc_id:
        return "Please provide an FDC ID. Use `food_search` first to find one."

    if not _limiter.acquire():
        return f"USDA FoodData rate limit reached. Try again in {_limiter.wait_time():.0f} seconds."

    try:
        result = _nutrition_cached(str(fdc_id))

        desc = result.get("description", "Unknown")
        dtype = result.get("dataType", "")
        brand = result.get("brandOwner", "")

        lines = [f"**{desc}**"]
        if brand:
            lines[0] += f" ({brand})"
        if dtype:
            lines.append(f"Data type: {dtype}")

        # Serving info
        serving = result.get("servingSize")
        serving_unit = result.get("servingSizeUnit", "")
        household = result.get("householdServingFullText", "")
        if serving:
            lines.append(f"Serving: {serving}{serving_unit}")
        if household:
            lines.append(f"Household serving: {household}")

        # Ingredients
        ingredients = result.get("ingredients", "")
        if ingredients:
            lines.append(f"Ingredients: {ingredients[:200]}")

        # Nutrition — pull key nutrients
        lines.append("")
        lines.append("**Nutrition per 100g:**")

        nutrients = result.get("foodNutrients", [])
        key_nutrients = [
            "Energy",
            "Protein",
            "Total lipid (fat)",
            "Carbohydrate, by difference",
            "Fiber, total dietary",
            "Sugars, total including NLEA",
            "Sodium, Na",
            "Calcium, Ca",
            "Iron, Fe",
            "Vitamin C, total ascorbic acid",
            "Vitamin A, IU",
            "Cholesterol",
        ]

        found = {}
        for n in nutrients:
            name = n.get("nutrient", {}).get("name", n.get("nutrientName", ""))
            if not name:
                continue
            for key in key_nutrients:
                if key.lower() in name.lower():
                    value = n.get("amount", n.get("value", 0))
                    unit = n.get("nutrient", {}).get("unitName", n.get("unitName", ""))
                    found[key] = f"{value} {unit}"
                    break

        for key in key_nutrients:
            if key in found:
                lines.append(f"  {key}: {found[key]}")

        # Label nutrients (branded foods)
        label = result.get("labelNutrients", {})
        if label and not found:
            lines.append("")
            lines.append("**Label nutrition:**")
            for name, info in label.items():
                if isinstance(info, dict):
                    lines.append(f"  {name}: {info.get('value', 0)}")

        return "\n".join(lines)

    except APIError as e:
        return str(e)
    except Exception as e:
        logger.error("FoodData lookup error: %s", e)
        return f"Error looking up nutrition data: {e}"


@cached(prefix="fooddata", ttl=86400)
def _nutrition_cached(fdc_id: str) -> dict:
    return api_get(
        f"{BASE_URL}/food/{fdc_id}",
        params=_params(format="full"),
        service_name="USDA FoodData",
    )


def nutrient_report(data: dict) -> str:
    """Compare nutrition across multiple foods."""
    fdc_ids = data.get("fdc_ids", [])
    if not fdc_ids or not isinstance(fdc_ids, list):
        return "Provide a list of FDC IDs to compare (use `food_search` to find them)."

    if len(fdc_ids) > 20:
        return "Maximum 20 foods per comparison."

    if not _limiter.acquire():
        return f"USDA FoodData rate limit reached. Try again in {_limiter.wait_time():.0f} seconds."

    try:
        result = api_get(
            f"{BASE_URL}/foods",
            params=_params(fdcIds=",".join(str(i) for i in fdc_ids), format="abridged"),
            service_name="USDA FoodData",
        )

        if not isinstance(result, list):
            result = [result] if result else []

        if not result:
            return "No foods found for the given IDs."

        lines = ["**Nutrition Comparison (per 100g):**", ""]

        header = f"{'Food':<40} {'Cal':>6} {'Prot':>6} {'Fat':>6} {'Carb':>6} {'Fiber':>6}"
        lines.append(header)
        lines.append("-" * len(header))

        for food in result:
            desc = food.get("description", "Unknown")[:38]
            nutrients = {
                n.get("nutrientName", n.get("name", "")): n.get("value", n.get("amount", 0))
                for n in food.get("foodNutrients", [])
            }

            cal = _find_nutrient(nutrients, "Energy")
            prot = _find_nutrient(nutrients, "Protein")
            fat = _find_nutrient(nutrients, "Total lipid")
            carb = _find_nutrient(nutrients, "Carbohydrate")
            fiber = _find_nutrient(nutrients, "Fiber")

            lines.append(f"{desc:<40} {cal:>6} {prot:>6} {fat:>6} {carb:>6} {fiber:>6}")

        return "\n".join(lines)

    except APIError as e:
        return str(e)
    except Exception as e:
        logger.error("FoodData report error: %s", e)
        return f"Error generating nutrient report: {e}"


def _find_nutrient(nutrients: dict, keyword: str) -> str:
    for name, value in nutrients.items():
        if keyword.lower() in name.lower():
            if isinstance(value, float):
                return f"{value:.1f}"
            return str(value)
    return "—"


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

TOOLS = [
    {
        "name": "food_search",
        "description": (
            "Search USDA FoodData Central for foods by name."
            " Returns FDC IDs for use with nutrition_lookup."
            " Covers 380K+ foods including branded products."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "Food name to search for (e.g., 'chicken breast', 'olive oil')",
                },
                "limit": {
                    "type": "integer",
                    "description": "Max results (1-25, default 10)",
                    "default": 10,
                },
                "data_type": {
                    "type": "string",
                    "description": (
                        "Filter by type: Branded, Foundation, Survey (FNDDS), SR Legacy."
                        " Leave empty for all types."
                    ),
                    "default": "",
                },
            },
            "required": ["query"],
        },
        "handler": food_search,
        "category": "fooddata",
    },
    {
        "name": "nutrition_lookup",
        "description": (
            "Get detailed nutrition facts for a food by its FDC ID."
            " Returns calories, macros, vitamins, minerals, serving size, and ingredients."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "fdc_id": {
                    "type": "string",
                    "description": "FDC ID from a food_search result",
                },
            },
            "required": ["fdc_id"],
        },
        "handler": nutrition_lookup,
        "category": "fooddata",
    },
    {
        "name": "nutrient_report",
        "description": (
            "Compare nutrition across multiple foods side by side."
            " Provide FDC IDs from food_search results."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "fdc_ids": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": "List of FDC IDs to compare (max 20)",
                },
            },
            "required": ["fdc_ids"],
        },
        "handler": nutrient_report,
        "category": "fooddata",
    },
]
