from __future__ import annotations

from typing import Optional, Union, List

from analogai.deepthink.presentation.parsers.message_parser import MessageParser
from analogai.deepthink.presentation.parsers.parsers_factory import build_message_parser
from analogai.deepthink.presentation.interpreters.interpreter_pipeline import InterpreterPipeline
from analogai.deepthink.presentation.interpreters.interpreter_result import InterpreterResult
from analogai.deepthink.presentation.preprocessing.preprocessor import preprocess_text
from analogai.deepthink.presentation.postprocessing.postprocessor import postprocess_text
# from analogai.deepthink.presentation.user_message import UserMessage
from analogai.deepthink.presentation.pipeline.results import PipelineOutput


class MessagePipeline:
    def __init__(
        self,
        message_parser: Optional[MessageParser] = None,
        interpreters: Optional[InterpreterPipeline] = None,
        intents_processor=None,
    ):
        self._message_parser = message_parser or build_message_parser()
        self._interpreters = interpreters
        self._intents_processor = intents_processor

    def execute(
        self,
        raw_message: str,
        user_agent,
        previous_messages_stack,
        intents_processor=None,
    ) -> Optional[Union[InterpreterResult, PipelineOutput]]:
        message_text = self.preprocess(raw_message, previous_messages_stack, user_agent)
        parsed_list = self.parse(message_text, user_agent)
        if not parsed_list:
            return PipelineOutput(message_text=None, transformed_json=message_text)
        processor = intents_processor or self._intents_processor
        if processor is None:
            result = self.interpret(parsed_list[0], user_agent, message_text)
            return result
        results: List[InterpreterResult] = []
        for parsed in parsed_list:
            result = self.interpret(parsed, user_agent, message_text)
            if result is None:
                continue
            results.append(result)

        if not results:
            return PipelineOutput(message_text=None, transformed_json=message_text)

        messages = []
        for result in results:
            response = self.execute_intent(result, processor)
            if response is None:
                continue
            if response.message:
                messages.append(response.message)
        if not messages:
            return PipelineOutput(message_text=None, transformed_json=message_text)
        combined_message = ". ".join(messages)
        processed_message = self.postprocess(combined_message)
        return self.output_result(processed_message, message_text)

    def preprocess(self, raw_message: str, previous_messages_stack, user_agent) -> str:
        return preprocess_text(raw_message, previous_messages_stack, user_agent=user_agent)

    def parse(self, message_text: str, user_agent) -> List:
        return self._message_parser.parse(message_text)

    def interpret(self, parsed, user_agent, message_text) -> Optional[InterpreterResult]:
        if self._interpreters is None:
            return None
        result = self._interpreters.run(parsed, user_agent, message_text)
        if result is None or result is False:
            return None
        return result

    def execute_intent(self, result: InterpreterResult, intents_processor):
        return intents_processor.execute(result.intent_type, result.request)

    def postprocess(self, message: Optional[str]) -> Optional[str]:
        return postprocess_text(message) if message else None

    def output_result(self, message_text: Optional[str], transformed_json: Optional[str] = None) -> PipelineOutput:
        return PipelineOutput(message_text=message_text, transformed_json=transformed_json)

