import asyncio
import os
from dataclasses import dataclass
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

load_dotenv()

_NER_SYSTEM_PROMPT = (
    "Your task is to find entities of certain type from the given text content.\n"
    "If there're multiple entities, please return them all with comma separated, "
    'e.g. "entity1, entity2, entity3".\n'
    "You should only return the entity list, nothing else.\n"
    'If there\'s no such entity, please return "None".'
)

_NER_USER_TEMPLATE = "Entity type: {entity_type}\nText content: {text}\nEntities:"


@dataclass
class NERInput:
    text: str
    entity_type: str = "job title"


class NERExecutor(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="NERAgent",
            instructions=_NER_SYSTEM_PROMPT,
        )

    @handler
    async def extract(self, ner_input: NERInput, ctx: WorkflowContext[str]) -> None:
        user_msg = _NER_USER_TEMPLATE.format(
            entity_type=ner_input.entity_type, text=ner_input.text
        )
        response = await self._agent.run(user_msg)
        await ctx.send_message(response.text)


class CleansingExecutor(Executor):
    @handler
    async def cleanse(self, entities_str: str, ctx: WorkflowContext[Never, List[str]]) -> None:
        parts = entities_str.split(",")
        cleaned = [p.strip(' \t."') for p in parts]
        entities = [p for p in cleaned if len(p) > 0]
        await ctx.yield_output(entities)


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

    MAF workflows do not support concurrent execution, so each
    concurrent caller needs its own workflow instance.
    """
    _ner = NERExecutor(id="NER_LLM")
    _cleansing = CleansingExecutor(id="cleansing")
    return (
        WorkflowBuilder(name="NERWorkflow", start_executor=_ner)
        .add_edge(_ner, _cleansing)
        .build()
    )


async def main():
    workflow = create_workflow()
    result = await workflow.run(
        NERInput(
            text="Maxime is a data scientist at Auto Dataset and he lives in Paris, France.",
            entity_type="job title",
        )
    )
    print(result.get_outputs()[0])


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