# 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

"""
Bookkeeping Skill

Track income, expenses, and generate financial records.
Nonprofit-specific: donation receipts, 990-prep data, fund accounting.

Data stored in ~/.familiar/data/bookkeeping.json
Dependencies: None (pure stdlib JSON storage)
"""

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


def _get_data_dir():
    """Get data directory (tenant-scoped in Reflection, default in standalone)."""
    try:
        from familiar.core import paths

        return paths.DATA_DIR
    except ImportError:
        return Path.home() / ".familiar" / "data"


try:
    from familiar.core.utils import atomic_write_json, generate_id, safe_load_json
except ImportError:
    atomic_write_json = None
    safe_load_json = None
    generate_id = None

logger = logging.getLogger(__name__)


def _get_data_file():
    """Get data file path (re-evaluates for tenant scoping)."""
    return _get_data_dir() / "bookkeeping.json"


DATA_FILE = _get_data_file()  # Default for backward compat

_DEFAULT_DATA = {
    "transactions": [],
    "budgets": [],  # {fund, category, amount, period}
    "funds": [  # Seed with standard nonprofit funds
        {"name": "General", "type": "unrestricted"},
    ],
    "categories": {
        "income": [
            "individual_donations",
            "corporate_donations",
            "grants",
            "events",
            "membership_dues",
            "investment_income",
            "other_income",
        ],
        "expense": [
            "salaries",
            "benefits",
            "rent",
            "utilities",
            "supplies",
            "travel",
            "professional_services",
            "insurance",
            "fundraising",
            "program_services",
            "depreciation",
            "other_expense",
        ],
    },
    "version": 1,
}


def _load_data() -> dict:
    if safe_load_json:
        return safe_load_json(
            _get_data_file(), default=lambda: json.loads(json.dumps(_DEFAULT_DATA))
        )
    if _get_data_file().exists():
        try:
            with open(_get_data_file(), encoding="utf-8") as f:
                return json.load(f)
        except (json.JSONDecodeError, IOError, OSError) as e:
            logger.warning(f"Failed to load bookkeeping data: {e}")
    return json.loads(json.dumps(_DEFAULT_DATA))


def _save_data(data: dict):
    if atomic_write_json:
        atomic_write_json(_get_data_file(), data)
    else:
        _get_data_file().parent.mkdir(parents=True, exist_ok=True)
        with open(_get_data_file(), "w", encoding="utf-8") as f:
            json.dump(data, f, indent=2)


def _gen_id() -> str:
    if generate_id:
        return generate_id()
    import secrets

    return secrets.token_hex(4)


def _parse_date(d: str) -> date:
    """Parse flexible date input."""
    for fmt in ("%Y-%m-%d", "%m/%d/%Y", "%m-%d-%Y", "%B %d, %Y", "%b %d, %Y"):
        try:
            return datetime.strptime(d.strip(), fmt).date()
        except ValueError:
            continue
    return datetime.fromisoformat(d.strip()).date()


def _filter_transactions(
    transactions: list,
    start: str = None,
    end: str = None,
    category: str = None,
    fund: str = None,
    tx_type: str = None,
) -> list:
    """Filter transactions by date range, category, fund, and type."""
    results = transactions

    if tx_type:
        results = [t for t in results if t.get("type") == tx_type]

    if category:
        cat_lower = category.lower()
        results = [t for t in results if cat_lower in t.get("category", "").lower()]

    if fund:
        fund_lower = fund.lower()
        results = [t for t in results if fund_lower in t.get("fund", "").lower()]

    if start:
        try:
            start_date = _parse_date(start)
            results = [t for t in results if _parse_date(t.get("date", "2000-01-01")) >= start_date]
        except (ValueError, TypeError):
            pass

    if end:
        try:
            end_date = _parse_date(end)
            results = [t for t in results if _parse_date(t.get("date", "2099-12-31")) <= end_date]
        except (ValueError, TypeError):
            pass

    return results


# === Tool Handlers ===


