"""test_lab.py — `pytest -q` (offline). Success criteria for the MCP + Semantic
Kernel concepts."""
from __future__ import annotations

import pytest

from mcp_mini import MCPClient, MCPServer, build_core_banking_server
from sk_mini import BankingPlugin, Kernel, kernel_function


# --- Semantic Kernel mini ---------------------------------------------------
def test_kernel_registers_and_invokes_plugin():
    k = Kernel()
    k.add_plugin(BankingPlugin({"A1": 100.0}), "banking")
    names = {(f.plugin, f.name) for f in k.functions()}
    assert ("banking", "get_balance") in names
    assert k.invoke("banking", "get_balance", account_id="A1") == "100.00 AED"
    assert k.invoke("banking", "dispute_window_days") == "60"


def test_kernel_function_metadata_is_the_schema():
    assert Kernel().functions() == []                    # empty kernel has no functions
    plug = BankingPlugin({})
    # the @kernel_function decorator attaches the name+description the LLM reads
    assert plug.get_balance._sk_function["description"].startswith("Get the current")


def test_kernel_unknown_function_raises():
    with pytest.raises(KeyError):
        Kernel().invoke("nope", "nope")


# --- MCP mini ---------------------------------------------------------------
def test_mcp_tools_list_and_call():
    client = MCPClient(build_core_banking_server())
    tools = {t["name"] for t in client.list_tools()}
    assert "get_balance" in tools
    res = client.call_tool("get_balance",
                           {"account_id": "AE070331234567890123456", "user": "user_amal"})
    assert res["balance"] == 1250.00


def test_mcp_resources_and_prompts():
    client = MCPClient(build_core_banking_server())
    assert "60 days" in client.read_resource("bank://policies/dispute")
    prompt = client.get_prompt("grounded_answer", {"sources": "[doc:1]"})
    assert "[doc:1]" in prompt and "ONLY" in prompt


def test_mcp_server_enforces_permissions_not_the_prompt():
    client = MCPClient(build_core_banking_server())
    with pytest.raises(RuntimeError):
        client.call_tool("get_balance",
                        {"account_id": "AE070331234567890123456", "user": "mallory"})


def test_mcp_unknown_method_returns_error():
    server = build_core_banking_server()
    resp = server.handle({"jsonrpc": "2.0", "id": 1, "method": "bogus/method"})
    assert "error" in resp


# --- the bridge: MCP tool -> kernel function --------------------------------
def test_mcp_tool_bridged_into_kernel():
    client = MCPClient(build_core_banking_server())
    k = Kernel()
    k.register_function(
        "core_banking", "get_balance", "via MCP",
        lambda account_id, user: client.call_tool(
            "get_balance", {"account_id": account_id, "user": user}))
    out = k.invoke("core_banking", "get_balance",
                   account_id="AE070331234567890123456", user="user_amal")
    assert out["balance"] == 1250.00     # same tool, now used through the kernel
