import asyncio
import os
from dataclasses import dataclass, field
from typing import List

from dotenv import load_dotenv
from typing_extensions import Never

from agent_framework import Agent, Executor, WorkflowBuilder, WorkflowContext, handler
from agent_framework.openai import OpenAIChatClient
from wiki_search import search
from python_repl import python

load_dotenv()

_TRIGGERING_PROMPT = (
    "Determine which next function to use, and respond using stringfield JSON object.\n"
    "If you have completed all your tasks, make sure to use the 'finish' function to signal "
    "and remember show your results."
)


def _search_tool(entity: str, count: int = 10) -> str:
    """Search Wikipedia for an entity and return the first sentences."""
    return search(entity, count)


def _python_tool(command: str) -> str:
    """Execute a Python command and return the output."""
    return python(command)


def _finish_tool(response: str) -> str:
    """Signal that all goals are completed and show results."""
    return response


@dataclass
class AutoGPTInput:
    name: str = "FilmTriviaGPT"
    goals: List[str] = field(default_factory=lambda: [
        "Introduce 'Lord of the Rings' film trilogy including the film title, "
        "release year, director, current age of the director, production company "
        "and a brief summary of the film."
    ])
    role: str = (
        "an AI specialized in film trivia that provides accurate and up-to-date "
        "information about movies, directors, actors, and more."
    )


class AutoGPTExecutor(Executor):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        client = OpenAIChatClient(
            azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
            model=os.environ.get("AZURE_OPENAI_DEPLOYMENT", "gpt-4"),
            api_key=os.environ["AZURE_OPENAI_API_KEY"],
        )
        self._agent = Agent(
            client=client,
            name="AutoGPTAgent",
            instructions="",  # will be set dynamically per run
            tools=[_search_tool, _python_tool, _finish_tool],
        )

    @handler
    async def process(self, gpt_input: AutoGPTInput, ctx: WorkflowContext[Never, str]) -> None:
        # Build system prompt
        system_prompt = (
            f"You are {gpt_input.name}, {gpt_input.role}\n"
            "Play to your strengths as an LLM and pursue simple strategies "
            "with no legal complications to complete all goals.\n"
            "Your decisions must always be made independently without seeking "
            "user assistance.\n\n"
            "Performance Evaluation:\n"
            "1. Continuously review and analyze your actions to ensure you are "
            "performing to the best of your abilities.\n"
            "2. Constructively self-criticize your big-picture behavior constantly.\n"
            "3. Reflect on past decisions and strategies to refine your approach.\n"
            "4. Every command has a cost, so be smart and efficient. "
            "Aim to complete tasks in the least number of steps.\n"
        )

        goals_text = "\n".join(f"{i + 1}. {g}" for i, g in enumerate(gpt_input.goals))
        user_prompt = f"Goals:\n\n{goals_text}\n\n{_TRIGGERING_PROMPT}"

        self._agent._instructions = system_prompt

        response = await self._agent.run(user_prompt)
        await ctx.yield_output(response.text)


def create_workflow():
    """Create a fresh workflow instance.

    MAF workflows do not support concurrent execution, so each
    concurrent caller needs its own workflow instance.
    """
    _executor = AutoGPTExecutor(id="autogpt")
    return (
        WorkflowBuilder(name="AutonomousAgentWorkflow", start_executor=_executor)
        .build()
    )


async def main():
    workflow = create_workflow()
    result = await workflow.run(
        AutoGPTInput(
            name="FilmTriviaGPT",
            goals=[
                "Introduce 'Lord of the Rings' film trilogy including the film title, "
                "release year, director, current age of the director, production company "
                "and a brief summary of the film."
            ],
            role=(
                "an AI specialized in film trivia that provides accurate and up-to-date "
                "information about movies, directors, actors, and more."
            ),
        )
    )
    print(f"Output:\n{result.get_outputs()[0]}")


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