def log_income(data: dict) -> str:
    """Record income entry (donation, grant payment, event revenue)."""
    amount = data.get("amount")
    if not amount or float(amount) <= 0:
        return "Please provide a positive amount."

    amount = float(amount)
    db = _load_data()

    tx = {
        "id": _gen_id(),
        "type": "income",
        "amount": amount,
        "date": data.get("date", datetime.now().strftime("%Y-%m-%d")),
        "category": data.get("category", "other_income"),
        "fund": data.get("fund", "General"),
        "source": data.get("source", "").strip(),
        "description": data.get("description", "").strip(),
        "donor_id": data.get("donor_id", ""),
        "grant_id": data.get("grant_id", ""),
        "receipt_issued": False,
        "created_at": datetime.now().isoformat(),
    }

    db["transactions"].append(tx)
    _save_data(db)

    source_info = f" from {tx['source']}" if tx["source"] else ""
    fund_info = f" ({tx['fund']} fund)" if tx["fund"] != "General" else ""
    return (
        f"✅ Income logged: ${amount:,.2f}{source_info} [{tx['category']}]{fund_info} [{tx['id']}]"
    )


def log_expense(data: dict) -> str:
    """Record expense entry (vendor, amount, category, receipt reference)."""
    amount = data.get("amount")
    if not amount or float(amount) <= 0:
        return "Please provide a positive amount."

    amount = float(amount)
    db = _load_data()

    tx = {
        "id": _gen_id(),
        "type": "expense",
        "amount": amount,
        "date": data.get("date", datetime.now().strftime("%Y-%m-%d")),
        "category": data.get("category", "other_expense"),
        "fund": data.get("fund", "General"),
        "vendor": data.get("vendor", "").strip(),
        "description": data.get("description", "").strip(),
        "receipt_ref": data.get("receipt_ref", ""),
        "check_number": data.get("check_number", ""),
        "created_at": datetime.now().isoformat(),
    }

    db["transactions"].append(tx)
    _save_data(db)

    vendor_info = f" to {tx['vendor']}" if tx["vendor"] else ""
    return f"✅ Expense logged: ${amount:,.2f}{vendor_info} [{tx['category']}] [{tx['id']}]"


def list_transactions(data: dict) -> str:
    """List transactions with date range, category, fund filters."""
    db = _load_data()

    filtered = _filter_transactions(
        db.get("transactions", []),
        start=data.get("start_date"),
        end=data.get("end_date"),
        category=data.get("category"),
        fund=data.get("fund"),
        tx_type=data.get("type"),
    )

    if not filtered:
        return "No transactions found matching criteria."

    # Sort by date descending
    filtered.sort(key=lambda t: t.get("date", ""), reverse=True)
    limit = min(data.get("limit", 50), 200)
    filtered = filtered[:limit]

    total_income = sum(t["amount"] for t in filtered if t["type"] == "income")
    total_expense = sum(t["amount"] for t in filtered if t["type"] == "expense")

    lines = [f"💰 Transactions ({len(filtered)} shown):\n"]

    for t in filtered:
        sign = "+" if t["type"] == "income" else "-"
        emoji = "📈" if t["type"] == "income" else "📉"
        who = t.get("source") or t.get("vendor") or ""
        who_str = f" {who}" if who else ""
        desc = t.get("description", "")
        desc_str = f" — {desc[:40]}" if desc else ""
        lines.append(
            f"  {emoji} {t['date']} {sign}${t['amount']:,.2f}{who_str} "
            f"[{t['category']}]{desc_str}  [{t['id']}]"
        )

    lines.append(f"\n  Income:  ${total_income:,.2f}")
    lines.append(f"  Expense: ${total_expense:,.2f}")
    lines.append(f"  Net:     ${total_income - total_expense:,.2f}")

    return "\n".join(lines)


