"""
Template for a multi-modal agent.

The idea is that this whole application functions as an agent that can do
multi-modal things.

Fill in the functions, and adjust/create new actions as needed.
"""
from typing import Tuple

from burr import tracking
from burr.core import Application, ApplicationBuilder, State, default, when
from burr.core.action import action


@action(reads=[], writes=["chat_history", "query"])
def apply_input_query(state: State, user_query: str) -> Tuple[dict, State]:
    # this is a simple example of how you might process a user query -- stick it directly into the chat history
    result = {"chat_item": {"role": "user", "content": user_query, "type": "text"}}
    new_state = state.append(chat_history=result["chat_item"]).update(query=user_query)
    return result, new_state


@action(reads=["prompt"], writes=["mode"])
def choose_mode(state: State) -> Tuple[dict, State]:
    """Code to choose a mode"""
    result = {"mode": "example"}
    return result, state.update(**result)


@action(reads=[], writes=["response"])
def unknown_action(state: State) -> Tuple[dict, State]:
    result = {
        "response": {
            "content": "None of the response modes I support apply to your question. Please clarify?",
            "type": "text",
            "role": "assistant",
        }
    }
    return result, state.update(**result)


@action(reads=["query", "chat_history", "mode"], writes=["response"])
def some_action(state: State) -> Tuple[dict, State]:
    """An example action -- create more of these to handle different modes"""
    _query = state["query"]  # noqa:F841
    _mode = state["mode"]  # noqa:F841
    _chat_history = state["chat_history"]  # noqa:F841
    result = {"response": {"some": "response"}}
    return result, state.update(**result)


@action(reads=["query", "mode", "response"], writes=["chat_history"])
def response(state: State) -> Tuple[dict, State]:
    """Function to create a response based on the prior action."""
    _result = {
        "content": state["response"],
        "type": state["mode"],
        "role": "assistant",
    }
    return _result, state.append(chat_history=_result)


def base_application(app_id: str, storage_dir: str, project_id: str) -> Application:
    """Creates the Burr application"""
    tracker = tracking.LocalTrackingClient(project=project_id, storage_dir=storage_dir)
    return (
        ApplicationBuilder()
        .with_actions(
            apply_input_query=apply_input_query,
            decide_mode=choose_mode,
            unknown_action=unknown_action,
            some_action=some_action,
            response=response,
        )
        .with_transitions(
            ("apply_input_query", "decide_mode", default),
            ("decide_mode", "unknown_action", when(mode="unknown")),
            ("decide_mode", "some_action", when(mode="some_action")),
            (["unknown_action", "some_action"], "response", default),
            ("response", "apply_input_query", default),
        )
        # initializes from the tracking log if it does not already exist
        .initialize_from(
            tracker,
            resume_at_next_action=False,  # always resume from entrypoint in the case of failure
            default_state={"chat_history": []},
            default_entrypoint="apply_input_query",
        )
        .with_tracker(tracker)
        .with_identifiers(app_id=app_id)
        .build()
    )


if __name__ == "__main__":
    import uuid

    app_id: str = str(uuid.uuid4())
    storage_dir: str = "~/.burr"
    project_id: str = "template:multi-modal-agent"
    app = base_application(app_id, storage_dir, project_id)
    app.visualize(
        output_file_path="multi_modal_agent", include_conditions=False, view=True, format="png"
    )
    # this is how you could run one cycle of the agent:
    # app.run(halt_after=["response"], inputs={"user_query": "Hello, world!"})
