# 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

"""
Grants.gov Search Skill

Search federal grants, view details, and track deadlines.
Free API — no authentication required for basic search.

API: https://api.grants.gov/
Docs: https://api.grants.gov/docs
"""

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__)

BASE_URL = "https://api.grants.gov"


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


def _headers() -> dict:
    h = {"Content-Type": "application/json"}
    key = _api_key()
    if key:
        h["X-Api-Key"] = key
    return h


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


def search_grants(data: dict) -> str:
    """Search for federal grants by keyword, agency, or category."""
    keyword = data.get("keyword", "").strip()
    agency = data.get("agency", "").strip()
    category = data.get("category", "").strip()

    if not keyword and not agency and not category:
        return "Please provide a keyword, agency, or category to search."

    try:
        body = {"paging": {"num": min(int(data.get("limit", 10)), 25), "offset": 0}}
        criteria = {}
        if keyword:
            criteria["keyword"] = keyword
        if agency:
            criteria["agency"] = agency
        if category:
            criteria["fundingCategory"] = category
        body["criteria"] = criteria

        result = api_post(
            f"{BASE_URL}/v1/api/search2",
            json=body,
            headers=_headers(),
            service_name="Grants.gov",
        )

        # Handle nested response
        search_data = result.get("data", result)
        opportunities = search_data.get("oppHits", [])
        total = search_data.get("totalCount", len(opportunities))

        if not opportunities:
            return f"No grants found for '{keyword or agency or category}'."

        lines = [f"**Federal Grants** ({total} total, showing {len(opportunities)}):", ""]

        for i, opp in enumerate(opportunities, 1):
            title = opp.get("title", "Untitled")
            opp_number = opp.get("number", opp.get("id", ""))
            agency_name = opp.get("agency", "")
            close_date = opp.get("closeDate", "")
            award_floor = opp.get("awardFloor", 0)
            award_ceiling = opp.get("awardCeiling", 0)

            lines.append(f"{i}. **{title}**")
            details = []
            if opp_number:
                details.append(f"#{opp_number}")
            if agency_name:
                details.append(agency_name)
            if close_date:
                details.append(f"Closes: {close_date}")
            lines.append(f"   {' | '.join(details)}")

            if award_ceiling:
                if award_floor:
                    lines.append(f"   Award: ${award_floor:,.0f} – ${award_ceiling:,.0f}")
                else:
                    lines.append(f"   Award up to: ${award_ceiling:,.0f}")
            lines.append("")

        return "\n".join(lines)

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


def grant_details(data: dict) -> str:
    """Get detailed information about a specific grant opportunity."""
    opp_id = data.get("opportunity_id", "").strip()
    if not opp_id:
        return "Please provide an opportunity_id."

    try:
        result = _grant_details_cached(opp_id)

        opp = result.get("data", result)
        title = opp.get("title", opp.get("opportunityTitle", ""))
        number = opp.get("number", opp.get("opportunityNumber", ""))
        agency = opp.get("agency", opp.get("agencyName", ""))
        description = opp.get("description", opp.get("synopsis", ""))
        close_date = opp.get("closeDate", opp.get("closeDate", ""))
        post_date = opp.get("postDate", opp.get("postedDate", ""))
        award_floor = opp.get("awardFloor", 0)
        award_ceiling = opp.get("awardCeiling", 0)
        category = opp.get("fundingCategory", opp.get("categoryOfFundingActivity", ""))
        eligible = opp.get("eligibleApplicants", "")
        url = opp.get("url", "")

        lines = [
            f"**{title}**",
            "",
        ]
        if number:
            lines.append(f"  Opportunity #: {number}")
        if agency:
            lines.append(f"  Agency: {agency}")
        if category:
            lines.append(f"  Category: {category}")
        if post_date:
            lines.append(f"  Posted: {post_date}")
        if close_date:
            lines.append(f"  Deadline: {close_date}")
        if award_ceiling:
            if award_floor:
                lines.append(f"  Award Range: ${award_floor:,.0f} – ${award_ceiling:,.0f}")
            else:
                lines.append(f"  Award up to: ${award_ceiling:,.0f}")
        if eligible:
            lines.append(f"  Eligible: {eligible}")
        if url:
            lines.append(f"  URL: {url}")
        if description:
            desc_text = description[:500]
            if len(description) > 500:
                desc_text += "..."
            lines.append(f"\n  {desc_text}")

        return "\n".join(lines)

    except APIError as e:
        return str(e)
    except Exception as e:
        logger.error("Grants.gov details error: %s", e)
        return f"Error fetching grant details: {e}"


