from flask import current_app, session
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough

from common.utils.business_event import BusinessEvent
from common.utils.business_event_context import current_event
from common.utils.model_utils import get_template
from eveai_chat_workers.outputs.globals.a2q_output.q_a_output_v1_0 import A2QOutput
from eveai_chat_workers.outputs.globals.q_a_output.q_a_output_v1_0 import QAOutput


class HumanAnswerServices:
    @staticmethod
    def check_affirmative_answer(tenant_id: int, question: str, answer: str, language_iso: str) -> bool:
        return HumanAnswerServices._check_answer(tenant_id, question, answer, language_iso, "check_affirmative_answer",
                                                 "Check Affirmative Answer")

    @staticmethod
    def check_additional_information(tenant_id: int, question: str, answer: str, language_iso: str) -> bool:
        result = HumanAnswerServices._check_answer(tenant_id, question, answer, language_iso,
                                                   "check_additional_information", "Check Additional Information")

        return result

    @staticmethod
    def get_answer_to_question(tenant_id: int, question: str, answer: str, language_iso: str) -> str:

        language = HumanAnswerServices._process_arguments(question, answer, language_iso)
        span_name = "Get Answer To Question"
        template_name = "get_answer_to_question"

        if not current_event:
            with BusinessEvent('Answer Check Service', tenant_id):
                with current_event.create_span(span_name):
                    return HumanAnswerServices._get_answer_to_question_logic(question, answer, language, template_name)
        else:
            with current_event.create_span('Check Affirmative Answer'):
                return HumanAnswerServices._get_answer_to_question_logic(question, answer, language, template_name)

    @staticmethod
    def _check_answer(tenant_id: int, question: str, answer: str, language_iso: str, template_name: str,
                      span_name: str) -> bool:
        language = HumanAnswerServices._process_arguments(question, answer, language_iso)
        if not current_event:
            with BusinessEvent('Answer Check Service', tenant_id):
                with current_event.create_span(span_name):
                    return HumanAnswerServices._check_answer_logic(question, answer, language, template_name)
        else:
            with current_event.create_span(span_name):
                return HumanAnswerServices._check_answer_logic(question, answer, language, template_name)

    @staticmethod
    def _check_answer_logic(question: str, answer: str, language: str, template_name: str) -> bool:
        prompt_params = {
            'question': question,
            'answer': answer,
            'language': language,
        }

        template, llm = get_template(template_name)
        check_answer_prompt = ChatPromptTemplate.from_template(template)
        setup = RunnablePassthrough()

        output_schema = QAOutput
        structured_llm = llm.with_structured_output(output_schema)

        chain = (setup | check_answer_prompt | structured_llm )

        raw_answer = chain.invoke(prompt_params)

        return raw_answer.answer

    @staticmethod
    def _get_answer_to_question_logic(question: str, answer: str, language: str, template_name: str) \
            -> str:
        prompt_params = {
            'question': question,
            'answer': answer,
            'language': language,
        }

        template, llm = get_template(template_name)
        check_answer_prompt = ChatPromptTemplate.from_template(template)
        setup = RunnablePassthrough()

        output_schema = A2QOutput
        structured_llm = llm.with_structured_output(output_schema)

        chain = (setup | check_answer_prompt | structured_llm)

        raw_answer = chain.invoke(prompt_params)

        return raw_answer.answer

    @staticmethod
    def _process_arguments(question, answer, language_iso: str) -> str:
        if language_iso.strip() == '':
            raise ValueError("Language cannot be empty")
        language = current_app.config.get('SUPPORTED_LANGUAGE_ISO639_1_LOOKUP').get(language_iso)
        if language is None:
            raise ValueError(f"Unsupported language: {language_iso}")
        if question.strip() == '':
            raise ValueError("Question cannot be empty")
        if answer.strip() == '':
            raise ValueError("Answer cannot be empty")

        return language
