import time

import numpy as np

from superagi.agent.agent_message_builder import AgentLlmMessageBuilder
from superagi.agent.task_queue import TaskQueue
from superagi.helper.error_handler import ErrorHandler
from superagi.helper.json_cleaner import JsonCleaner
from superagi.helper.prompt_reader import PromptReader
from superagi.helper.token_counter import TokenCounter
from superagi.lib.logger import logger
from superagi.models.agent_execution import AgentExecution
from superagi.models.agent_execution_feed import AgentExecutionFeed
from superagi.models.workflows.agent_workflow_step import AgentWorkflowStep
from superagi.models.workflows.agent_workflow_step_tool import AgentWorkflowStepTool
from superagi.models.agent import Agent
from superagi.types.queue_status import QueueStatus


class QueueStepHandler:
    """Handles the queue step of the agent workflow"""
    def __init__(self, session, llm, agent_id: int, agent_execution_id: int):
        self.session = session
        self.llm = llm
        self.agent_execution_id = agent_execution_id
        self.agent_id = agent_id
        self.organisation = Agent.find_org_by_agent_id(self.session, agent_id=self.agent_id)

    def _queue_identifier(self, step_tool):
        return step_tool.unique_id + "_" + str(self.agent_execution_id)

    def _build_task_queue(self, step_tool):
        return TaskQueue(self._queue_identifier(step_tool))

    def execute_step(self):
        execution = AgentExecution.get_agent_execution_from_id(self.session, self.agent_execution_id)
        workflow_step = AgentWorkflowStep.find_by_id(self.session, execution.current_agent_step_id)
        step_tool = AgentWorkflowStepTool.find_by_id(self.session, workflow_step.action_reference_id)
        task_queue = self._build_task_queue(step_tool)

        if not task_queue.get_status() or task_queue.get_status() == QueueStatus.COMPLETE.value:
            task_queue.set_status(QueueStatus.INITIATED.value)

        if task_queue.get_status() == QueueStatus.INITIATED.value:
            self._add_to_queue(task_queue, step_tool)
            execution.current_feed_group_id = "DEFAULT"
            task_queue.set_status(QueueStatus.PROCESSING.value)

        if not task_queue.get_tasks():
            task_queue.set_status(QueueStatus.COMPLETE.value)
            return "COMPLETE"
        self._consume_from_queue(task_queue)
        return "default"

    def _add_to_queue(self, task_queue: TaskQueue, step_tool: AgentWorkflowStepTool):
        assistant_reply = self._process_input_instruction(step_tool)
        self._process_reply(task_queue, assistant_reply)

    def _consume_from_queue(self, task_queue: TaskQueue):
        tasks = task_queue.get_tasks()
        agent_execution = AgentExecution.find_by_id(self.session, self.agent_execution_id)
        if tasks:
            task = task_queue.get_first_task()
            # generating the new feed group id
            agent_execution.current_feed_group_id = "GROUP_" + str(int(time.time()))
            self.session.commit()
            task_response_feed = AgentExecutionFeed(agent_execution_id=self.agent_execution_id,
                                                    agent_id=self.agent_id,
                                                    feed="Input: " + task,
                                                    role="assistant",
                                                    feed_group_id=agent_execution.current_feed_group_id)
            self.session.add(task_response_feed)
            self.session.commit()
            task_queue.complete_task("PROCESSED")

    def _process_reply(self, task_queue: TaskQueue, assistant_reply: str):
        assistant_reply = JsonCleaner.extract_json_array_section(assistant_reply)
        print("Queue reply:", assistant_reply)
        task_array = np.array(eval(assistant_reply)).flatten().tolist()
        for task in task_array:
            task_queue.add_task(str(task))
            logger.info("RAMRAM: Added task to queue: ", task)

    def _process_input_instruction(self, step_tool):
        prompt = self._build_queue_input_prompt(step_tool)
        logger.info("Prompt: ", prompt)
        agent_feeds = AgentExecutionFeed.fetch_agent_execution_feeds(self.session, self.agent_execution_id)
        print(".........//////////////..........2")
        messages = AgentLlmMessageBuilder(self.session, self.llm, self.llm.get_model(), self.agent_id, self.agent_execution_id) \
            .build_agent_messages(prompt, agent_feeds, history_enabled=step_tool.history_enabled,
                                  completion_prompt=step_tool.completion_prompt)
        current_tokens = TokenCounter.count_message_tokens(messages, self.llm.get_model())
        response = self.llm.chat_completion(messages, TokenCounter(session=self.session, organisation_id=self.organisation.id).token_limit(self.llm.get_model()) - current_tokens)
        
        if 'error' in response and response['message'] is not None:
            ErrorHandler.handle_openai_errors(self.session, self.agent_id, self.agent_execution_id, response['message'])
            
        if 'content' not in response or response['content'] is None:
            raise RuntimeError(f"Failed to get response from llm")
        total_tokens = current_tokens + TokenCounter.count_message_tokens(response, self.llm.get_model())
        AgentExecution.update_tokens(self.session, self.agent_execution_id, total_tokens)
        assistant_reply = response['content']
        return assistant_reply

    def _build_queue_input_prompt(self, step_tool: AgentWorkflowStepTool):
        queue_input_prompt = PromptReader.read_agent_prompt(__file__, "agent_queue_input.txt")
        queue_input_prompt = queue_input_prompt.replace("{instruction}", step_tool.input_instruction)

        return queue_input_prompt
