# Turn one CRM event into a contact update and a call, safely repeatable. Python 3.9+ with requests.
#   CRM_EVENT_FILE=event.json AGENT_ID=agent_... python crm-event-to-call.py
# Every request runs, so before calling, the script looks for a call already placed for this
# CRM event (by the crm_event_id in metadata): a CRM that fires the same event twice, or a
# re-run after a timeout, never places a second call.
import json
import os
import sys
from datetime import datetime, timedelta, timezone

import requests

base = os.environ.get("RELATIONSAI_API_BASE", "https://voice-api.relationsai.com/v1")
# Defaults to the test-mode Sample agent; set AGENT_ID to use your own agent.
agent_id = os.environ.get("AGENT_ID", "agent_1ZltiWzxrXmMprGD6q63")
# A fictional CRM webhook payload; replace with what your CRM sends.
if os.environ.get("CRM_EVENT_FILE"):
    with open(os.environ["CRM_EVENT_FILE"]) as f:
        crm = json.load(f)
else:
    crm = {"event_id": "E-9001", "record_id": "L-1042", "phone": "+12025550100", "first_name": "Asha", "do_not_call": False}


auth = {"Authorization": f"Bearer {os.environ['RELATIONSAI_API_KEY']}"}


def post(path, body):
    r = requests.post(f"{base}{path}", headers=auth, json=body, timeout=30)
    return r, r.json()


# 1. Compliance first: a do-not-call flag in the CRM goes to the do-not-call list, and nothing is dialled.
if crm.get("do_not_call"):
    r, body = post("/dnc-entries", {"phone_number": crm["phone"]})
    if not r.ok:
        sys.exit(f"{body['error']['code']} (request {body['error']['request_id']})")
    print(f"{crm['record_id']}: added to the do-not-call list, not calling")
    sys.exit(0)

# 2. Upsert the contact. Send consent only as the CRM records it: true, or false for a withdrawal.
contact = {"phone_number": crm["phone"], "name": crm["first_name"]}
if isinstance(crm.get("consent"), bool):
    contact["consent_granted"] = crm["consent"]
r, upserted = post("/contacts/upsert", contact)
if not r.ok:
    sys.exit(f"{upserted['error']['code']} (request {upserted['error']['request_id']})")

# 3. Already called for this CRM event? Look at the calls to this number in the last day.
since = (datetime.now(timezone.utc) - timedelta(days=1)).isoformat(timespec="milliseconds").replace("+00:00", "Z")
listed = requests.get(f"{base}/calls", params={"to": crm["phone"], "created_after": since, "limit": 100}, headers=auth, timeout=30)
recent = listed.json()
if not listed.ok:
    sys.exit(f"{recent['error']['code']} (request {recent['error']['request_id']})")
existing = next((c for c in recent["data"] if c["metadata"].get("crm_event_id") == crm["event_id"]), None)
if existing:
    print(f"{crm['record_id']}: already called for {crm['event_id']} ({existing['id']} {existing['status']}), not calling again")
    sys.exit(0)

# 4. Place the call. metadata comes back on the call and in every call event.
r, call = post(
    "/calls",
    {
        "agent_id": agent_id,
        "to": crm["phone"],
        "variables": {"first_name": crm["first_name"]},
        "metadata": {"crm_record_id": crm["record_id"], "crm_event_id": crm["event_id"]},
    },
)
if not r.ok:
    # A refusal (retry "no") is a business outcome: record it on the CRM record instead of retrying.
    print(f"{crm['record_id']}: not called: {call['error']['code']} (retry={call['error']['retry']})")
    sys.exit(0 if call["error"]["retry"] == "no" else 1)
print(f"{crm['record_id']}: contact {upserted['id']}, call {call['id']} {call['status']}")
