"""Offline acceptance cases: python -m unittest -v test_refund_workflow.py"""
import json
import tempfile
import unittest
from types import SimpleNamespace
from refund_workflow import Workflow, drain_pending


def action(call="call-1", name="refund_order", order="ORDER-100"):
    return {"type": "function_call", "turn_id": "turn-1", "call_id": call,
            "name": name, "arguments": {"order_id": order}}


class Acceptance(unittest.TestCase):
    def setUp(self):
        self.directory = tempfile.TemporaryDirectory()
        self.path = self.directory.name + "/state.sqlite"
        self.workflow = Workflow(self.path)
        self.workflow.bind("session-1", "demo-user")

    def tearDown(self):
        self.workflow.close()
        self.directory.cleanup()

    def handle(self, value=None):
        return self.workflow.handle("session-1", "demo-user", value or action())

    def approve(self):
        self.assertIsNone(self.handle())
        proposal = self.workflow.pending()[0]
        self.assertEqual(proposal["amount"], 2500)
        self.workflow.decide(proposal["operation"], "demo-user", "approved")

    def test_lookup_includes_server_policy(self):
        result = self.handle(action(name="lookup_order"))
        self.assertEqual(json.loads(result["output"])["paid_cents"], 2500)

    def test_pending_has_no_side_effect(self):
        self.assertIsNone(self.handle())
        self.assertEqual(self.workflow.db.execute("SELECT count(*) FROM refunds").fetchone()[0], 0)

    def test_denial_is_tool_failure(self):
        self.handle()
        self.workflow.decide(self.workflow.pending()[0]["operation"], "demo-user", "denied")
        self.assertFalse(self.handle()["success"])
        self.assertEqual(self.workflow.db.execute("SELECT count(*) FROM refunds").fetchone()[0], 0)

    def test_approved_refund_survives_restart_and_duplicate_calls(self):
        self.approve()
        original = self.handle()
        self.workflow.close()
        self.workflow = Workflow(self.path)
        self.assertEqual(self.handle(), original)
        self.assertTrue(self.handle(action(call="another-call"))["success"])
        self.assertEqual(self.workflow.db.execute("SELECT count(*) FROM refunds").fetchone()[0], 1)

    def test_order_and_session_authorization(self):
        self.assertFalse(self.handle(action(order="OTHER"))["success"])
        with self.assertRaises(ValueError):
            self.workflow.handle("session-1", "attacker", action())
        with self.assertRaises(ValueError):
            self.workflow.bind("session-1", "attacker")

    def test_model_cannot_change_amount_or_approve(self):
        value = action()
        value["arguments"].update(amount=1, approved=True)
        self.assertFalse(self.handle(value)["success"])
        self.assertEqual(self.workflow.pending(), [])

    def test_wrong_approver_and_redecision_rejected(self):
        self.handle()
        key = self.workflow.pending()[0]["operation"]
        with self.assertRaises(ValueError):
            self.workflow.decide(key, "attacker", "approved")
        self.workflow.decide(key, "demo-user", "denied")
        with self.assertRaises(ValueError):
            self.workflow.decide(key, "demo-user", "approved")

    def test_call_identity_conflict_rejected(self):
        self.handle(action(name="lookup_order"))
        with self.assertRaises(ValueError):
            self.handle(action())

    def test_delivery_failure_recovery_uses_stored_result(self):
        self.approve()
        attempts = []
        def send(session, **payload):
            attempts.append(payload)
            if len(attempts) == 1:
                raise TimeoutError("Simulated lost acknowledgment")
        api = SimpleNamespace(beta=SimpleNamespace(agents=SimpleNamespace(sessions=SimpleNamespace(
            retrieve=lambda session: SimpleNamespace(required_actions=[SimpleNamespace(to_dict=action)]),
            events=SimpleNamespace(create=send)))))
        with self.assertRaises(TimeoutError):
            drain_pending(api, self.workflow, "session-1", "demo-user")
        self.workflow.close()
        self.workflow = Workflow(self.path)
        self.assertEqual(drain_pending(api, self.workflow, "session-1", "demo-user")["results_submitted"], 1)
        self.assertEqual(attempts[0], attempts[1])
        self.assertEqual(self.workflow.db.execute("SELECT count(*) FROM refunds").fetchone()[0], 1)


if __name__ == "__main__":
    unittest.main()
