# 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

"""
ProPublica Nonprofit Explorer Skill

Search nonprofits, view 990 filings, and compare organizations.
Free API — no authentication required.

API: https://projects.propublica.org/nonprofits/api/v2/
"""

import logging

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

logger = logging.getLogger(__name__)

BASE_URL = "https://projects.propublica.org/nonprofits/api/v2"


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


def nonprofit_search(data: dict) -> str:
    """Search for nonprofits by name or EIN."""
    query = data.get("query", "").strip()
    if not query:
        return "Please provide a search query (name or EIN)."

    state = data.get("state", "").strip().upper()
    ntee_code = data.get("ntee_code", "").strip()

    try:
        params = {"q": query}
        if state:
            params["state[id]"] = state
        if ntee_code:
            params["ntee[id]"] = ntee_code

        result = _search_cached(query, state, ntee_code)

        orgs = result.get("organizations", [])
        total = result.get("total_results", len(orgs))

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

        lines = [f"**Nonprofit Search: '{query}'** ({total} results):", ""]

        for i, org in enumerate(orgs[:15], 1):
            name = org.get("name", "Unknown")
            ein = org.get("ein", "")
            city = org.get("city", "")
            org_state = org.get("state", "")
            ntee = org.get("ntee_code", "")
            income = org.get("income_amount", 0)
            assets = org.get("asset_amount", 0)

            location = f"{city}, {org_state}" if city else org_state

            lines.append(f"{i}. **{name}** (EIN: {ein})")
            details = []
            if location:
                details.append(location)
            if ntee:
                details.append(f"NTEE: {ntee}")
            if income:
                details.append(f"Income: ${income:,.0f}")
            if assets:
                details.append(f"Assets: ${assets:,.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("ProPublica search error: %s", e)
        return f"Error searching nonprofits: {e}"


@cached(prefix="propublica_search", ttl=3600)
def _search_cached(query: str, state: str, ntee_code: str) -> dict:
    params = {"q": query}
    if state:
        params["state[id]"] = state
    if ntee_code:
        params["ntee[id]"] = ntee_code
    return api_get(
        f"{BASE_URL}/search.json",
        params=params,
        service_name="ProPublica",
    )


def nonprofit_990(data: dict) -> str:
    """Get 990 filing data for a nonprofit by EIN."""
    ein = data.get("ein", "").strip().replace("-", "")
    if not ein:
        return "Please provide an EIN (Employer Identification Number)."

    if not ein.isdigit() or len(ein) != 9:
        return "Invalid EIN format. Must be 9 digits (e.g., 131624100)."

    try:
        result = _org_cached(ein)

        org = result.get("organization", {})
        filings = result.get("filings_with_data", result.get("filings", []))

        name = org.get("name", "Unknown")
        city = org.get("city", "")
        state = org.get("state", "")
        ntee = org.get("ntee_code", "")
        subsection = org.get("subsection_code", "")
        ruling_date = org.get("ruling_date", "")

        lines = [
            f"**{name}** (EIN: {ein})",
            "",
        ]
        location = f"{city}, {state}" if city else state
        if location:
            lines.append(f"  Location: {location}")
        if ntee:
            lines.append(f"  NTEE Code: {ntee}")
        if subsection:
            lines.append(f"  Tax Status: 501(c)({subsection})")
        if ruling_date:
            lines.append(f"  Ruling Date: {ruling_date}")

        if filings:
            lines.append("")
            lines.append("**Recent 990 Filings:**")
            lines.append("")

            for f in filings[:5]:
                tax_year = f.get("tax_prd_yr", f.get("tax_period", ""))
                total_revenue = f.get("totrevenue", f.get("total_revenue", 0))
                total_expenses = f.get("totfuncexpns", f.get("total_expenses", 0))
                net_assets = f.get("totassetsend", f.get("total_assets", 0))
                pdf_url = f.get("pdf_url", "")

                lines.append(f"  **{tax_year}:**")
                if total_revenue:
                    lines.append(f"    Revenue: ${total_revenue:,.0f}")
                if total_expenses:
                    lines.append(f"    Expenses: ${total_expenses:,.0f}")
                if net_assets:
                    lines.append(f"    Net Assets: ${net_assets:,.0f}")
                if pdf_url:
                    lines.append(f"    990 PDF: {pdf_url}")
                lines.append("")
        else:
            lines.append("\n  No 990 filings with data available.")

        return "\n".join(lines)

    except APIError as e:
        return str(e)
    except Exception as e:
        logger.error("ProPublica 990 error: %s", e)
        return f"Error fetching 990 data: {e}"


@cached(prefix="propublica_org", ttl=3600)
def _org_cached(ein: str) -> dict:
    return api_get(
        f"{BASE_URL}/organizations/{ein}.json",
        service_name="ProPublica",
    )


def compare_nonprofits(data: dict) -> str:
    """Compare two nonprofits by EIN side-by-side."""
    ein1 = data.get("ein1", "").strip().replace("-", "")
    ein2 = data.get("ein2", "").strip().replace("-", "")

    if not ein1 or not ein2:
        return "Please provide two EINs to compare (ein1 and ein2)."

    try:
        result1 = _org_cached(ein1)
        result2 = _org_cached(ein2)

        org1 = result1.get("organization", {})
        org2 = result2.get("organization", {})

        filings1 = result1.get("filings_with_data", [])
        filings2 = result2.get("filings_with_data", [])

        name1 = org1.get("name", ein1)
        name2 = org2.get("name", ein2)

        lines = [
            f"**Comparison: {name1} vs {name2}**",
            "",
            f"| Metric | {name1[:25]} | {name2[:25]} |",
            "|--------|" + "-" * 27 + "|" + "-" * 27 + "|",
        ]

        # Location
        loc1 = f"{org1.get('city', '')}, {org1.get('state', '')}".strip(", ")
        loc2 = f"{org2.get('city', '')}, {org2.get('state', '')}".strip(", ")
        lines.append(f"| Location | {loc1} | {loc2} |")

        # NTEE
        lines.append(
            f"| NTEE Code | {org1.get('ntee_code', 'N/A')} | {org2.get('ntee_code', 'N/A')} |"
        )

        # Latest filing data
        f1 = filings1[0] if filings1 else {}
        f2 = filings2[0] if filings2 else {}

        rev1 = f1.get("totrevenue", 0)
        rev2 = f2.get("totrevenue", 0)
        exp1 = f1.get("totfuncexpns", 0)
        exp2 = f2.get("totfuncexpns", 0)
        assets1 = f1.get("totassetsend", 0)
        assets2 = f2.get("totassetsend", 0)

        lines.append(f"| Revenue | ${rev1:,.0f} | ${rev2:,.0f} |")
        lines.append(f"| Expenses | ${exp1:,.0f} | ${exp2:,.0f} |")
        lines.append(f"| Net Assets | ${assets1:,.0f} | ${assets2:,.0f} |")

        if rev1 and exp1:
            efficiency1 = (1 - exp1 / rev1) * 100 if rev1 > 0 else 0
        else:
            efficiency1 = 0
        if rev2 and exp2:
            efficiency2 = (1 - exp2 / rev2) * 100 if rev2 > 0 else 0
        else:
            efficiency2 = 0
        lines.append(f"| Surplus % | {efficiency1:.1f}% | {efficiency2:.1f}% |")

        return "\n".join(lines)

    except APIError as e:
        return str(e)
    except Exception as e:
        logger.error("ProPublica compare error: %s", e)
        return f"Error comparing nonprofits: {e}"


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

TOOLS = [
    {
        "name": "nonprofit_search",
        "description": (
            "Search for nonprofits by name, EIN, state, or NTEE code."
            " Returns name, location, income, and assets."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "Search query (name or EIN)",
                },
                "state": {
                    "type": "string",
                    "description": "State abbreviation filter (e.g., 'NY')",
                },
                "ntee_code": {
                    "type": "string",
                    "description": "NTEE code filter (e.g., 'P' for human services)",
                },
            },
            "required": ["query"],
        },
        "handler": nonprofit_search,
        "category": "propublica_nonprofit",
    },
    {
        "name": "nonprofit_990",
        "description": (
            "Get 990 filing data for a nonprofit by EIN."
            " Shows revenue, expenses, assets, and PDF links."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "ein": {
                    "type": "string",
                    "description": "9-digit EIN (e.g., 131624100)",
                },
            },
            "required": ["ein"],
        },
        "handler": nonprofit_990,
        "category": "propublica_nonprofit",
    },
    {
        "name": "compare_nonprofits",
        "description": (
            "Compare two nonprofits side-by-side by EIN."
            " Shows revenue, expenses, assets, and efficiency."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "ein1": {
                    "type": "string",
                    "description": "First nonprofit EIN",
                },
                "ein2": {
                    "type": "string",
                    "description": "Second nonprofit EIN",
                },
            },
            "required": ["ein1", "ein2"],
        },
        "handler": compare_nonprofits,
        "category": "propublica_nonprofit",
    },
]