def budget_status(data: dict) -> str:
    """Show budget vs. actual by category for a given period."""
    db = _load_data()
    fund_filter = data.get("fund", "").strip()
    period = data.get("period", datetime.now().strftime("%Y"))

    # Parse period: "2025", "2025-Q1", "2025-01"
    year = period[:4] if len(period) >= 4 else datetime.now().strftime("%Y")

    if "-Q" in period:
        quarter = int(period.split("-Q")[1])
        start_month = (quarter - 1) * 3 + 1
        end_month = start_month + 2
        start = f"{year}-{start_month:02d}-01"
        if end_month == 12:
            end = f"{year}-12-31"
        else:
            end = f"{year}-{end_month + 1:02d}-01"
    elif len(period) == 7:  # "2025-01"
        start = f"{period}-01"
        month = int(period.split("-")[1])
        if month == 12:
            end = f"{year}-12-31"
        else:
            end = f"{year}-{month + 1:02d}-01"
    else:
        start = f"{year}-01-01"
        end = f"{year}-12-31"

    transactions = _filter_transactions(
        db.get("transactions", []), start=start, end=end, fund=fund_filter if fund_filter else None
    )

    # Aggregate actuals by category
    actuals = {}
    for t in transactions:
        cat = t.get("category", "other")
        tx_type = t["type"]
        key = f"{tx_type}:{cat}"
        actuals[key] = actuals.get(key, 0) + t["amount"]

    # Load budgets for this period/fund
    budgets = {}
    for b in db.get("budgets", []):
        if fund_filter and fund_filter.lower() not in b.get("fund", "").lower():
            continue
        if b.get("period", "") == period or b.get("period", "").startswith(year):
            key = f"{b.get('type', 'expense')}:{b['category']}"
            budgets[key] = budgets.get(key, 0) + b.get("amount", 0)

    fund_str = f" ({fund_filter})" if fund_filter else ""
    lines = [f"📊 Budget Status: {period}{fund_str}\n"]

    if not budgets and not actuals:
        lines.append("  No budget data or transactions found for this period.")
        lines.append("  Set budgets by adding entries to bookkeeping.json budgets array.")
        return "\n".join(lines)

    # Income section
    income_cats = sorted(
        set(
            k.split(":")[1]
            for k in list(actuals.keys()) + list(budgets.keys())
            if k.startswith("income:")
        )
    )
    if income_cats:
        lines.append("  INCOME:")
        for cat in income_cats:
            key = f"income:{cat}"
            actual = actuals.get(key, 0)
            budget = budgets.get(key, 0)
            pct = f" ({actual / budget * 100:.0f}%)" if budget > 0 else ""
            budget_str = f" / ${budget:,.2f}" if budget > 0 else ""
            lines.append(f"    {cat}: ${actual:,.2f}{budget_str}{pct}")

    # Expense section
    expense_cats = sorted(
        set(
            k.split(":")[1]
            for k in list(actuals.keys()) + list(budgets.keys())
            if k.startswith("expense:")
        )
    )
    if expense_cats:
        lines.append("\n  EXPENSES:")
        for cat in expense_cats:
            key = f"expense:{cat}"
            actual = actuals.get(key, 0)
            budget = budgets.get(key, 0)
            pct = f" ({actual / budget * 100:.0f}%)" if budget > 0 else ""
            budget_str = f" / ${budget:,.2f}" if budget > 0 else ""
            over = " ⚠️ OVER" if budget > 0 and actual > budget else ""
            lines.append(f"    {cat}: ${actual:,.2f}{budget_str}{pct}{over}")

    # Totals
    total_income = sum(v for k, v in actuals.items() if k.startswith("income:"))
    total_expense = sum(v for k, v in actuals.items() if k.startswith("expense:"))
    lines.append(f"\n  Total Income:  ${total_income:,.2f}")
    lines.append(f"  Total Expense: ${total_expense:,.2f}")
    lines.append(f"  Net:           ${total_income - total_expense:,.2f}")

    return "\n".join(lines)


