import asyncio
import os
from dataclasses import dataclass

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

load_dotenv()

_CLASSIFY_SYSTEM_PROMPT = """\
There is a search bar in the mall APP and users can enter any query in the search bar.

The user may want to search for orders, view product information, or seek recommended products.

Therefore, please classify user intentions into the following three types \
according to the query: product_recommendation, order_search, product_info

Please note that only the above three situations can be returned, and try not to include other return values."""


@dataclass
class ClassifiedQuery:
    query: str
    intention: str


class ClassifyExecutor(Executor):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        client = OpenAIChatClient(
            azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
            model=os.environ["AZURE_OPENAI_DEPLOYMENT"],
            api_key=os.environ["AZURE_OPENAI_API_KEY"],
        )
        self._agent = Agent(
            client=client,
            name="ClassifyAgent",
            instructions=_CLASSIFY_SYSTEM_PROMPT,
        )

    @handler
    async def classify(self, query: str, ctx: WorkflowContext[ClassifiedQuery]) -> None:
        response = await self._agent.run(f"The user's query is {query}")
        llm_result = response.text
        intentions_list = ["order_search", "product_info", "product_recommendation"]
        matches = [i for i in intentions_list if i in llm_result.lower()]
        intention = matches[0] if matches else "unknown"
        await ctx.send_message(ClassifiedQuery(query=query, intention=intention))


class OrderSearchExecutor(Executor):
    @handler
    async def search(self, msg: ClassifiedQuery, ctx: WorkflowContext[Never, str]) -> None:
        await ctx.yield_output("Your order is being mailed, please wait patiently.")


class ProductInfoExecutor(Executor):
    @handler
    async def info(self, msg: ClassifiedQuery, ctx: WorkflowContext[Never, str]) -> None:
        await ctx.yield_output("This product is produced by Microsoft.")


class ProductRecommendationExecutor(Executor):
    @handler
    async def recommend(self, msg: ClassifiedQuery, ctx: WorkflowContext[Never, str]) -> None:
        await ctx.yield_output(
            "I recommend promptflow to you, which can solve your problem very well."
        )


class DefaultExecutor(Executor):
    @handler
    async def default(self, msg: ClassifiedQuery, ctx: WorkflowContext[Never, str]) -> None:
        await ctx.yield_output("Sorry, no results matching your search were found.")


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

    MAF workflows do not support concurrent execution, so each
    concurrent caller needs its own workflow instance.
    """
    _classify = ClassifyExecutor(id="classify")
    _order = OrderSearchExecutor(id="order_search")
    _product = ProductInfoExecutor(id="product_info")
    _recommend = ProductRecommendationExecutor(id="product_recommendation")
    _default = DefaultExecutor(id="default")
    return (
        WorkflowBuilder(name="ConditionalSwitchWorkflow", start_executor=_classify)
        .add_edge(_classify, _order, condition=lambda m: m.intention == "order_search")
        .add_edge(_classify, _product, condition=lambda m: m.intention == "product_info")
        .add_edge(_classify, _recommend, condition=lambda m: m.intention == "product_recommendation")
        .add_edge(_classify, _default, condition=lambda m: m.intention == "unknown")
        .build()
    )


async def main():
    workflow = create_workflow()
    result = await workflow.run("When will my order be shipped?")
    print(f"Response: {result.get_outputs()[0]}")


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