# Copyright (c) Microsoft. All rights reserved.

from __future__ import annotations

import asyncio
import os
from typing import Annotated, Any, Literal

from agent_framework import Agent, tool
from agent_framework.foundry import FoundryChatClient
from agent_framework_monty import MontyExecuteCodeTool
from azure.identity import AzureCliCredential
from dotenv import load_dotenv

"""This sample demonstrates manual static wiring of Monty CodeAct without a provider.

Instead of using `MontyCodeActProvider` with `context_providers=`, this sample
creates a `MontyExecuteCodeTool` directly, extracts its CodeAct instructions
once, and passes both to the `Agent` constructor at build time.

This avoids the per-run provider lifecycle (`before_run` / `after_run`) and is
well-suited when the tool registry is fixed for the agent's lifetime. The
tradeoff is that dynamic tool changes between runs are not supported - any
mutations to the tool would not update the agent's instructions automatically.

Note: `agent-framework-monty` is an alpha package and is not yet part of
`agent-framework[all]`. Install it explicitly with:

    pip install agent-framework agent-framework-monty --pre
"""

load_dotenv()


@tool(approval_mode="never_require")
def compute(
    operation: Annotated[
        Literal["add", "subtract", "multiply", "divide"],
        "Math operation: add, subtract, multiply, or divide.",
    ],
    a: Annotated[float, "First numeric operand."],
    b: Annotated[float, "Second numeric operand."],
) -> float:
    """Perform a math operation used by sandboxed code."""
    operations = {
        "add": a + b,
        "subtract": a - b,
        "multiply": a * b,
        "divide": a / b if b else float("inf"),
    }
    return operations[operation]


@tool(approval_mode="never_require")
def fetch_data(
    table: Annotated[str, "Name of the simulated table to query."],
) -> list[dict[str, Any]]:
    """Fetch simulated records from a named table."""
    data: dict[str, list[dict[str, Any]]] = {
        "users": [
            {"id": 1, "name": "Alice", "role": "admin"},
            {"id": 2, "name": "Bob", "role": "user"},
            {"id": 3, "name": "Charlie", "role": "admin"},
        ],
        "products": [
            {"id": 101, "name": "Widget", "price": 9.99},
            {"id": 102, "name": "Gadget", "price": 19.99},
        ],
    }
    return data.get(table, [])


@tool(approval_mode="never_require")
def send_email(
    to: Annotated[str, "Recipient email address."],
    subject: Annotated[str, "Email subject line."],
    body: Annotated[str, "Email body text."],
) -> str:
    """Simulate sending an email (direct-only tool, not available inside the sandbox)."""
    return f"Email sent to {to}: {subject}"


async def main() -> None:
    """Run the manual static-wiring Monty sample."""
    # 1. Create the execute_code tool and register sandbox tools on it.
    execute_code = MontyExecuteCodeTool(
        tools=[compute, fetch_data],
        approval_mode="never_require",
    )

    # 2. Build CodeAct instructions once. Setting tools_visible_to_model=False
    #    tells the instructions builder that sandbox tools are not in the agent's
    #    direct tool list, so the model must call them inside execute_code.
    codeact_instructions = execute_code.build_instructions(tools_visible_to_model=False)

    # 3. Create the client and the agent with everything wired at construction time.
    #    - send_email is a direct-only tool (not available inside the sandbox).
    #    - execute_code carries sandbox tools (compute, fetch_data) for Monty.
    agent = Agent(
        client=FoundryChatClient(
            project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
            model=os.environ["FOUNDRY_MODEL"],
            credential=AzureCliCredential(),
        ),
        name="MontyManualWiringAgent",
        instructions=f"You are a helpful assistant.\n\n{codeact_instructions}",
        tools=[send_email, execute_code],
    )

    # 4. Run a request that exercises both the sandbox and the direct tool.
    print("=" * 60)
    print("Manual static-wiring Monty CodeAct sample")
    print("=" * 60)
    query = (
        "Fetch all users, find admins, multiply 6*7, and print the users, admins, "
        "and multiplication result. Use one execute_code call. "
        "Then send an email to admin@example.com summarising the results."
    )
    print(f"User: {query}")
    result = await agent.run(query)
    print(f"Agent: {result.text}")


"""
Sample output (shape only):

============================================================
Manual static-wiring Monty CodeAct sample
============================================================
User: Fetch all users, find admins, multiply 6*7, ...
Agent: ...
"""


if __name__ == "__main__":
    asyncio.run(main())