def generate_receipt(data: dict) -> str:
    """Create IRS-compliant donation receipt (integrates with documents + contacts skills)."""
    donor_name = data.get("donor_name", "").strip()
    amount = data.get("amount")
    transaction_id = data.get("transaction_id", "").strip()

    # If transaction_id provided, look up details
    if transaction_id:
        db = _load_data()
        tx = None
        for t in db["transactions"]:
            if t["id"] == transaction_id:
                tx = t
                break
        if not tx:
            return f"Transaction {transaction_id} not found."
        amount = tx["amount"]
        donor_name = donor_name or tx.get("source", "Donor")
        date_of_gift = tx.get("date", datetime.now().strftime("%Y-%m-%d"))
    else:
        if not donor_name or not amount:
            return "Please provide donor_name and amount, or a transaction_id."
        date_of_gift = data.get("date_of_gift", datetime.now().strftime("%Y-%m-%d"))

    # Try to use documents skill for formatted receipt
    try:
        from familiar.skills.documents.skill import create_receipt as doc_receipt

        result = doc_receipt(
            {
                "donor_name": donor_name,
                "amount": float(amount),
                "date_of_gift": date_of_gift,
                "description": data.get("description", "Cash/check donation"),
                "format": data.get("format", "pdf"),
            }
        )

        # Mark transaction as receipt issued
        if transaction_id:
            db = _load_data()
            for t in db["transactions"]:
                if t["id"] == transaction_id:
                    t["receipt_issued"] = True
                    t["receipt_date"] = datetime.now().isoformat()
                    break
            _save_data(db)

        return result

    except (ImportError, Exception) as e:
        logger.info(f"Documents skill not available for receipt: {e}")

    # Fallback: text receipt
    receipt_text = (
        f"DONATION RECEIPT\n"
        f"{'=' * 40}\n\n"
        f"Donor: {donor_name}\n"
        f"Amount: ${float(amount):,.2f}\n"
        f"Date: {date_of_gift}\n"
        f"Description: {data.get('description', 'Cash/check donation')}\n\n"
        f"No goods or services were provided in exchange for this contribution.\n"
        f"Please retain for your tax records.\n"
    )

    # Mark receipt issued
    if transaction_id:
        db = _load_data()
        for t in db["transactions"]:
            if t["id"] == transaction_id:
                t["receipt_issued"] = True
                t["receipt_date"] = datetime.now().isoformat()
                break
        _save_data(db)

    return f"✅ Receipt generated (text format):\n\n{receipt_text}"


def financial_summary(data: dict) -> str:
    """Generate income/expense summary for a period."""
    db = _load_data()
    start = data.get("start_date", f"{datetime.now().year}-01-01")
    end = data.get("end_date", datetime.now().strftime("%Y-%m-%d"))

    transactions = _filter_transactions(db.get("transactions", []), start=start, end=end)

    if not transactions:
        return f"No transactions found between {start} and {end}."

    # Aggregate by category and fund
    income_by_cat = {}
    expense_by_cat = {}
    income_by_fund = {}
    expense_by_fund = {}

    for t in transactions:
        cat = t.get("category", "other")
        fund = t.get("fund", "General")
        amt = t["amount"]

        if t["type"] == "income":
            income_by_cat[cat] = income_by_cat.get(cat, 0) + amt
            income_by_fund[fund] = income_by_fund.get(fund, 0) + amt
        else:
            expense_by_cat[cat] = expense_by_cat.get(cat, 0) + amt
            expense_by_fund[fund] = expense_by_fund.get(fund, 0) + amt

    total_income = sum(income_by_cat.values())
    total_expense = sum(expense_by_cat.values())

    lines = [f"💰 Financial Summary: {start} to {end}\n"]

    lines.append("  INCOME:")
    for cat, amt in sorted(income_by_cat.items(), key=lambda x: -x[1]):
        pct = amt / total_income * 100 if total_income > 0 else 0
        lines.append(f"    {cat}: ${amt:,.2f} ({pct:.1f}%)")
    lines.append(f"    TOTAL: ${total_income:,.2f}")

    lines.append("\n  EXPENSES:")
    for cat, amt in sorted(expense_by_cat.items(), key=lambda x: -x[1]):
        pct = amt / total_expense * 100 if total_expense > 0 else 0
        lines.append(f"    {cat}: ${amt:,.2f} ({pct:.1f}%)")
    lines.append(f"    TOTAL: ${total_expense:,.2f}")

    lines.append(f"\n  NET: ${total_income - total_expense:,.2f}")

    if len(income_by_fund) > 1 or len(expense_by_fund) > 1:
        lines.append("\n  BY FUND:")
        all_funds = sorted(set(list(income_by_fund.keys()) + list(expense_by_fund.keys())))
        for fund in all_funds:
            inc = income_by_fund.get(fund, 0)
            exp = expense_by_fund.get(fund, 0)
            lines.append(f"    {fund}: +${inc:,.2f} / -${exp:,.2f} = ${inc - exp:,.2f}")

    lines.append(f"\n  Transactions: {len(transactions)}")

    return "\n".join(lines)


