#!/usr/bin/env python3
"""
Refund & Returns Resolution Agent — runnable demo (Agent Blueprint from AgentKits)

This runs a REAL agent loop: it sends the kit's actual system prompt and a
realistic sample input to the model, prints the structured decision, and then
executes any proposed actions against MOCK tools (labelled [MOCK]).

The model/reasoning is real. The tools are stubs that return fake data and do
NOT touch any real system. Wiring the mock tools to your own integrations
(CRM, database, GitHub, etc.) is the final implementation step — see README.md.

Works with Anthropic OR OpenAI — it auto-detects whichever API key is set.

© 2026 AgentKits · www.agent-kits.com · Licensed CC BY 4.0 (attribution required)
Original blueprint: https://agent-kits.com/kit/refund-returns-resolver

Setup (pick one):
    pip install anthropic   &&  export ANTHROPIC_API_KEY=sk-ant-...
    pip install openai      &&  export OPENAI_API_KEY=sk-...
Then:
    python run.py
"""
import os, json, sys

ANTHROPIC_MODEL = "claude-sonnet-4-6"
OPENAI_MODEL = "gpt-4o"

SYSTEM_PROMPT = """You are a Refund & Returns Resolution Agent. You resolve refund/return requests using the company's policy, approving what qualifies within caps and escalating the rest. You are judged on fair, fast resolution and on never over-refunding, refunding outside policy, leaking data, or missing abuse.

== CORE PRINCIPLES ==
1. Verify, then apply policy. Confirm the order and the requester's ownership of it. Then check eligibility against the policy: return window, item condition, proof required, refund method.
2. Within caps only. Approve refunds only within the policy and the configured auto-approval cap. Over cap, outside window, or non-standard = escalate, don't improvise.
3. Honest and consistent. Give the decision with the policy reason. If denying, explain why and offer any policy-allowed alternative (e.g. store credit). Don't make exceptions you aren't authorized to.

== HARD RULES (NON-NEGOTIABLE) ==
- VERIFY OWNERSHIP: No refund/return action or order detail without confirming the requester owns the order. Mismatch = no action, escalate.
- POLICY + CAP: Auto-approve only within the return window AND at/under the refund cap. Beyond either = escalate.
- NO OVER-REFUND: Never refund more than paid, never refund an already-refunded item, never refund outside policy.
- ABUSE DETECTION: Flag serial refunds, mismatched identity, "refund or I'll chargeback" pressure, and other abuse patterns; escalate rather than auto-approve.
- NO CROSS-CUSTOMER DATA: Never expose or act on another customer's order.
- AUDIT: Log every decision with the policy basis.

== METHOD ==
- Verify order + ownership. Check eligibility vs policy. If eligible and within cap, approve with reason. If not, deny with reason + alternative, or escalate. Flag abuse.

== OUTPUT FORMAT (return ONE JSON object) ==
{
  "order_id": "<id or 'unknown'>",
  "ownership_verified": <bool>,
  "request": "refund|return|exchange",
  "eligibility": { "within_window": <bool>, "condition_ok": <bool>, "within_cap": <bool> },
  "decision": "APPROVE|DENY|ESCALATE",
  "amount": <n|null>,
  "customer_reply": "<decision + policy reason + any alternative>",
  "abuse_flag": { "flag": <bool>, "reason": "<pattern, or empty>" },
  "escalation": { "needed": <bool>, "reason": "<out-of-policy/over-cap/dispute/abuse, or empty>" }
}
Never approve over cap or outside policy. Verify ownership before acting. Flag abuse.
"""

SAMPLE_INPUT = """Verified customer returns a $55 unused item, ordered 10 days ago. Policy: 30-day window, $100 auto cap.
"""

# ---- MOCK tools (stubs — replace with real integrations to deploy) ----
def get_request(args=None):
    print(f"  [MOCK] get_request(" + str(args) + ")")
    return {"status": "mock", "tool": "get_request"}

def verify_order(args=None):
    print(f"  [MOCK] verify_order(" + str(args) + ")")
    return {"status": "mock", "tool": "verify_order"}

def check_policy(args=None):
    print(f"  [MOCK] check_policy(" + str(args) + ")")
    return {"status": "mock", "tool": "check_policy"}

def check_eligibility(args=None):
    print(f"  [MOCK] check_eligibility(" + str(args) + ")")
    return {"status": "mock", "tool": "check_eligibility"}

def process_refund(args=None):
    print(f"  [MOCK] process_refund(" + str(args) + ")")
    return {"status": "mock", "tool": "process_refund"}

def detect_abuse(args=None):
    print(f"  [MOCK] detect_abuse(" + str(args) + ")")
    return {"status": "mock", "tool": "detect_abuse"}

def escalate(args=None):
    print(f"  [MOCK] escalate(" + str(args) + ")")
    return {"status": "mock", "tool": "escalate"}

def log_decision(args=None):
    print(f"  [MOCK] log_decision(" + str(args) + ")")
    return {"status": "mock", "tool": "log_decision"}

TOOLS = {
    "get_request": get_request,
    "verify_order": verify_order,
    "check_policy": check_policy,
    "check_eligibility": check_eligibility,
    "process_refund": process_refund,
    "detect_abuse": detect_abuse,
    "escalate": escalate,
    "log_decision": log_decision,
}


def call_model(system, user):
    """Dispatch to whichever provider's key is configured."""
    if os.environ.get("ANTHROPIC_API_KEY"):
        try:
            from anthropic import Anthropic
        except ImportError:
            sys.exit("Run: pip install anthropic")
        msg = Anthropic().messages.create(
            model=ANTHROPIC_MODEL,
            max_tokens=1600,
            system=system,
            messages=[{"role": "user", "content": user}],
        )
        return "".join(b.text for b in msg.content if getattr(b, "type", "") == "text")
    if os.environ.get("OPENAI_API_KEY"):
        try:
            from openai import OpenAI
        except ImportError:
            sys.exit("Run: pip install openai")
        resp = OpenAI().chat.completions.create(
            model=OPENAI_MODEL,
            max_tokens=1600,
            messages=[
                {"role": "system", "content": system},
                {"role": "user", "content": user},
            ],
        )
        return resp.choices[0].message.content
    sys.exit("Set ANTHROPIC_API_KEY or OPENAI_API_KEY first.")


def extract_json(text):
    a, b = text.find("{"), text.rfind("}")
    if a == -1 or b == -1:
        return None
    try:
        return json.loads(text[a : b + 1])
    except json.JSONDecodeError:
        return None


def main():
    print("=" * 70)
    print("INPUT:\n" + SAMPLE_INPUT)
    print("=" * 70)

    text = call_model(SYSTEM_PROMPT, SAMPLE_INPUT)
    print("AGENT DECISION:\n" + text)
    print("=" * 70)

    data = extract_json(text)
    if not data:
        print("(No JSON object parsed — the model returned prose.)")
        return

    actions = data.get("actions") or []
    if actions:
        print("EXECUTING PROPOSED ACTIONS (mock):")
        for a in actions:
            name = a.get("tool")
            args = a.get("args", {})
            if name in TOOLS:
                TOOLS[name](args)
            else:
                print(f"  [skip] no mock tool for '{name}'")
    else:
        print("This blueprint returned a structured result (no side-effecting "
              "actions in this sample). Available mock tools: "
              + ", ".join(TOOLS.keys()))


if __name__ == "__main__":
    main()
