"""可选真实模型入口：Claude Messages HTTP + 同一套受控文件工具。

本轮没有执行此程序。读者运行会调用收费 API，只发送教学资料。
未实现流式传输、重试、持久消息历史和硬墙钟取消；请求有 socket 超时，
任务工具预算保存在状态中，模型请求额度也跨恢复累计。
"""
import argparse
import json
import os
import urllib.error
import urllib.request
from instruction_manifest import assemble
from runtime_core import Runtime, TOOLS, ToolRejected

class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        # 认证请求不自动跟随重定向到其他地址。
        return None


def request_model(model, key, messages):
    rules = assemble("order_report")["instructions"]
    body = {
        "model": model, "max_tokens": 1800,
        "system": "\n".join(rules) + "\n先读来源，调用工具写报告。finish 成功后才算完成。",
        "tools": TOOLS, "messages": messages,
    }
    request = urllib.request.Request(
        "https://api.anthropic.com/v1/messages",
        data=json.dumps(body, ensure_ascii=False).encode("utf-8"),
        headers={"content-type": "application/json", "x-api-key": key,
                 "anthropic-version": "2023-06-01"}, method="POST",
    )
    try:
        with urllib.request.build_opener(NoRedirect()).open(request, timeout=30) as response:
            raw = response.read(2_000_001)
    except urllib.error.HTTPError as exc:
        # 不打印认证头和服务器返回的可能敏感全文。
        raise RuntimeError(f"model_http_status_{exc.code}") from None
    except urllib.error.URLError:
        raise RuntimeError("model_connection_failed") from None
    if len(raw) > 2_000_000:
        raise RuntimeError("model_response_too_large")
    return json.loads(raw)


def run(root):
    runtime = Runtime(root)
    if runtime.state["phase"] == "completed":
        print(runtime.inspect_completed())
        return
    key = os.environ.get("ANTHROPIC_API_KEY")
    model = os.environ.get("HARNESS_MODEL")
    if not key or not model:
        raise RuntimeError("set_ANTHROPIC_API_KEY_and_HARNESS_MODEL")
    # 恢复时用可信状态重建小型请求，不宣称恢复完整历史或原模型推理过程。
    messages = [{"role": "user", "content": "为订单 A1042 生成售后报告。当前执行状态："
                 + json.dumps(runtime.state, ensure_ascii=False)
                 + "。若已有候选，先验证；不要无条件覆盖。"}]
    seen = set()
    while runtime.state.get("model_calls", 0) < 8:
        # 调用前持久扣减；网络失败不会让反复启动重新得到无限请求额度。
        runtime.state["model_calls"] = runtime.state.get("model_calls", 0) + 1
        runtime.save()
        response = request_model(model, key, messages)
        content = response.get("content", [])
        calls = [item for item in content if item.get("type") == "tool_use"]
        # 截断输出即使参数碰巧能解析，也不能作为完整动作执行。
        if response.get("stop_reason") != "tool_use" or not calls:
            raise RuntimeError("model_stopped_without_verified_delivery")
        # 保留整份 assistant 内容，随后成批回填对应 ID 的结果。
        messages.append({"role": "assistant", "content": content})
        results = []
        for call in calls:
            call_id = call.get("id")
            if not isinstance(call_id, str) or call_id in seen:
                raise RuntimeError("invalid_or_duplicate_tool_call_id")
            seen.add(call_id)
            try:
                result = runtime.call(call.get("name"), call.get("input"))
                failed = False
            except ToolRejected as exc:
                result, failed = {"error": str(exc)}, True
            results.append({"type": "tool_result", "tool_use_id": call_id,
                            "is_error": failed,
                            "content": json.dumps(result, ensure_ascii=False)})
        messages.append({"role": "user", "content": results})
        if runtime.state["phase"] == "completed":
            # 直接根据可信交付记录回复，不额外请求模型润色“已完成”。
            print(runtime.inspect_completed())
            return
    raise RuntimeError("model_call_budget_exhausted")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("workspace")
    run(parser.parse_args().workspace)
