# 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

"""
SMS Skill - Iris, Rainbow Messenger

Iris was the personal messenger of Hera, traveling on rainbows between
the mortal and divine realms. She delivers your urgent messages anywhere.

Send text messages via Twilio.

Setup:
1. Get Twilio account: https://www.twilio.com
2. Set environment variables:
   - TWILIO_ACCOUNT_SID
   - TWILIO_AUTH_TOKEN
   - TWILIO_PHONE_NUMBER
"""

import json
import logging
import os
from pathlib import Path

logger = logging.getLogger(__name__)

# Storage paths - use centralized paths


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.paths import DATA_DIR
except ImportError:
    DATA_DIR = _get_data_dir()

CONTACTS_FILE = DATA_DIR / "sms_contacts.json"


def _get_twilio_client():
    """Get Twilio client."""
    try:
        from twilio.rest import Client
    except ImportError:
        from familiar.core.deps import SMS_PACKAGES, ensure_packages

        ok, _ = ensure_packages(SMS_PACKAGES)
        if ok:
            from twilio.rest import Client
        else:
            raise RuntimeError("Twilio setup failed. Check server logs.")

    account_sid = os.environ.get("TWILIO_ACCOUNT_SID")
    auth_token = os.environ.get("TWILIO_AUTH_TOKEN")

    if not account_sid or not auth_token:
        raise RuntimeError(
            "Twilio credentials not set.\n"
            "Set TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN environment variables."
        )

    return Client(account_sid, auth_token)


def _get_phone_number():
    """Get Twilio phone number."""
    number = os.environ.get("TWILIO_PHONE_NUMBER")
    if not number:
        raise RuntimeError("TWILIO_PHONE_NUMBER not set")
    return number


def _load_contacts() -> dict:
    """Load saved contacts."""
    if CONTACTS_FILE.exists():
        try:
            return json.loads(CONTACTS_FILE.read_text())
        except (json.JSONDecodeError, IOError, OSError) as e:
            logger.warning(f"Failed to load SMS contacts: {e}")
    return {}


def _save_contacts(contacts: dict):
    """Save contacts."""
    DATA_DIR.mkdir(parents=True, exist_ok=True)
    CONTACTS_FILE.write_text(json.dumps(contacts, indent=2))


def _normalize_phone(phone: str) -> str:
    """Normalize phone number to E.164 format."""
    # Remove non-digits
    digits = "".join(c for c in phone if c.isdigit())

    # Add country code if needed (assume US)
    if len(digits) == 10:
        digits = "1" + digits

    return "+" + digits


def _lookup_contact(name_or_number: str) -> str:
    """Look up contact by name or return number."""
    contacts = _load_contacts()

    # Check if it's a saved name
    name_lower = name_or_number.lower()
    for name, number in contacts.items():
        if name.lower() == name_lower:
            return number

    # Otherwise treat as phone number
    return _normalize_phone(name_or_number)


def send_sms(data: dict) -> str:
    """Send an SMS message."""
    to = data.get("to", "")
    message = data.get("message", "")

    if not data.get("_confirmed"):
        from familiar.core.confirmations import needs_confirmation

        return needs_confirmation(
            tool_name="send_sms",
            tool_input=data,
            preview=f"Send SMS to {to}: {message[:60]}...",
            risk="medium",
        )

    if not to or not message:
        return "Please provide 'to' (phone number or contact name) and 'message'"

    try:
        client = _get_twilio_client()
        from_number = _get_phone_number()
    except Exception as e:
        logger.error(f"SMS operation failed: {e}")
        return "❌ SMS operation failed. Check Twilio credentials."

    to_number = _lookup_contact(to)

    try:
        msg = client.messages.create(body=message, from_=from_number, to=to_number)

        return f"✅ SMS sent to {to_number}\nMessage ID: {msg.sid}"

    except Exception as e:
        logger.error(f"SMS send failed: {e}")
        return "❌ Failed to send SMS. Check Twilio credentials and phone number format."


def add_contact(data: dict) -> str:
    """Add a contact for easy SMS sending."""
    name = data.get("name", "").strip()
    phone = data.get("phone", "").strip()

    if not name or not phone:
        return "Please provide name and phone number"

    contacts = _load_contacts()
    contacts[name] = _normalize_phone(phone)
    _save_contacts(contacts)

    return f"✅ Saved contact: {name} = {contacts[name]}"