@cached(prefix="grants_gov_detail", ttl=3600)
def _grant_details_cached(opp_id: str) -> dict:
    return api_get(
        f"{BASE_URL}/v1/api/fetchOppDetails/{opp_id}",
        headers=_headers(),
        service_name="Grants.gov",
    )


def grant_deadlines(data: dict) -> str:
    """Find grants with upcoming deadlines."""
    category = data.get("category", "").strip()
    days_ahead = int(data.get("days_ahead", 30))

    try:
        body = {
            "paging": {"num": min(int(data.get("limit", 15)), 25), "offset": 0},
            "criteria": {"sortBy": "closeDate"},
        }
        if category:
            body["criteria"]["fundingCategory"] = category

        result = api_post(
            f"{BASE_URL}/v1/api/search2",
            json=body,
            headers=_headers(),
            service_name="Grants.gov",
        )

        search_data = result.get("data", result)
        opportunities = search_data.get("oppHits", [])

        if not opportunities:
            return "No upcoming grant deadlines found."

        lines = [f"**Upcoming Grant Deadlines** (next {days_ahead} days):", ""]

        for i, opp in enumerate(opportunities, 1):
            title = opp.get("title", "Untitled")
            close_date = opp.get("closeDate", "N/A")
            agency = opp.get("agency", "")
            award_ceiling = opp.get("awardCeiling", 0)

            lines.append(f"{i}. **{close_date}** — {title}")
            details = []
            if agency:
                details.append(agency)
            if award_ceiling:
                details.append(f"Up to ${award_ceiling:,.0f}")
            if details:
                lines.append(f"   {' | '.join(details)}")
            lines.append("")

        return "\n".join(lines)

    except APIError as e:
        return str(e)
    except Exception as e:
        logger.error("Grants.gov deadlines error: %s", e)
        return f"Error fetching deadlines: {e}"


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

TOOLS = [
    {
        "name": "search_grants",
        "description": (
            "Search Grants.gov for federal funding opportunities."
            " Filter by keyword, agency, or funding category."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "keyword": {
                    "type": "string",
                    "description": "Search keyword (e.g., 'community health')",
                },
                "agency": {
                    "type": "string",
                    "description": "Agency filter (e.g., 'HHS', 'USDA')",
                },
                "category": {
                    "type": "string",
                    "description": "Funding category (e.g., 'health', 'education')",
                },
                "limit": {
                    "type": "integer",
                    "description": "Max results (1-25, default 10)",
                    "default": 10,
                },
            },
        },
        "handler": search_grants,
        "category": "grants_gov",
    },
    {
        "name": "grant_details",
        "description": (
            "Get detailed information about a specific federal grant opportunity."
            " Shows description, eligibility, deadlines, and award amounts."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "opportunity_id": {
                    "type": "string",
                    "description": "Grant opportunity ID",
                },
            },
            "required": ["opportunity_id"],
        },
        "handler": grant_details,
        "category": "grants_gov",
    },
    {
        "name": "grant_deadlines",
        "description": (
            "Find grants with upcoming deadlines. Sorted by close date. Filter by funding category."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "category": {
                    "type": "string",
                    "description": "Funding category filter",
                },
                "days_ahead": {
                    "type": "integer",
                    "description": "Look ahead days (default 30)",
                    "default": 30,
                },
                "limit": {
                    "type": "integer",
                    "description": "Max results (1-25, default 15)",
                    "default": 15,
                },
            },
        },
        "handler": grant_deadlines,
        "category": "grants_gov",
    },
]
