"""AI Ping offline tool-request lab, fictional data, 2026-10-28.

The recorded text proposals came from Claude Sonnet 5.5. This editor-written
runner validates and replays them locally. It does not call an LLM API, email,
reservation system or database. All draft state disappears on process exit.
"""
import json

STOCK = {"B17": 2, "B22": 0}
CHECKED_AT = "2026-10-28T10:00:00+09:00"


class ReviewRequired(ValueError):
    pass


def parse_request(raw):
    if not isinstance(raw, str) or len(raw) > 4000:
        raise ReviewRequired("request_too_large")
    try:
        value = json.loads(raw)
    except (json.JSONDecodeError, TypeError) as error:
        raise ReviewRequired("invalid_json") from error
    if not isinstance(value, dict) or set(value) != {"name", "arguments"}:
        raise ReviewRequired("wrong_fields")
    name, args = value["name"], value["arguments"]
    if not isinstance(name, str) or not isinstance(args, dict):
        raise ReviewRequired("wrong_types")
    expected = {"lookup_stock": {"book_id"}, "create_draft": {"request_id", "book_id", "quantity", "note"}}
    if name not in expected or set(args) != expected[name]:
        raise ReviewRequired("unknown_tool_or_fields")
    if not isinstance(args["book_id"], str) or args["book_id"] not in STOCK:
        raise ReviewRequired("unknown_book")
    if name == "create_draft":
        if not isinstance(args["request_id"], str) or not args["request_id"] or len(args["request_id"]) > 40:
            raise ReviewRequired("invalid_request_id")
        if type(args["quantity"]) is not int or not 1 <= args["quantity"] <= 3:
            raise ReviewRequired("invalid_quantity")
        if not isinstance(args["note"], str) or not args["note"].strip() or len(args["note"]) > 300:
            raise ReviewRequired("invalid_note")
    return value


class DraftLab:
    def __init__(self):
        self.drafts = {}
        self.lookups = {}
        self.attempts = 0
        self.max_attempts = 4

    def execute(self, request, *, lose_reply=False):
        # Revalidate even callers supplying a Python dict.
        request = parse_request(json.dumps(request, ensure_ascii=False))
        if self.attempts >= self.max_attempts:
            raise ReviewRequired("attempt_limit")
        self.attempts += 1
        name, args = request["name"], request["arguments"]
        if name == "lookup_stock":
            result = {"book_id": args["book_id"], "available": STOCK[args["book_id"]], "checked_at": CHECKED_AT}
            self.lookups[args["book_id"]] = result
            return result
        key = args["request_id"]
        if key in self.drafts:
            saved = self.drafts[key]
            if saved["arguments"] != args:
                raise ReviewRequired("request_id_conflict")
            return saved["result"]
        stock = self.lookups.get(args["book_id"])
        if stock is None:
            raise ReviewRequired("lookup_required")
        if stock["available"] < args["quantity"]:
            raise ReviewRequired("insufficient_stock")
        result = {"draft_id": f"DRAFT-{key}", "status": "draft"}
        self.drafts[key] = {"arguments": dict(args), "result": result}
        if lose_reply:
            # Deliberate teaching fault: draft exists, but its reply is lost.
            raise TimeoutError("simulated_reply_lost_after_create")
        return result


# Actual model proposals are filled from the recorded conversation by the editor.
RECORDED_PROPOSALS = ['```json\n{\n  "name": "lookup_stock",\n  "arguments": {\n    "book_id": "B17"\n  }\n}\n```\n\n재고 조회 결과가 아직 없으므로, 읽기 전용인 `lookup_stock`을 먼저 호출합니다. `create_draft`는 `available`이 수량 2권을 충족한다는 결과를 확인한 뒤에만 `request_id: "R101"`, `book_id: "B17"`, `quantity: 2`로 제안하는 것이 안전합니다.', '{"name":"lookup_stock","arguments":{"book_id":"B17"}}', '{"name":"create_draft","arguments":{"request_id":"R101","book_id":"B17","quantity":2,"note":"재고조회 시점(2026-10-28T10:00:00+09:00) 기준 2권 가용, 예약 미확정. 담당자 검토용 초안."}}', '{"name":"create_draft","arguments":{"request_id":"R101","book_id":"B17","quantity":2,"note":"재고조회 시점(2026-10-28T10:00:00+09:00) 기준 2권 가용, 예약 미확정. 담당자 검토용 초안."}}']


def run_demo():
    lab = DraftLab()
    events = []
    for index, raw in enumerate(RECORDED_PROPOSALS):
        try:
            request = parse_request(raw)
            result = lab.execute(request, lose_reply=index == 2)
            events.append({"turn": index + 1, "result": result})
        except (ReviewRequired, TimeoutError) as error:
            events.append({"turn": index + 1, "review": str(error)})
    return {"events": events, "tool_attempts": lab.attempts, "draft_count": len(lab.drafts)}


if __name__ == "__main__":
    print(json.dumps(run_demo(), ensure_ascii=False, indent=2))