def list_contacts(data: dict) -> str:
    """List saved SMS contacts."""
    contacts = _load_contacts()

    if not contacts:
        return "No contacts saved. Use add_sms_contact to add some."

    lines = ["📱 SMS Contacts:\n"]
    for name, phone in sorted(contacts.items()):
        lines.append(f"  {name}: {phone}")

    return "\n".join(lines)


def remove_contact(data: dict) -> str:
    """Remove a contact."""
    name = data.get("name", "").strip()

    if not data.get("_confirmed"):
        from familiar.core.confirmations import needs_confirmation

        return needs_confirmation(
            tool_name="remove_sms_contact",
            tool_input=data,
            preview=f"Remove SMS contact '{name}'.",
            risk="medium",
        )

    if not name:
        return "Please provide contact name"

    contacts = _load_contacts()

    # Case-insensitive lookup
    for saved_name in list(contacts.keys()):
        if saved_name.lower() == name.lower():
            del contacts[saved_name]
            _save_contacts(contacts)
            return f"✅ Removed contact: {saved_name}"

    return f"Contact not found: {name}"


def get_messages(data: dict) -> str:
    """Get recent SMS messages (sent and received)."""
    limit = data.get("limit", 10)

    try:
        client = _get_twilio_client()
        from_number = _get_phone_number()
    except Exception as e:
        logger.error(f"SMS operation failed: {e}")
        return "❌ SMS operation failed. Check Twilio credentials."

    try:
        # Get recent messages
        messages = client.messages.list(limit=limit)

        if not messages:
            return "No recent messages"

        lines = ["📱 Recent Messages:\n"]

        for msg in messages:
            direction = "→" if msg.from_ == from_number else "←"
            other = msg.to if msg.from_ == from_number else msg.from_
            date = msg.date_sent.strftime("%m/%d %H:%M") if msg.date_sent else ""
            body = msg.body[:50] + "..." if len(msg.body) > 50 else msg.body

            lines.append(f"{direction} {other} ({date})")
            lines.append(f"   {body}")

        return "\n".join(lines)

    except Exception as e:
        logger.error(f"SMS fetch failed: {e}")
        return "❌ Failed to fetch SMS messages. Check Twilio credentials."


# Tool definitions
TOOLS = [
    {
        "name": "send_sms",
        "description": (
            "Send an SMS text message via Twilio."
            " Use only when the user specifically asks for SMS or text."
            " For general 'message someone', use send_message instead."
            " Returns delivery confirmation with Twilio message SID."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "to": {"type": "string", "description": "Phone number or saved contact name"},
                "message": {"type": "string", "description": "Message to send"},
            },
            "required": ["to", "message"],
        },
        "handler": send_sms,
        "requires_confirmation": True,
        "category": "sms",
    },
    {
        "name": "add_sms_contact",
        "description": (
            "Save a contact name and phone number for easy SMS sending."
            " Use when the user wants to store a contact for future texts."
            " Does not send any message; only persists the contact locally."
            " Returns confirmation with the saved name and normalized number."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "name": {"type": "string", "description": "Contact name"},
                "phone": {"type": "string", "description": "Phone number"},
            },
            "required": ["name", "phone"],
        },
        "handler": add_contact,
        "category": "sms",
    },
    {
        "name": "list_sms_contacts",
        "description": (
            "List all saved SMS contacts with their phone numbers."
            " Use when the user wants to see who they can text by name."
            " Read-only: does not modify contacts or send messages."
            " Returns alphabetically sorted list of name-number pairs."
        ),
        "input_schema": {"type": "object", "properties": {}},
        "handler": list_contacts,
        "category": "sms",
    },
    {
        "name": "remove_sms_contact",
        "description": (
            "Remove a saved SMS contact by name."
            " Use when the user wants to delete a contact from the list."
            " Case-insensitive name matching; does not affect Twilio config."
            " Returns confirmation with the removed contact name."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "name": {"type": "string", "description": "Contact name to remove"},
            },
            "required": ["name"],
        },
        "handler": remove_contact,
        "requires_confirmation": True,
        "category": "sms",
    },
    {
        "name": "get_sms_messages",
        "description": (
            "Get recent SMS messages sent and received via Twilio."
            " Use when the user wants to review their text message history."
            " Read-only: does not send or modify any messages."
            " Returns chronological list with direction, contact, date, and body."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "limit": {
                    "type": "integer",
                    "default": 10,
                    "description": "Maximum number of messages to return",
                },
            },
        },
        "handler": get_messages,
        "category": "sms",
    },
]