def fund_balances(data: dict) -> str:
    """Show balances by restricted/unrestricted fund."""
    db = _load_data()

    # Calculate running balance per fund from all transactions
    balances = {}
    for t in db.get("transactions", []):
        fund = t.get("fund", "General")
        if fund not in balances:
            balances[fund] = {"income": 0, "expense": 0}
        balances[fund][t["type"]] += t["amount"]

    if not balances:
        return "No transactions recorded. Fund balances are zero."

    # Get fund metadata
    fund_meta = {f["name"]: f for f in db.get("funds", [])}

    lines = ["🏦 Fund Balances:\n"]

    total_balance = 0
    unrestricted_total = 0
    restricted_total = 0

    for fund_name in sorted(balances.keys()):
        b = balances[fund_name]
        balance = b["income"] - b["expense"]
        total_balance += balance

        meta = fund_meta.get(fund_name, {})
        fund_type = meta.get("type", "unrestricted")
        type_label = f" ({fund_type})" if fund_type != "unrestricted" else ""

        if "restricted" in fund_type.lower():
            restricted_total += balance
        else:
            unrestricted_total += balance

        lines.append(f"  {fund_name}{type_label}: ${balance:,.2f}")
        lines.append(f"    Income: ${b['income']:,.2f}  |  Expenses: ${b['expense']:,.2f}")

    lines.append(f"\n  Unrestricted Total: ${unrestricted_total:,.2f}")
    if restricted_total != 0:
        lines.append(f"  Restricted Total:   ${restricted_total:,.2f}")
    lines.append(f"  Grand Total:        ${total_balance:,.2f}")

    return "\n".join(lines)


