# 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

"""
CiviCRM Donor Management Skill

Manage contacts, donations, memberships, and events via self-hosted CiviCRM.

Setup:
    CIVICRM_URL=https://your-site.org
    CIVICRM_API_KEY=your_api_key
    CIVICRM_SITE_KEY=your_site_key

API: CiviCRM API v3 REST interface
"""

import logging

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

logger = logging.getLogger(__name__)


def _base_url() -> str:
    return (get_env("CIVICRM_URL") or "").rstrip("/")


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


def _site_key() -> str:
    return get_env("CIVICRM_SITE_KEY") or ""


def _check_config() -> str:
    if not _base_url():
        return "CiviCRM not configured.\nSet CIVICRM_URL, CIVICRM_API_KEY, and CIVICRM_SITE_KEY."
    if not _api_key() or not _site_key():
        return "CiviCRM API key and site key are required."
    return ""


def _api_params(entity: str, action: str) -> dict:
    """Build base API params for CiviCRM REST API v3."""
    return {
        "entity": entity,
        "action": action,
        "api_key": _api_key(),
        "key": _site_key(),
        "json": "1",
    }


def _rest_url() -> str:
    return f"{_base_url()}/sites/default/files/civicrm/extern/rest.php"


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


def crm_contacts(data: dict) -> str:
    """List or search CiviCRM contacts."""
    err = _check_config()
    if err:
        return err

    search = data.get("search", "").strip()
    contact_type = data.get("contact_type", "Individual")
    limit = min(int(data.get("limit", 25)), 100)

    try:
        params = _api_params("Contact", "get")
        params["rowCount"] = str(limit)
        params["return"] = "id,display_name,email,phone,contact_type"
        if search:
            params["display_name"] = search
        if contact_type:
            params["contact_type"] = contact_type

        result = _contacts_cached(search, contact_type, limit)

        contacts = result.get("values", [])
        if isinstance(contacts, dict):
            contacts = list(contacts.values())
        count = result.get("count", len(contacts))

        if not contacts:
            return "No contacts found."

        lines = [f"**CRM Contacts** ({count} found):", ""]

        for i, c in enumerate(contacts[:limit], 1):
            name = c.get("display_name", "Unknown")
            email = c.get("email", "")
            phone = c.get("phone", "")
            cid = c.get("id", c.get("contact_id", ""))

            lines.append(f"{i}. **{name}** (ID: {cid})")
            details = []
            if email:
                details.append(email)
            if phone:
                details.append(phone)
            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("CiviCRM contacts error: %s", e)
        return f"Error fetching contacts: {e}"


@cached(prefix="civicrm_contacts", ttl=120)
def _contacts_cached(search: str, contact_type: str, limit: int) -> dict:
    params = _api_params("Contact", "get")
    params["rowCount"] = str(limit)
    params["return"] = "id,display_name,email,phone,contact_type"
    if search:
        params["display_name"] = search
    if contact_type:
        params["contact_type"] = contact_type
    return api_get(
        _rest_url(),
        params=params,
        service_name="CiviCRM",
    )


def crm_donations(data: dict) -> str:
    """List recent donations/contributions."""
    err = _check_config()
    if err:
        return err

    contact_id = data.get("contact_id", "")
    limit = min(int(data.get("limit", 25)), 100)

    try:
        result = _donations_cached(contact_id, limit)

        contributions = result.get("values", [])
        if isinstance(contributions, dict):
            contributions = list(contributions.values())

        if not contributions:
            return "No donations found."

        lines = ["**Recent Donations:**", ""]

        for i, c in enumerate(contributions[:limit], 1):
            amount = c.get("total_amount", "0")
            currency = c.get("currency", "USD")
            date = c.get("receive_date", "")[:10]
            status = c.get("contribution_status", "")
            source = c.get("source", "")
            donor = c.get("display_name", c.get("sort_name", ""))

            lines.append(f"{i}. **${float(amount):,.2f} {currency}**")
            details = []
            if donor:
                details.append(donor)
            if date:
                details.append(date)
            if status:
                details.append(status)
            if source:
                details.append(source)
            lines.append(f"   {' | '.join(details)}")
            lines.append("")

        return "\n".join(lines)

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


@cached(prefix="civicrm_donations", ttl=120)
def _donations_cached(contact_id: str, limit: int) -> dict:
    params = _api_params("Contribution", "get")
    params["rowCount"] = str(limit)
    params["return"] = "id,total_amount,currency,receive_date,contribution_status,source,sort_name"
    params["options"] = '{"sort":"receive_date DESC"}'
    if contact_id:
        params["contact_id"] = contact_id
    return api_get(
        _rest_url(),
        params=params,
        service_name="CiviCRM",
    )


