"""
Template for multi-agent collaboration.

This contains a simple example of how to set up a multi-agent collaboration.
The functions are to be filled in with the actual code to run the agents.
"""
from burr import core
from burr.core import ApplicationBuilder, State, action, default
from burr.tracking import client as burr_tclient

# --- Define some tools

tools = []  # these could be functions, etc.

# --- Start defining Action


@action(reads=["query", "messages"], writes=["parsed_tool_calls"])
def some_agent_1(state: State) -> tuple[dict, State]:
    """An agent specializing in some task.

    :param state: state of the application
    :return:
    """
    _query = state["query"]  # noqa:F841
    _message = state["messages"]  # noqa:F841
    # Do LLM call, passing in tool information
    # determine which tool to call
    _result = {"tool_call": "some_tool", "tool_args": {"arg1": "value1"}}
    return _result, state.update(parsed_tool_calls=[_result], sender="some_agent_1")


@action(reads=["query", "messages"], writes=["parsed_tool_calls"])
def some_agent_2(state: State) -> tuple[dict, State]:
    """An agent specializing in some other task.

    :param state: state of the application
    :return:
    """
    _query = state["query"]  # noqa:F841
    _message = state["messages"]  # noqa:F841
    # Do LLM call, passing in tool information
    # determine which tool to call
    _result = {"tool_call": "some_tool2", "tool_args": {"arg1": "value1"}}
    return _result, state.update(parsed_tool_calls=[_result], sender="some_agent_2")


@action(reads=["messages", "parsed_tool_calls"], writes=["messages", "parsed_tool_calls"])
def tool_node(state: State) -> tuple[dict, State]:
    """Given a tool call, execute it and return the result."""
    _messages = state["messages"]  # noqa:F841
    _parsed_tool_calls = state["parsed_tool_calls"]  # noqa:F841
    # execute the tool call
    # get result and create new message
    _tool_results = [{"some": "message", "from": "tool_node"}]
    new_state = state.append(messages=_tool_results)
    new_state = new_state.update(parsed_tool_calls=[])
    # We return a list, because this will get added to the existing list
    return {"tool_results": _tool_results}, new_state


@action(reads=["messages"], writes=[])
def terminal_step(state: State) -> tuple[dict, State]:
    """Terminal step we have here that does nothing, but it could"""
    return {}, state


def default_state_and_entry_point(query: str = None) -> tuple[dict, str]:
    """Returns the default state and entry point for the application."""
    return {
        "messages": [],
        "query": query,
        "sender": "",
        "parsed_tool_calls": [],
    }, "some_agent_1"


def main(query: str = None, app_instance_id: str = None, sequence_number: int = None):
    """Main function to run the application.

    :param query: the query for the agents to run over.
    :param app_instance_id: a prior app instance id to restart from.
    :param sequence_number: a prior sequence number to restart from.
    :return:
    """
    project_name = "template:multi-agent-collaboration"
    if app_instance_id:
        tracker = burr_tclient.LocalTrackingClient(project_name)
        persisted_state = tracker.load("", app_id=app_instance_id, sequence_no=sequence_number)
        if not persisted_state:
            print(f"Warning: No persisted state found for app_id {app_instance_id}.")
            state, entry_point = default_state_and_entry_point(query)
        else:
            state = persisted_state["state"]
            entry_point = persisted_state["position"]
    else:
        state, entry_point = default_state_and_entry_point(query)
    # look up app_id for particular user
    # if None -- then proceed with defaults
    # else load from state, and set entry point
    app = (
        ApplicationBuilder()
        .with_state(**state)
        .with_actions(
            some_agent_1=some_agent_1,
            some_agent_2=some_agent_2,
            tool_node=tool_node,
            terminal=terminal_step,
        )
        .with_transitions(
            ("some_agent_1", "tool_node", core.expr("len(parsed_tool_calls) > 0")),
            (
                "some_agent_1",
                "terminal",
                core.expr("'SOMETHING_IN_MESSAGES' in messages[-1]['content']"),
            ),
            ("some_agent_1", "some_agent_2", default),
            ("some_agent_2", "tool_node", core.expr("len(parsed_tool_calls) > 0")),
            (
                "some_agent_2",
                "terminal",
                core.expr("'SOMETHING_IN_MESSAGES' in messages[-1]['content']"),
            ),
            ("some_agent_2", "some_agent_1", default),
            ("tool_node", "some_agent_1", core.expr("sender == 'some_agent_1'")),
            ("tool_node", "some_agent_2", core.expr("sender == 'some_agent_2'")),
        )
        .with_identifiers(partition_key="demo")
        .with_entrypoint(entry_point)
        .with_tracker(project=project_name)
        .build()
    )
    return app


if __name__ == "__main__":
    # Add an app_id to restart from last sequence in that state
    _app_id = "8458dc58-7b6c-430b-9ab3-23450774f883"
    app = main("some user query", _app_id)
    app.visualize(
        output_file_path="multi_agent_collaboration",
        include_conditions=True,
        view=True,
        format="png",
    )
    # app.run(halt_after=["terminal"])