def export_for_990(data: dict) -> str:
    """Export financial data in format suitable for IRS Form 990 preparation."""
    db = _load_data()
    year = data.get("year", str(datetime.now().year))

    transactions = _filter_transactions(
        db.get("transactions", []), start=f"{year}-01-01", end=f"{year}-12-31"
    )

    if not transactions:
        return f"No transactions found for {year}."

    # Part I: Revenue
    revenue_cats = {}
    for t in transactions:
        if t["type"] == "income":
            cat = t.get("category", "other_income")
            revenue_cats[cat] = revenue_cats.get(cat, 0) + t["amount"]

    # Part II: Expenses (functional allocation)
    expense_cats = {}
    for t in transactions:
        if t["type"] == "expense":
            cat = t.get("category", "other_expense")
            expense_cats[cat] = expense_cats.get(cat, 0) + t["amount"]

    total_revenue = sum(revenue_cats.values())
    total_expenses = sum(expense_cats.values())

    # 990 mapping
    mapping_revenue = {
        "individual_donations": "Part I, Line 1a: Federated campaigns / direct contributions",
        "corporate_donations": "Part I, Line 1b: Membership dues",
        "grants": "Part I, Line 1c: Government grants",
        "events": "Part I, Line 8a: Gross receipts from events",
        "investment_income": "Part I, Line 10: Investment income",
        "other_income": "Part I, Line 11: Other revenue",
    }

    mapping_expense = {
        "salaries": "Part IX, Line 5: Compensation of current officers",
        "benefits": "Part IX, Line 8: Pension and other employee benefits",
        "rent": "Part IX, Line 16: Occupancy",
        "professional_services": "Part IX, Line 11: Fees for services",
        "travel": "Part IX, Line 17: Travel",
        "supplies": "Part IX, Line 15: Office expenses",
        "insurance": "Part IX, Line 18: Insurance",
        "depreciation": "Part IX, Line 22: Depreciation",
    }

    lines = [f"📋 IRS Form 990 Data Export: {year}\n"]

    lines.append("REVENUE (Part I):")
    for cat, amt in sorted(revenue_cats.items(), key=lambda x: -x[1]):
        mapping = mapping_revenue.get(cat, "[Map to appropriate 990 line]")
        lines.append(f"  {cat}: ${amt:,.2f}")
        lines.append(f"    → {mapping}")
    lines.append(f"  TOTAL REVENUE: ${total_revenue:,.2f}")

    lines.append("\nEXPENSES (Part IX):")
    for cat, amt in sorted(expense_cats.items(), key=lambda x: -x[1]):
        mapping = mapping_expense.get(cat, "[Map to appropriate 990 line]")
        lines.append(f"  {cat}: ${amt:,.2f}")
        lines.append(f"    → {mapping}")
    lines.append(f"  TOTAL EXPENSES: ${total_expenses:,.2f}")

    lines.append(f"\n  NET ASSETS CHANGE: ${total_revenue - total_expenses:,.2f}")
    lines.append(f"\n  Total transactions: {len(transactions)}")
    lines.append(
        "\n  ⚠️ This is a data extract, not a completed Form 990. "
        "Consult your accountant or tax preparer for final filing."
    )

    # Also save as JSON for programmatic use
    export_data = {
        "year": year,
        "revenue": revenue_cats,
        "expenses": expense_cats,
        "total_revenue": total_revenue,
        "total_expenses": total_expenses,
        "net": total_revenue - total_expenses,
        "transaction_count": len(transactions),
        "exported_at": datetime.now().isoformat(),
    }

    export_path = Path.home() / ".familiar" / "documents" / f"990_export_{year}.json"
    export_path.parent.mkdir(parents=True, exist_ok=True)
    with open(export_path, "w", encoding="utf-8") as f:
        json.dump(export_data, f, indent=2)

    lines.append(f"\n  JSON export saved: {export_path}")

    return "\n".join(lines)


# === Tool Definitions ===

