"""Outbound voice calls via Twilio REST API."""

import os
from base64 import b64encode

import httpx

TWILIO_API = "https://api.twilio.com/2010-04-01"


def _config() -> tuple[str, str, str]:
    """Return (account_sid, auth_token, phone_number) or raise."""
    sid = os.environ.get("TWILIO_ACCOUNT_SID")
    token = os.environ.get("TWILIO_AUTH_TOKEN")
    phone = os.environ.get("TWILIO_PHONE_NUMBER")

    if not sid or not token:
        raise ValueError(
            "TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN not set. "
            "Get them at https://console.twilio.com"
        )
    if not phone:
        raise ValueError(
            "TWILIO_PHONE_NUMBER not set. "
            "Buy a voice-capable number at https://console.twilio.com/phone-numbers"
        )
    return sid, token, phone


def _auth_header(sid: str, token: str) -> dict:
    """Build HTTP Basic Auth header for Twilio."""
    creds = b64encode(f"{sid}:{token}".encode()).decode()
    return {"Authorization": f"Basic {creds}"}


async def handler(params: dict) -> dict:
    """Handle voice call operations."""
    action = params["action"]
    sid, token, phone = _config()

    try:
        async with httpx.AsyncClient(timeout=30) as client:
            if action == "make_call":
                return await _make_call(client, sid, token, phone, params)
            elif action == "call_status":
                return await _call_status(client, sid, token, params)
            else:
                return {"success": False, "message": f"Unknown action: {action}", "data": {}}
    except ValueError:
        raise
    except httpx.HTTPStatusError as e:
        error_body = e.response.text[:500]
        return {"success": False, "message": f"Twilio API error ({e.response.status_code}): {error_body}", "data": {}}
    except Exception as e:
        return {"success": False, "message": f"Error: {e}", "data": {}}


async def _make_call(
    client: httpx.AsyncClient, sid: str, token: str, from_number: str, params: dict
) -> dict:
    to = params.get("to")
    message = params.get("message")
    if not to or not message:
        raise ValueError("'to' and 'message' are required for make_call")

    voice = params.get("voice", "alice")
    if voice not in ("alice", "man", "woman"):
        voice = "alice"

    # Build TwiML inline
    twiml = f'<Response><Say voice="{voice}">{_escape_xml(message)}</Say></Response>'

    resp = await client.post(
        f"{TWILIO_API}/Accounts/{sid}/Calls.json",
        headers=_auth_header(sid, token),
        data={
            "To": to,
            "From": from_number,
            "Twiml": twiml,
        },
    )
    resp.raise_for_status()
    data = resp.json()

    return {
        "success": True,
        "message": f"Call initiated to {to}",
        "data": {
            "call_sid": data.get("sid"),
            "status": data.get("status"),
            "from": data.get("from"),
            "to": data.get("to"),
        },
    }


async def _call_status(client: httpx.AsyncClient, sid: str, token: str, params: dict) -> dict:
    call_sid = params.get("call_sid")
    if not call_sid:
        raise ValueError("'call_sid' is required for call_status")

    resp = await client.get(
        f"{TWILIO_API}/Accounts/{sid}/Calls/{call_sid}.json",
        headers=_auth_header(sid, token),
    )
    resp.raise_for_status()
    data = resp.json()

    return {
        "success": True,
        "message": f"Call {call_sid}: {data.get('status')}",
        "data": {
            "call_sid": data.get("sid"),
            "status": data.get("status"),
            "duration": data.get("duration"),
            "direction": data.get("direction"),
            "from": data.get("from"),
            "to": data.get("to"),
            "start_time": data.get("start_time"),
            "end_time": data.get("end_time"),
        },
    }


def _escape_xml(text: str) -> str:
    """Escape special characters for TwiML XML."""
    return (
        text.replace("&", "&amp;")
        .replace("<", "&lt;")
        .replace(">", "&gt;")
        .replace('"', "&quot;")
        .replace("'", "&apos;")
    )
