import json
import time

from omagent_core.clients.devices.app.schemas import (CodeEnum, ContentStatus,
                                                      InteractionType,
                                                      MessageType)
from omagent_core.clients.input_base import InputBase
from omagent_core.engine.configuration.configuration import Configuration
from omagent_core.engine.http.models.workflow_status import running_status
from omagent_core.engine.orkes.orkes_workflow_client import (
    OrkesWorkflowClient, workflow_client)
from omagent_core.services.connectors.redis import RedisConnector
from omagent_core.utils import registry
from omagent_core.utils.container import container
from omagent_core.utils.general import read_image
from omagent_core.utils.logger import logging
from omagent_core.utils.registry import registry


@registry.register_component()
class AppInput(InputBase):
    redis_stream_client: RedisConnector

    def read_input(self, workflow_instance_id: str, input_prompt=""):
        stream_name = f"{workflow_instance_id}_input"
        group_name = "omappagent"  # consumer group name
        consumer_name = f"{workflow_instance_id}_agent"  # consumer name
        poll_interval: int = 1

        if input_prompt is not None:
            start_id = self._send_input_message(workflow_instance_id, input_prompt)
        else:
            current_timestamp = int(time.time() * 1000)
            start_id = f"{current_timestamp}-0"

        result = {}
        # ensure consumer group exists
        try:
            self.redis_stream_client._client.xgroup_create(
                stream_name, group_name, id="0", mkstream=True
            )
        except Exception as e:
            logging.debug(f"Consumer group may already exist: {e}")

        logging.info(
            f"Listening to Redis stream: {stream_name} in group: {group_name} start_id: {start_id}"
        )
        data_flag = False
        while True:
            try:
                # logging.info(f"Checking workflow status: {workflow_instance_id}")
                workflow_status = workflow_client.get_workflow_status(
                    workflow_instance_id
                )
                if workflow_status.status not in running_status:
                    logging.info(
                        f"Workflow {workflow_instance_id} is not running, exiting..."
                    )
                    break

                # read new messages from redis stream
                messages = self.redis_stream_client._client.xrevrange(
                    stream_name, max="+", min=start_id, count=1
                )
                # Convert byte data to string
                messages = [
                    (
                        message_id,
                        {
                            k.decode("utf-8"): v.decode("utf-8")
                            for k, v in message.items()
                        },
                    )
                    for message_id, message in messages
                ]
                # logging.info(f"Messages: {messages}")

                for message_id, message in messages:
                    data_flag = self.process_message(message, result)
                if data_flag:
                    break
                # Sleep for the specified interval before checking for new messages again
                # logging.info(f"Sleeping for {poll_interval} seconds, waiting for {stream_name} ...")
                time.sleep(poll_interval)
            except Exception as e:
                logging.error(f"Error while listening to stream: {e}")
                time.sleep(poll_interval)  # Wait before retrying
        return result

    def process_message(self, message, result):
        logging.info(f"Received message: {message}")
        try:
            payload = message.get("payload")
            """
            {
                "agent_id": "string",
                "messages": [
                    {
                        "role": "string",
                        "content": [
                            {
                                "type": "string",
                                "data": "string"
                            }
                        ]
                    }
                ],
                "kwargs": {}
            }
            """
            # check payload data
            if not payload:
                logging.error("Payload is empty")
                return False

            try:
                payload_data = json.loads(payload)
            except json.JSONDecodeError as e:
                logging.error(f"Payload is not a valid JSON: {e}")
                return False

            if "agent_id" not in payload_data:
                logging.error("Payload does not contain 'agent_id' key")
                return False

            if "messages" not in payload_data:
                logging.error("Payload does not contain 'messages' key")
                return False

            if not isinstance(payload_data["messages"], list):
                logging.error("'messages' should be a list")
                return False

            for message in payload_data["messages"]:
                if not isinstance(message, dict):
                    logging.error("Each item in 'messages' should be a dictionary")
                    return False
                if "role" not in message or "content" not in message:
                    logging.error(
                        "Each item in 'messages' should contain 'role' and 'content' keys"
                    )
                    return False
                if not isinstance(message["content"], list):
                    logging.error("'content' should be a list")
                    return False
                for content in message["content"]:
                    if not isinstance(content, dict):
                        logging.error("Each item in 'content' should be a dictionary")
                        return False
                    if "type" not in content or "data" not in content:
                        logging.error(
                            "Each item in 'content' should contain 'type' and 'data' keys"
                        )
                        return False

            message_data = json.loads(payload)
            result.update(message_data)
        except Exception as e:
            logging.error(f"Error processing message: {e}")
            return False
        return True

    def _send_input_message(self, agent_id, msg):
        message_id = self._send_base_message(
            agent_id,
            CodeEnum.SUCCESS.value,
            "",
            0,
            MessageType.TEXT.value,
            msg,
            ContentStatus.END_BLOCK.value,
            InteractionType.INPUT.value,
            0,
            0,
        )
        return message_id

    def _create_message_data(
        self,
        agent_id,
        code,
        error_info,
        took,
        msg_type,
        msg,
        content_status,
        interaction_type,
        prompt_tokens,
        output_tokens,
    ):
        message = {"role": "assistant", "type": msg_type, "content": msg}
        usage = {"prompt_tokens": prompt_tokens, "output_tokens": output_tokens}
        data = {
            "agent_id": agent_id,
            "code": code,
            "error_info": error_info,
            "took": took,
            "content_status": content_status,
            "interaction_type": int(interaction_type),
            "message": message,
            "usage": usage,
        }
        return {"payload": json.dumps(data, ensure_ascii=False)}

    def _send_to_group(self, stream_name, group_name, data):
        logging.info(f"Stream: {stream_name}, Group: {group_name}, Data: {data}")
        message_id = self.redis_stream_client._client.xadd(stream_name, data)
        try:
            self.redis_stream_client._client.xgroup_create(
                stream_name, group_name, id="0"
            )
        except Exception as e:
            logging.debug(f"Consumer group may already exist: {e}")

        return message_id

    def _send_base_message(
        self,
        agent_id,
        code,
        error_info,
        took,
        msg_type,
        msg,
        content_status,
        interaction_type,
        prompt_tokens,
        output_tokens,
    ):
        stream_name = f"{agent_id}_output"
        group_name = "omappagent"  # replace with your consumer group name
        data = self._create_message_data(
            agent_id,
            code,
            error_info,
            took,
            msg_type,
            msg,
            content_status,
            interaction_type,
            prompt_tokens,
            output_tokens,
        )
        message_id = self._send_to_group(stream_name, group_name, data)
        return message_id