TOOLS = [
    {
        "name": "log_income",
        "description": (
            "Record an income entry such as a donation, grant payment, or event revenue. "
            "Use this to log money received with fund allocation and category classification. "
            "Supports linking to donor and grant records from other skills. "
            "Returns a confirmation with the transaction ID for later reference."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "amount": {"type": "number", "description": "Income amount"},
                "category": {
                    "type": "string",
                    "enum": [
                        "individual_donations",
                        "corporate_donations",
                        "grants",
                        "events",
                        "membership_dues",
                        "investment_income",
                        "other_income",
                    ],
                    "default": "other_income",
                },
                "fund": {
                    "type": "string",
                    "default": "General",
                    "description": "Fund name (General, Restricted, etc.)",
                },
                "source": {"type": "string", "description": "Source/donor name"},
                "description": {"type": "string", "description": "Description of income"},
                "date": {"type": "string", "description": "Date (YYYY-MM-DD, default: today)"},
                "donor_id": {"type": "string", "description": "Contact ID of donor"},
                "grant_id": {"type": "string", "description": "Grant ID (from nonprofit skill)"},
            },
            "required": ["amount"],
        },
        "handler": log_income,
        "category": "bookkeeping",
    },
    {
        "name": "log_expense",
        "description": (
            "Record an expense entry with vendor, amount, category, and optional receipt reference. "
            "Use this to log organizational expenditures for fund accounting and 990 preparation. "
            "Supports fund allocation for restricted vs. unrestricted spending. "
            "Returns a confirmation with the transaction ID for later reference."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "amount": {"type": "number", "description": "Expense amount"},
                "category": {
                    "type": "string",
                    "enum": [
                        "salaries",
                        "benefits",
                        "rent",
                        "utilities",
                        "supplies",
                        "travel",
                        "professional_services",
                        "insurance",
                        "fundraising",
                        "program_services",
                        "depreciation",
                        "other_expense",
                    ],
                    "default": "other_expense",
                },
                "fund": {
                    "type": "string",
                    "default": "General",
                    "description": "Fund name (General, Restricted, etc.)",
                },
                "vendor": {"type": "string", "description": "Vendor/payee name"},
                "description": {"type": "string", "description": "Description of the expense"},
                "date": {"type": "string", "description": "Date (YYYY-MM-DD, default: today)"},
                "receipt_ref": {"type": "string", "description": "Receipt or invoice reference"},
                "check_number": {"type": "string", "description": "Check number if applicable"},
            },
            "required": ["amount"],
        },
        "handler": log_expense,
        "category": "bookkeeping",
    },
    {
        "name": "list_transactions",
        "description": (
            "List financial transactions with optional date range, category, fund, and type filters. "
            "Use this to review income and expenses for a period or audit specific categories. "
            "Results are sorted by date descending with running income/expense/net totals. "
            "Returns transaction details including amounts, sources, vendors, and IDs."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "start_date": {"type": "string", "description": "Start date (YYYY-MM-DD)"},
                "end_date": {"type": "string", "description": "End date (YYYY-MM-DD)"},
                "category": {"type": "string", "description": "Filter by category"},
                "fund": {"type": "string", "description": "Filter by fund name"},
                "type": {
                    "type": "string",
                    "enum": ["income", "expense"],
                    "description": "Filter by type",
                },
                "limit": {"type": "integer", "default": 50, "description": "Max results"},
            },
        },
        "handler": list_transactions,
        "category": "bookkeeping",
    },
    {
        "name": "budget_status",
        "description": "Show budget vs. actual by category for a given period (year, quarter, or month)",
        "input_schema": {
            "type": "object",
            "properties": {
                "period": {
                    "type": "string",
                    "description": "Period: '2025', '2025-Q1', or '2025-01'",
                },
                "fund": {"type": "string", "description": "Filter by fund name"},
            },
        },
        "handler": budget_status,
        "category": "bookkeeping",
    },
    {
        "name": "generate_receipt",
        "description": "Create IRS-compliant donation receipt. Integrates with documents and contacts skills.",
        "input_schema": {
            "type": "object",
            "properties": {
                "donor_name": {"type": "string", "description": "Donor full name"},
                "amount": {"type": "number", "description": "Donation amount"},
                "transaction_id": {
                    "type": "string",
                    "description": "Transaction ID (auto-fills details)",
                },
                "date_of_gift": {"type": "string", "description": "Date of gift (YYYY-MM-DD)"},
                "description": {"type": "string", "default": "Cash/check donation"},
                "format": {"type": "string", "enum": ["pdf", "docx", "text"], "default": "pdf"},
            },
        },
        "handler": generate_receipt,
        "category": "bookkeeping",
    },
    {
        "name": "financial_summary",
        "description": "Generate income/expense summary for a period, broken down by category and fund",
        "input_schema": {
            "type": "object",
            "properties": {
                "start_date": {
                    "type": "string",
                    "description": "Start date (YYYY-MM-DD, default: Jan 1 this year)",
                },
                "end_date": {
                    "type": "string",
                    "description": "End date (YYYY-MM-DD, default: today)",
                },
            },
        },
        "handler": financial_summary,
        "category": "bookkeeping",
    },
    {
        "name": "fund_balances",
        "description": "Show balances by restricted/unrestricted fund",
        "input_schema": {"type": "object", "properties": {}},
        "handler": fund_balances,
        "category": "bookkeeping",
    },
    {
        "name": "export_for_990",
        "description": "Export financial data in format suitable for IRS Form 990 preparation",
        "input_schema": {
            "type": "object",
            "properties": {
                "year": {"type": "string", "description": "Tax year (default: current year)"}
            },
        },
        "handler": export_for_990,
        "category": "bookkeeping",
    },
]
