"""Charter 0.6.0: 架空の注文に対する権限点検。外部HTTPはrespxで置き換える。"""
from typing import Annotated
from importlib.metadata import version
import httpx
import respx
from pydantic import BaseModel, model_validator
from charter import FromContext, Path, ContextError, ToolValidationError, api_key_tool_factory, format_egress_map

# 教材専用の架空データ。本番では認証済みセッションとDBで照合する。
OWNERS = {"order-a": "customer-a", "order-b": "customer-b"}

class GetOrder(BaseModel):
    order_id: Annotated[str, Path()]
    customer_id: Annotated[str, FromContext()]

    @model_validator(mode="after")
    def check_owner(self):
        if OWNERS.get(self.order_id) != self.customer_id:
            raise ValueError("この顧客の注文ではありません")
        return self

shop = api_key_tool_factory(pack="demo", base_url="https://shop.example.invalid/", api_key_headers={"X-Demo-Key": "dummy-not-a-secret"})
get_order = shop(name="order_get", description="本人の注文を確認", method="GET", url_template="orders/{order_id}", args_schema=GetOrder)

def main():
    assert version("charter-ai") == "0.6.0"
    schema = get_order.to_json_schema()["parameters"]["properties"]
    assert "customer_id" not in schema
    print("PASS: モデルの入力欄にcustomer_idがない")
    cases = [
        ("本人の注文", {"order_id": "order-a"}, {"customer_id": "customer-a"}, None),
        ("別顧客の注文", {"order_id": "order-b"}, {"customer_id": "customer-a"}, ToolValidationError),
        ("顧客IDの偽装", {"order_id": "order-b", "customer_id": "customer-b"}, {"customer_id": "customer-a"}, ToolValidationError),
        ("アプリが顧客IDを渡し忘れ", {"order_id": "order-a"}, {}, ContextError),
        ("存在しない注文", {"order_id": "missing"}, {"customer_id": "customer-a"}, ToolValidationError),
    ]
    with respx.mock(assert_all_called=False, assert_all_mocked=True) as mock:
        route = mock.get("https://shop.example.invalid/orders/order-a").mock(return_value=httpx.Response(200, json={"item": "架空のノート", "status": "発送準備中"}))
        for label, args, context, expected_error in cases:
            before = len(mock.calls)
            try:
                get_order.invoke(args, context=context)
            except (ToolValidationError, ContextError) as exc:
                assert expected_error is not None and isinstance(exc, expected_error), (label, exc)
                assert len(mock.calls) == before, label
                print(f"PASS: {label} → 送信前に拒否 ({type(exc).__name__})")
            else:
                assert expected_error is None, label
                assert len(mock.calls) == before + 1, label
                print(f"PASS: {label} → モックに1回到達")
        assert route.call_count == 1
        request = route.calls.last.request
        assert "customer-a" not in str(request.url) + str(request.headers) + request.content.decode()
        print("PASS: 判定専用の顧客IDはHTTPに含まれない")
    print(format_egress_map([get_order]))
    print("完了: 7項目確認。実API・LLMには未接続。")

if __name__ == "__main__":
    main()