def crm_add_donation(data: dict) -> str:
    """Log a new donation/contribution."""
    err = _check_config()
    if err:
        return err

    contact_id = data.get("contact_id", "").strip()
    if not contact_id:
        return "Please provide a contact_id for the donor."

    amount = data.get("amount", 0)
    if not amount or float(amount) <= 0:
        return "Please provide a valid donation amount."

    source = data.get("source", "").strip()
    financial_type = data.get("financial_type_id", "1")

    try:
        params = _api_params("Contribution", "create")
        params["contact_id"] = contact_id
        params["total_amount"] = str(float(amount))
        params["financial_type_id"] = financial_type
        params["contribution_status_id"] = "1"
        if source:
            params["source"] = source

        result = api_get(
            _rest_url(),
            params=params,
            service_name="CiviCRM",
        )

        if result.get("is_error"):
            return f"CiviCRM error: {result.get('error_message', 'Unknown error')}"

        values = result.get("values", {})
        if isinstance(values, dict):
            values = list(values.values())
        contrib = values[0] if values else {}
        contrib_id = contrib.get("id", "")

        return (
            f"**Donation logged!**\n\n"
            f"  Contribution ID: {contrib_id}\n"
            f"  Contact ID: {contact_id}\n"
            f"  Amount: ${float(amount):,.2f}\n"
            f"  Status: Completed"
        )

    except APIError as e:
        return str(e)
    except Exception as e:
        logger.error("CiviCRM add donation error: %s", e)
        return f"Error logging donation: {e}"


def crm_events(data: dict) -> str:
    """List upcoming CiviCRM events."""
    err = _check_config()
    if err:
        return err

    limit = min(int(data.get("limit", 10)), 50)

    try:
        result = _events_cached(limit)

        events = result.get("values", [])
        if isinstance(events, dict):
            events = list(events.values())

        if not events:
            return "No upcoming events found."

        lines = ["**Upcoming Events:**", ""]

        for i, e in enumerate(events[:limit], 1):
            title = e.get("title", e.get("event_title", "Untitled"))
            start = e.get("start_date", "")[:10]
            end = e.get("end_date", "")[:10]
            event_type = e.get("event_type", "")

            lines.append(f"{i}. **{title}**")
            details = []
            if start:
                date_str = start
                if end and end != start:
                    date_str += f" – {end}"
                details.append(date_str)
            if event_type:
                details.append(event_type)
            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("CiviCRM events error: %s", e)
        return f"Error fetching events: {e}"


@cached(prefix="civicrm_events", ttl=300)
def _events_cached(limit: int) -> dict:
    params = _api_params("Event", "get")
    params["rowCount"] = str(limit)
    params["return"] = "id,title,start_date,end_date,event_type,is_active"
    params["is_active"] = "1"
    params["options"] = '{"sort":"start_date ASC"}'
    return api_get(
        _rest_url(),
        params=params,
        service_name="CiviCRM",
    )


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

TOOLS = [
    {
        "name": "crm_contacts",
        "description": (
            "List or search CiviCRM contacts (donors, members, volunteers)."
            " Shows name, email, phone."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "search": {
                    "type": "string",
                    "description": "Search by name",
                },
                "contact_type": {
                    "type": "string",
                    "description": "Type: Individual, Organization, Household",
                    "default": "Individual",
                },
                "limit": {
                    "type": "integer",
                    "description": "Max results (1-100, default 25)",
                    "default": 25,
                },
            },
        },
        "handler": crm_contacts,
        "category": "civicrm",
    },
    {
        "name": "crm_donations",
        "description": ("List recent donations/contributions from CiviCRM. Filter by contact ID."),
        "input_schema": {
            "type": "object",
            "properties": {
                "contact_id": {
                    "type": "string",
                    "description": "Filter by donor contact ID",
                },
                "limit": {
                    "type": "integer",
                    "description": "Max results (1-100, default 25)",
                    "default": 25,
                },
            },
        },
        "handler": crm_donations,
        "category": "civicrm",
    },
    {
        "name": "crm_add_donation",
        "description": ("Log a new donation in CiviCRM. Requires contact ID and amount."),
        "input_schema": {
            "type": "object",
            "properties": {
                "contact_id": {
                    "type": "string",
                    "description": "Donor's CiviCRM contact ID",
                },
                "amount": {
                    "type": "number",
                    "description": "Donation amount",
                },
                "source": {
                    "type": "string",
                    "description": "Donation source (e.g., 'Online', 'Gala')",
                },
            },
            "required": ["contact_id", "amount"],
        },
        "handler": crm_add_donation,
        "category": "civicrm",
    },
    {
        "name": "crm_events",
        "description": "List upcoming CiviCRM events with dates and types.",
        "input_schema": {
            "type": "object",
            "properties": {
                "limit": {
                    "type": "integer",
                    "description": "Max results (1-50, default 10)",
                    "default": 10,
                },
            },
        },
        "handler": crm_events,
        "category": "civicrm",
    },
]
