import json
import random
from datetime import date
from typing import Optional, List, Dict, Any

from crewai.flow.flow import start, listen, router
from flask import current_app
from pydantic import BaseModel, Field, EmailStr

from common.extensions import cache_manager, db, minio_client
from common.models.interaction import EveAIAsset
from common.models.user import Tenant
from common.services.interaction.capsule_services import CapsuleServices
from common.services.utils.human_answer_services import HumanAnswerServices
from common.services.utils.translation_services import TranslationServices
from common.utils.business_event_context import current_event
from common.utils.eveai_exceptions import EveAISpecialistExecutionError
from eveai_chat_workers.definitions.language_level.language_level_v1_0 import LANGUAGE_LEVEL, get_language_level_context
from eveai_chat_workers.definitions.tone_of_voice.tone_of_voice_v1_0 import TONE_OF_VOICE, get_tone_of_voice_context
from eveai_chat_workers.outputs.globals.basic_types.list_item import ListItem
from eveai_chat_workers.outputs.globals.rag.rag_v1_0 import RAGOutput
from eveai_chat_workers.outputs.traicie.affirmative_answer.affirmative_answer_v1_0 import TraicieAffirmativeAnswerOutput
from eveai_chat_workers.outputs.traicie.interview_mode.interview_mode_v1_0 import TraicieInterviewModeOutput
from eveai_chat_workers.outputs.traicie.knockout_questions.knockout_questions_v1_0 import KOQuestion, KOQuestions
from eveai_chat_workers.specialists.crewai_base_classes import EveAICrewAICrew, EveAICrewAIFlow, EveAIFlowState
from eveai_chat_workers.specialists.crewai_base_specialist import CrewAIBaseSpecialistExecutor
from eveai_chat_workers.specialists.specialist_typing import SpecialistResult, SpecialistArguments

INITIALISATION_MESSAGES = [
    "Great! Let's see if this job might be a match for you by going through a few questions.",
    "Nice to hear that! I’ll start with a first question to kick things off.",
    "Good to know! Let’s begin with the first question.",
    "Thanks for your reply. Let's get started with a few short questions.",
    "Excellent! Here's a first question to explore your fit with the role.",
    "Glad to hear that. Let's start with the first question.",
    "Appreciate your response! I’ll now ask you the first question.",
    "Awesome! Let's begin with a few questions to learn more about you.",
    "Perfect, thank you. Let's start the matching process with a first question.",
    "Thanks for sharing that. Ready for the first question?"
]
START_SELECTION_QUESTIONS = [
    "Shall we see if this job could be a good fit for you?",
    "Shall we go through a few questions to explore if there's a potential match?",
    "May I ask you a first question?",
    "Are you interested in applying for this position?",
    "Would you like to take the next step and answer a few short questions?",
    "Shall we begin the application process together?",
    "Would you like to start the matching process to see if this role suits your preferences?",
    "Let’s explore if this opportunity aligns with what you're looking for — ready for a few questions?",
    "Would you be open to answering a few questions to learn more about the role and your fit?",
    "Would you like to continue and start the first part of the application journey?"
]
START_SELECTION_QUESTIONS_CTD = [
    "Do you have any other questions, or shall we start the interview to see if there’s a match with the job?",
    "Do you have any questions, or shall we begin to explore if this job suits you?",
    "Would you like to know anything else first, or shall we start the interview to see if the role feels like a good fit?",
    "Are there any remaining questions, or shall we find out whether this job is something for you?",
    "Is there anything still on your mind, or shall we begin the conversation to explore the match?",
    "Feel free to ask if you still have any questions; otherwise, shall we begin the interview?",
    "Is everything clear for you, or shall we now take a look together to see if this role is made for you?",
    "Are you ready, or is there anything else you’d like clarified before we get started?",
    "No more questions, or shall we start discovering if there’s a connection with this role?",
    "Are you ready to begin and see if you and this job are a good match?",
    # Extra variants
    "Before we move on, is there anything you’d like to ask? Or shall we begin?",
    "Shall we get started, or is there something else you’d like to clarify first?",
    "Would you like to go over anything else before we start the interview?",
    "Is now a good time to begin, or do you have more questions before we proceed?",
    "Do you feel ready to start, or should we address any remaining questions first?",
    "Would you prefer to discuss any final points, or shall we begin the selection process?",
    "Is there anything we should clear up before starting, or shall we dive right in?",
    "Would you like to ask anything else before we begin exploring the role together?",
    "Do you want to go over anything else first, or shall we kick off the interview?",
    "Are we good to start, or is there anything else you’d like to cover?"
]
TRY_TO_START_SELECTION_QUESTIONS = [
    "That's a pity — we can only move forward if we start the selection process. Would you like to begin now?",
    "We understand, though it’s worth mentioning that the only way to continue is to start the procedure. Shall we get started after all?",
    "Too bad! To proceed, we do need to go through the selection steps. Would you be open to starting now?",
    "We’re sorry to hear that. The next steps are only possible if we start the selection process. Would you reconsider and allow us to begin?",
    "That's unfortunate — continuing isn’t possible without starting the process. Are you sure you don’t want to begin now?",
    "Thanks for your response. Just so you know: we can only continue if we go through the initial questions. Shall we start anyway?",
    "We respect your answer, of course. Still, we’d love to continue — but that’s only possible if we begin the selection process. Can we do that now?",
    "We get it — but to move forward, the selection process does need to be started. Would you like to give it a go?",
    "Understood! However, we can't proceed without initiating the process. Would you like to start it now after all?",
    "We appreciate your honesty. Just to clarify: the process only continues if we begin the selection. Shall we go ahead?"
]
INSUFFICIENT_INFORMATION_MESSAGES = [
    "I'm afraid I don't have enough information to answer that properly. Feel free to ask something else!",
    "There isn’t enough data available right now to give you a clear answer. You're welcome to rephrase or ask a different question.",
    "Sorry, I can't provide a complete answer based on the current information. Would you like to try asking something else?",
    "I don’t have enough details to give you a confident answer. You can always ask another question if you’d like.",
    "Unfortunately, I can’t answer that accurately with the information at hand. Please feel free to ask something else.",
    "That’s a great question, but I currently lack the necessary information to respond properly. Want to ask something different?",
    "I wish I could help more, but the data I have isn't sufficient to answer this. You’re welcome to explore other questions.",
    "There’s not enough context for me to provide a good answer. Don’t hesitate to ask another question if you'd like!",
    "I'm not able to give a definitive answer to that. Perhaps try a different question or angle?",
    "Thanks for your question. At the moment, I can’t give a solid answer — but I'm here if you want to ask something else!"
]
KO_CRITERIA_NOT_MET_MESSAGES = [
    "Thank you for your answers. Based on your responses, we won't be moving forward with this particular role. We do encourage you to keep an eye on our website for future opportunities.",
    "We appreciate the time you took to answer our questions. At this point, we won’t be proceeding with your application, but feel free to check our website regularly for new vacancies.",
    "Thanks for your input. While we’re not continuing with your application for this role, we’d be happy to welcome your interest again in the future — new opportunities are posted regularly on our site.",
    "Thank you for participating. Although this role doesn’t seem to be the right match right now, we invite you to stay connected and check back for other opportunities.",
    "We truly appreciate your time and effort. Unfortunately, we won’t be progressing with this application, but we encourage you to visit our website again for future job openings.",
    "Thanks so much for answering our questions. This role may not be the right fit, but we’d love for you to consider applying again when new positions become available.",
    "We value your interest in this position. While we won’t be moving forward in this case, we warmly invite you to explore other roles with us in the future.",
    "Your input has been very helpful. Although we're not proceeding at this time, we thank you for your interest and hope to see you again for other opportunities.",
    "Thank you for taking part in the process. We won’t continue with your application for this role, but we invite you to stay informed about future openings through our website.",
    # Nieuwe varianten
    "Thank you for providing some additional clarification. For this role, the previously mentioned requirement remains essential. We hope you’ll continue to follow our other vacancies!",
    "We appreciate your engagement. For this specific role, we do have to end the process here. Wishing you the best of luck with your next step!",
    "Thank you for your additional information. Unfortunately, this does not change the selection process for this position. We look forward to possibly meeting you again in the future!",
    "Thank you for taking the time to respond. For this vacancy, we can only proceed with candidates who meet all the requirements. We wish you all the very best!",
    "We’ve reviewed your answers carefully. Unfortunately, we can’t continue with your application for this role, but we encourage you to check our site for future openings.",
    "We’re grateful for your time and interest. Sadly, this position requires criteria that weren’t met, but we’d love to see your application for other roles.",
    "Thank you for sharing more details. For this specific position, the original requirements still apply. Please keep an eye out for roles that might suit you better.",
    "We value the effort you’ve put into this process. While we can’t move forward this time, we’d be happy to see your application for future opportunities.",
    "Your answers gave us a good understanding of your background. Unfortunately, we can’t proceed with this position, but we hope to connect again in the future."
]
KO_CRITERIA_MET_MESSAGES = [
    "Thank you for your answers. They correspond to some key elements of the role. Would you be open to sharing your contact details so we can continue the selection process?",
    "We appreciate your input. Based on your answers, we'd like to continue the conversation. Could you share your contact information with us?",
    "Thanks for your replies. To proceed with the application process, may we ask you to provide your contact details?",
    "Your answers help us better understand your background. If you're open to it, can share your contact info so we can follow up?",
    "Thank you for taking the time to answer these questions. If you'd like to continue, could we have your contact information?",
    "Your responses give us a good first impression. In order to move forward with the process, could you share your contact details?",
    "We’ve reviewed your answers with interest. To take the next step, would you be willing to share your contact information?",
    "Your input has been recorded. If you’re comfortable doing so, will you please leave your contact information so we can reach out for the next steps?",
    "We’d like to keep in touch regarding the next phases of the selection. Could you provide your contact details for further communication?"
]
KO_CRITERIA_NEXT_MESSAGES = [
    "Thank you for your answer. Here's a next question.",
    "Your answer fits our needs. We have yet another question to ask you.",
    "Positive this far! Here's a follow-up question.",
    "Great, that’s just what we were hoping for. Let’s continue with another question.",
    "Appreciate your reply! Here's the next one.",
    "Thanks for the input. Let’s move on to the next question.",
    "That’s exactly what we needed to hear. Here comes the next question.",
    "Looks promising! Let’s continue with another quick check."
]
CONTACT_DATA_GUIDING_MESSAGE = ("Thank you for trusting your contact data with us. Below you find a form to help you "
                                "to provide us the necessary information.")
NO_CONTACT_DATA_QUESTIONS = [
    "That's a pity! In order to continue, we do need your contact details. Would you be willing to share them? ",
    "We understand your hesitation. However, to proceed with the process, your contact information is required. Would you like to share it with us?",
    "Unfortunately, we can only move forward if you provide your contact details. Would you still consider sharing them with us?",
    "It’s totally your choice, of course. But without your contact details, we can’t proceed further. Would you be open to sharing them?",
    "We’d love to keep going, but we can only do so if we have your contact details. Would you like to provide them now?",
    "Your dpa matters, and we respect your decision. Just know that without your contact details, we’ll need to end the process here. Still interested in moving forward?",
    "It’s a shame to stop here, but we do need your contact info to proceed. Would you like to share it so we can continue?"
]
CONTACT_DATA_QUESTIONS = [
    "Could you please share your contact details so we can reach out to you for the next steps in the selection process?",
    "Would you be willing to provide your contact information so we can continue with your application?",
    "Can you share a way for us to contact you as we move forward with the selection process?",
    "May we have your contact details so we can follow up with the next steps?",
    "Would you mind sharing your contact information to proceed with the selection?",
    "Can you provide your email address or phone number so we can get in touch?",
    "Shall we continue? If so, could you let us know how we can best reach you?",
    "To move forward, may we contact you? If yes, could you share your details?",
    "Are you comfortable sharing your contact information so we can follow up?",
    "Would you like to continue the process by providing your contact details?"
]
CONTACT_DATA_PROCESSED_MESSAGE = "Thank you for allowing us to contact you."
CONTACT_TIME_QUESTION = "When do you prefer us to contact you? You can select some options in the provided form"
CONTACT_TIME_PROCESSED_MESSAGES = [
    "Thank you! We've received all the information we need to continue with the selection process. We'll get in touch with you as soon as possible. If you have any questions in the meantime, don't hesitate to ask.",
    "Great, we have everything we need to proceed. We'll be in touch shortly. Don't hesitate to ask if anything comes up in the meantime.",
    "Thanks for providing your details. We now have all the necessary information and will contact you soon. If you have any further questions, we're here to help.",
    "Perfect, your information has been received. We'll move forward and get back to you as soon as we can. Feel free to reach out if you have any questions.",
    "All set! We’ve received everything needed to move forward. We'll contact you soon. In the meantime, feel free to ask us anything.",
    "Thanks again! We've got everything we need to proceed. Expect to hear from us shortly. If anything is unclear, you're welcome to ask further questions.",
    "Excellent, we now have all the information required to take the next steps. We’ll be in touch as soon as possible. If you have any questions, just let us know.",
    "We appreciate your input. With all the needed details in place, we’ll reach out shortly to continue the process. Questions are always welcome in the meantime.",
    "Thank you for completing this step. We have all the information we need and will contact you as soon as we can. If you have questions, we're happy to assist."
]
NO_FURTHER_QUESTIONS_MESSAGE = "We do not process further questions."
SUCCESSFUL_ENDING_MESSAGES = [
    "Thank you for your application! We'll contact you as soon as possible. If you have any questions in the meantime, don’t hesitate to reach out.",
    "We appreciate your interest and the information you’ve shared. We'll be in touch shortly. Feel free to contact us if anything comes up.",
    "Thanks again for your application. We’ll get back to you soon. In the meantime, we’re happy to answer any questions you may have.",
    "Your application has been received. We’ll reach out to you as soon as we can. If you need anything in the meantime, just let us know.",
    "Thank you for completing the first steps. We’ll follow up as quickly as possible. If you have further questions, we're here to help.",
    "Thanks for taking the time to apply! We’ll contact you shortly. Let us know if you have any questions or need additional information.",
    "We’ve received everything we need for now — thank you! We’ll be in touch soon. Don’t hesitate to ask if something’s unclear.",
    "We’re looking forward to speaking with you. Thanks again for your application, and feel free to reach out if you need anything.",
    "Thanks! We’ll contact you soon to discuss the next steps. In the meantime, we’re happy to answer any further questions.",
    "Your application is complete — thank you! We’ll be reaching out shortly. If you’d like to ask anything in the meantime, we’re available."
]


class SpecialistExecutor(CrewAIBaseSpecialistExecutor):
    """
    type: TRAICIE_SELECTION_SPECIALIST
    type_version: 1.1
    Traicie Selection Specialist Executor class
    """

    def __init__(self, tenant_id, specialist_id, session_id, task_id, **kwargs):
        self.rag_crew = None
        self.determination_crew = None
        self.affirmative_answer_crew = None

        super().__init__(tenant_id, specialist_id, session_id, task_id)

        # Load the Tenant & set language
        self.tenant = Tenant.query.get_or_404(tenant_id)
        self.specialist_phase = "initial"
        self.previous_ai_question = None
        self.previous_interview_phase = None

    @property
    def type(self) -> str:
        return "TRAICIE_SELECTION_SPECIALIST"

    @property
    def type_version(self) -> str:
        return "1.5"

    def _config_task_agents(self):
        self._add_task_agent("advanced_rag_task", "rag_agent")
        self._add_task_agent("traicie_determine_interview_mode_task", "traicie_recruiter_agent")
        self._add_task_agent("traicie_affirmative_answer_check_task", "traicie_recruiter_agent")

    def _config_pydantic_outputs(self):
        self._add_pydantic_output("advanced_rag_task", RAGOutput, "rag_output")
        self._add_pydantic_output("traicie_determine_interview_mode_task", TraicieInterviewModeOutput, "interview_mode")
        self._add_pydantic_output("traicie_affirmative_answer_check_task", TraicieAffirmativeAnswerOutput, "affirmative_answer")

    def _config_state_result_relations(self):
        self._add_state_result_relation("ai_question")
        self._add_state_result_relation("rag_output")
        self._add_state_result_relation("ko_criteria_scores")
        self._add_state_result_relation("current_ko_criterium")
        self._add_state_result_relation("current_ko_criterium_idx")
        self._add_state_result_relation("personal_contact_data")
        self._add_state_result_relation("contact_time_prefs")
        self._add_state_result_relation("interview_phase")
        self._add_state_result_relation("interview_mode")
        self._add_state_result_relation("affirmative_answer")

    def _instantiate_specialist(self):
        verbose = self.tuning
        rag_agents = [self.rag_agent]
        recruitment_agents = [self.traicie_recruiter_agent]
        rag_tasks = [self.advanced_rag_task]
        determination_tasks = [self.traicie_determine_interview_mode_task]
        affirmative_answer_tasks = [self.traicie_affirmative_answer_check_task]

        self.rag_crew = EveAICrewAICrew(
            self,
            "Advanced Rag Crew",
            agents=rag_agents,
            tasks=rag_tasks,
            verbose=verbose,
        )
        self.determination_crew = EveAICrewAICrew(
            self,
            "Determination Crew",
            agents=recruitment_agents,
            tasks=determination_tasks,
            verbose=verbose,
        )
        self.affirmative_answer_crew = EveAICrewAICrew(
            self,
            "Affirmative Answer Crew",
            agents=recruitment_agents,
            tasks=affirmative_answer_tasks,
            verbose=verbose,
        )
        self.flow = SelectionFlow(
            self,
            self.rag_crew,
            self.determination_crew,
            self.affirmative_answer_crew
        )

    def execute(self, arguments: SpecialistArguments, formatted_context, citations) -> SpecialistResult:
        self.log_tuning("Traicie Selection Specialist execution started", {})

        self.arguments = arguments
        self.formatted_context = formatted_context
        self.citations = citations

        self.log_tuning("Traicie Selection Specialist inputs", {
            "Arguments": arguments.model_dump(),
            "Formatted Context": formatted_context,
            "Citations": citations,
            "History": self._formatted_history
        })

        if not self._cached_session.interactions:
            self.specialist_phase = "initial"
        else:
            self.specialist_phase = self._cached_session.interactions[-1].specialist_results.get('phase', 'initial')
            self.previous_ai_question = self._cached_session.interactions[-1].specialist_results.get('ai_question', '')
            self.previous_interview_phase = self._cached_session.interactions[-1].specialist_results.get('interview_phase', '')

        results = None
        match self.specialist_phase:
            case "initial":
                results = self.execute_initial_state()
            case "start_selection_procedure":
                results = self.execute_start_selection_procedure_state()
            case "rag":
                results = self.execute_rag_state()
            case "ko_question_evaluation":
                results = self.execute_ko_question_evaluation()
            case "personal_contact_data_preparation":
                results = self.execute_personal_contact_data_preparation()
            case "personal_contact_data_processing":
                results = self.execute_personal_contact_data_processing()
            case "contact_time_evaluation":
                results = self.execute_contact_time_evaluation_state()
            case "no_valid_candidate":
                results = self.execute_no_valid_candidate_state(arguments, formatted_context, citations)

        self.log_tuning(f"Traicie Selection Specialist execution ended",
                        {"Results": results.model_dump() if results else "No info"})

        return results

    def execute_initial_state(self) -> SpecialistResult:
        self.log_tuning("Traicie Selection Specialist initial_state execution started", {})

        interaction_mode = self.arguments.interaction_mode
        if not interaction_mode:
            interaction_mode = "selection"
        welcome_message = self.specialist.configuration.get("welcome_message", "Welcome to our selection process.")
        welcome_message = TranslationServices.translate(self.tenant_id, welcome_message, self.arguments.language)

        if interaction_mode == "selection":
            return self.execute_start_selection_procedure_state(welcome_message)
        #  We are in orientation mode, so we give a standard message, and move to rag state
        start_selection_question = TranslationServices.translate(self.tenant_id,
                                                                 random.choice(START_SELECTION_QUESTIONS),
                                                                 self.arguments.language)
        self.flow.state.answer = f"{welcome_message}"
        self.flow.state.phase = "rag"
        self.flow.state.interview_phase = "start_selection_procedure"
        self.flow.state.ai_question = welcome_message

        results = SelectionResult.create_for_type(self.type, self.type_version)

        return results

    def execute_start_selection_procedure_state(self, start_message=None) -> SpecialistResult:

        initialisation_message = TranslationServices.translate(self.tenant_id,
                                                               random.choice(INITIALISATION_MESSAGES),
                                                               self.arguments.language)
        if start_message:
            answer = f"{start_message}\n\n{initialisation_message}"
        else:
            answer = initialisation_message

        ko_questions = self._get_ko_questions()

        current_ko_criterium = ko_questions.ko_questions[0].title
        current_ko_criterium_idx = 0
        ko_form = self._prepare_ko_question_form(ko_questions, current_ko_criterium, self.arguments.language)

        self.flow.state.current_ko_criterium = current_ko_criterium
        self.flow.state.current_ko_criterium_idx = current_ko_criterium_idx
        self.flow.state.ko_criteria_scores = []
        self.flow.state.answer = answer
        self.flow.state.phase = "ko_question_evaluation"
        self.flow.state.form_request = ko_form

        results = SelectionResult.create_for_type(self.type, self.type_version)

        return results

    def execute_ko_question_evaluation(self) -> SpecialistResult:
        self.log_tuning("Traicie Selection Specialist ko_question_evaluation started", {})

        # Check if the form has been returned (it should)
        if not self.arguments.form_values:
            raise EveAISpecialistExecutionError(self.tenant_id, self.specialist_id, self.session_id,
                                                "No form values returned")

        ko_questions = self._get_ko_questions()

        previous_idx = self.flow.state.current_ko_criterium_idx
        previous_ko_question = ko_questions.ko_questions[previous_idx]

        # Evaluate KO Criteria
        evaluation = "positive"
        criterium, answer = next(iter(self.arguments.form_values.items()))
        if TranslationServices.translate(self.tenant_id, previous_ko_question.answer_negative, self.arguments.language) == answer:
            evaluation = "negative"
        if previous_ko_question.additional_answer_negative:
            if TranslationServices.translate(self.tenant_id, previous_ko_question.additional_answer_negative, self.arguments.language) == answer:
                evaluation = "negative"

        score = SelectionKOCriteriumScore(
            criterium=criterium,
            answer=answer,
            score=1 if evaluation == "positive" else 0,
        )

        self.flow.state.ko_criteria_scores.append(score)

        if evaluation == "negative":
            answer = TranslationServices.translate(self.tenant_id,
                                                   random.choice(KO_CRITERIA_NOT_MET_MESSAGES),
                                                   self.arguments.language)

            self.flow.state.answer = answer
            self.flow.state.phase = "no_valid_candidate"

            results = SelectionResult.create_for_type(self.type, self.type_version)
        else:
            next_idx = previous_idx + 1

            if next_idx < len(ko_questions.ko_questions):   # There's still a KO criterium to be evaluated
                next_ko_criterium = ko_questions.ko_questions[next_idx]
                ko_form = self._prepare_ko_question_form(ko_questions, next_ko_criterium.title, self.arguments.language)
                next_message = random.choice(KO_CRITERIA_NEXT_MESSAGES)
                answer = TranslationServices.translate(self.tenant_id, next_message, self.arguments.language)

                self.flow.state.answer = answer
                self.flow.state.form_request = ko_form
                self.flow.state.current_ko_criterium = next_ko_criterium.title
                self.flow.state.current_ko_criterium_idx = next_idx
                self.flow.state.phase = "ko_question_evaluation"
            else:   # All KO Criteria have been met
                answer = TranslationServices.translate(self.tenant_id,
                                                       random.choice(KO_CRITERIA_MET_MESSAGES),
                                                       self.arguments.language)
                self.flow.state.ai_question = answer

                self.flow.state.answer = answer
                self.flow.state.current_ko_criterium = ""
                self.flow.state.current_ko_criterium_idx = None
                self.flow.state.phase = "rag"
                self.flow.state.interview_phase = "personal_contact_data_preparation"

            results = SelectionResult.create_for_type(self.type, self.type_version,)

        return results

    def execute_personal_contact_data_preparation(self) -> SpecialistResult:
        self.log_tuning("Traicie Selection Specialist personal_contact_data_preparation started", {})


        contact_form = cache_manager.specialist_forms_config_cache.get_config("MINIMAL_PERSONAL_CONTACT_FORM", "1.0")
        contact_form = TranslationServices.translate_config(self.tenant_id, contact_form, "fields",
                                                            self.arguments.language)
        answer = TranslationServices.translate(self.tenant_id, CONTACT_DATA_GUIDING_MESSAGE,
                                               self.arguments.language)
        self.flow.state.answer = answer
        self.flow.state.form_request = contact_form
        self.flow.state.phase = "personal_contact_data_processing"

        results = SelectionResult.create_for_type(self.type, self.type_version,)

        return results

    def execute_personal_contact_data_processing(self) -> SpecialistResult:
        self.log_tuning("Traicie Selection Specialist personal_contact_data_processing started", {})
        contact_time_question = TranslationServices.translate(self.tenant_id, CONTACT_TIME_QUESTION, self.arguments.language)
        answer = (
            f"{TranslationServices.translate(self.tenant_id, CONTACT_DATA_PROCESSED_MESSAGE, self.arguments.language)} "
            f"{contact_time_question}")
        time_pref_form = cache_manager.specialist_forms_config_cache.get_config("CONTACT_TIME_PREFERENCES_SIMPLE", "1.0")
        time_pref_form = TranslationServices.translate_config(self.tenant_id, time_pref_form, "fields",
                                                              self.arguments.language)

        self.flow.state.answer = answer
        self.flow.state.ai_question = contact_time_question
        self.flow.state.interview_phase = "contact_time_evaluation"
        self.flow.state.phase = "contact_time_evaluation"
        self.flow.state.personal_contact_data = self.arguments.form_values
        self.flow.state.form_request = time_pref_form

        rqc_info = {
            "ko_criteria_scores": self.flow.state.ko_criteria_scores,
            "personal_contact_data": self.flow.state.personal_contact_data,
        }

        CapsuleServices.push_capsule_data(self._cached_session.id, "TRAICIE_RQC", "1.0", {}, rqc_info)

        results = SelectionResult.create_for_type(self.type, self.type_version,)
        return results

    def execute_contact_time_evaluation_state(self) -> SpecialistResult:
        self.log_tuning("Traicie Selection Specialist contact_time_evaluation started", {})

        answer = TranslationServices.translate(self.tenant_id,
                                               random.choice(CONTACT_TIME_PROCESSED_MESSAGES),
                                               self.arguments.language)

        self.flow.state.answer = answer
        self.flow.state.phase = "rag"
        self.flow.state.interview_phase = "candidate_selected"
        self.flow.state.contact_time_prefs = self.arguments.form_values

        rqc_info = {
            "ko_criteria_scores": self.flow.state.ko_criteria_scores,
            "personal_contact_data": self.flow.state.personal_contact_data,
            "contact_time_prefs": self.flow.state.contact_time_prefs,
        }

        CapsuleServices.push_capsule_data(self._cached_session.id, "TRAICIE_RQC", "1.0", {}, rqc_info)

        results = SelectionResult.create_for_type(self.type, self.type_version,)

        return results

    def execute_no_valid_candidate_state(self, arguments: SpecialistArguments, formatted_context, citations) \
            -> SpecialistResult:
        self.log_tuning("Traicie Selection Specialist no_valid_candidate started", {})
        answer = TranslationServices.translate(self.tenant_id,
                                               random.choice(KO_CRITERIA_NOT_MET_MESSAGES),
                                               arguments.language)

        self.flow.state.answer = answer
        self.flow.state.phase = "no_valid_candidate"

        results = SelectionResult.create_for_type(self.type, self.type_version,)
        return results

    def execute_rag_state(self) -> None | SpecialistResult:
        self.log_tuning("Traicie Selection Specialist rag_state started", {})

        # Prepare & Execute Selection Flow
        formatted_context, citations = self._retrieve_context(self.arguments)
        self.flow.state.citations = citations
        tone_of_voice = self.specialist.configuration.get('tone_of_voice', 'Professional & Neutral')
        tone_of_voice_context = get_tone_of_voice_context(tone_of_voice)
        language_level = self.specialist.configuration.get('language_level', 'Standard')
        language_level_context = get_language_level_context(language_level)
        flow_inputs = {
            "language": self.arguments.language,
            "question": self.arguments.question,
            "context": formatted_context,
            "history": self.formatted_history,
            "name": self.specialist.configuration.get('name', ''),
            "tone_of_voice": tone_of_voice,
            "tone_of_voice_context": tone_of_voice_context,
            "language_level": language_level,
            "language_level_context": language_level_context,
        }
        flow_results = self.flow.kickoff(inputs=flow_inputs)

        # Handle the results - stored in the state object
        if self.flow.state.interview_mode == "RAG":
            # In case of RAG mode, we get a rag_output
            if self.flow.state.rag_output:
                if self.flow.state.rag_output.insufficient_info:
                    answer = TranslationServices.translate(self.tenant_id,
                                                           random.choice(INSUFFICIENT_INFORMATION_MESSAGES),
                                                           self.arguments.language)
                else:
                    answer = self.flow.state.rag_output.answer
            else:
                current_app.logger.error("No RAG output found in the state object!")
                answer = TranslationServices.translate(self.tenant_id,
                                                       random.choice(INSUFFICIENT_INFORMATION_MESSAGES),
                                                       self.arguments.language)
            interview_question = self._get_question_for_interview_phase()
            self.flow.state.answer = f"{self.flow.state.rag_output.answer}\n{interview_question}"
            self.flow.state.phase = "rag"
            self.flow.state.interview_phase = self.previous_interview_phase
        else:   # self.flow.state.interview_mode == "CHECK"
            if self.previous_interview_phase == "candidate_selected":   # We blijven in RAG mode
                interview_question = self._get_question_for_interview_phase()
                self.flow.state.answer = interview_question
                self.flow.state.phase = "rag"
                self.flow.state.interview_phase = "candidate_selected"
            else:
                if self.flow.state.affirmative_answer:
                    return self._execute_next_interview_phase()
                else:
                    self.flow.state.answer = self._respond_to_negative_answer()
                    self.flow.state.phase = "rag"
                    self.flow.state.interview_phase = self.previous_interview_phase

        results = SelectionResult.create_for_type(self.type, self.type_version,)
        return results

    def _get_ko_questions(self) -> KOQuestions:
        ko_questions_asset = db.session.query(EveAIAsset).filter(
            EveAIAsset.type == "TRAICIE_KO_CRITERIA_QUESTIONS",
            EveAIAsset.type_version == "1.0.0",
            EveAIAsset.configuration.is_not(None),
            EveAIAsset.configuration.has_key('specialist_id'),
            EveAIAsset.configuration['specialist_id'].astext.cast(db.Integer) == self.specialist_id
        ).first()

        if not ko_questions_asset:
            raise EveAISpecialistExecutionError(self.tenant_id, self.specialist_id, self.session_id,
                                                "No KO criteria questions found")

        # Register Asset Usage
        prompt_tokens = ko_questions_asset.prompt_tokens
        completion_tokens = ko_questions_asset.completion_tokens
        total_tokens = prompt_tokens + completion_tokens

        metrics = {
            'total_tokens': total_tokens,
            'prompt_tokens': prompt_tokens,
            'completion_tokens': completion_tokens,
            'time_elapsed': 0,
            'interaction_type': 'ASSET',
        }

        current_event.log_llm_metrics(metrics)

        ko_questions_data = minio_client.download_asset_file(self.tenant_id, ko_questions_asset.bucket_name,
                                                             ko_questions_asset.object_name)
        ko_questions = KOQuestions.from_json(ko_questions_data)

        return ko_questions

    def _prepare_ko_question_form(self, ko_questions: KOQuestions, current_ko_criterium: str, language: str) \
            -> Dict[str, Any]:
        fields = {}
        ko_question = ko_questions.get_by_title(current_ko_criterium)
        current_app.logger.debug(f"Preparing form for ko_question: {ko_question.to_json()}")
        allowed_values = [ko_question.answer_positive, ko_question.answer_negative]
        if ko_question.additional_answer_positive:
            current_app.logger.debug(f"Additional answer positive found for question {ko_question.title}")
            allowed_values.append(ko_question.additional_answer_positive)
        if ko_question.additional_answer_negative:
            current_app.logger.debug(f"Additional answer negative found for question {ko_question.title}")
            allowed_values.append(ko_question.additional_answer_negative)

        current_app.logger.debug(f"Allowed values: {allowed_values}")
        fields[ko_question.title] = {
            "name": ko_question.title,
            "description": ko_question.title,
            "context": ko_question.question,
            "type": "options",
            "required": True,
            "allowed_values": allowed_values
        }

        ko_form = {
            "type": "KO_CRITERIA_FORM",
            "version": "1.0.0",
            "name": f"Starter Question: {current_ko_criterium}",
            "icon": "verified",
            "fields": fields,
        }

        ko_form = TranslationServices.translate_config(self.tenant_id, ko_form, "fields", language)

        return ko_form

    def _get_question_for_interview_phase(self) -> str:
        question = None
        match self.previous_interview_phase:
            case "start_selection_procedure":
                question = random.choice(START_SELECTION_QUESTIONS_CTD)
            case "personal_contact_data_preparation":
                question = random.choice(CONTACT_DATA_QUESTIONS)
            case "candidate_selected":
                question = random.choice(SUCCESSFUL_ENDING_MESSAGES)

        translated_question = TranslationServices.translate(self.tenant_id, question, self.arguments.language)
        return translated_question

    def _respond_to_negative_answer(self) -> str:
        question = None
        match self.previous_interview_phase:
            case "start_selection_procedure":
                question =  random.choice(TRY_TO_START_SELECTION_QUESTIONS)
            case "personal_contact_data_preparation":
                question =  random.choice(NO_CONTACT_DATA_QUESTIONS)

        translated_question = TranslationServices.translate(self.tenant_id, question, self.arguments.language)
        return translated_question

    def _execute_next_interview_phase(self) -> SpecialistResult | None:
        match self.previous_interview_phase:
            case "start_selection_procedure":
                return self.execute_start_selection_procedure_state()
            case "personal_contact_data_preparation":
                return self.execute_personal_contact_data_preparation()

        return None


class SelectionKOCriteriumScore(BaseModel):
    criterium: Optional[str] = Field(None, alias="criterium")
    answer: Optional[str] = Field(None, alias="answer")
    score: Optional[int] = Field(None, alias="score")


class PersonalContactData(BaseModel):
    name: str = Field(..., description="Your name", alias="name")
    email: EmailStr = Field(..., description="Your Name", alias="email")
    phone: str = Field(..., description="Your Phone Number", alias="phone")
    address: Optional[str] = Field(None, description="Your Address", alias="address")
    zip: Optional[str] = Field(None, description="Postal Code", alias="zip")
    city: Optional[str] = Field(None, description="City", alias="city")
    country: Optional[str] = Field(None, description="Country", alias="country")
    consent: bool = Field(..., description="Consent", alias="consent")


class ContactTimePreferences(BaseModel):
    early: Optional[bool] = Field(None, description="Early", alias="early")
    late_morning: Optional[bool] = Field(None, description="Late Morning", alias="late_morning")
    afternoon: Optional[bool] = Field(None, description="Afternoon", alias="afternoon")
    evening: Optional[bool] = Field(None, description="Evening", alias="evening")
    other: Optional[str] = Field(None, description="Other", alias="other")


class SelectionInput(BaseModel):
    # RAG elements
    language: Optional[str] = Field(None, alias="language")
    question: Optional[str] = Field(None, alias="question")
    context: Optional[str] = Field(None, alias="context")
    citations: Optional[List[int]] = Field(None, alias="citations")
    history: Optional[str] = Field(None, alias="history")
    name: Optional[str] = Field(None, alias="name")
    # Selection elements
    region: Optional[str] = Field(None, alias="region")
    working_schedule: Optional[str] = Field(None, alias="working_schedule")
    start_date: Optional[date] = Field(None, alias="vacancy_text")
    interaction_mode: Optional[str] = Field(None, alias="interaction_mode")
    tone_of_voice: Optional[str] = Field(None, alias="tone_of_voice")
    tone_of_voice_context: Optional[str] = Field(None, alias="tone_of_voice_context")
    language_level: Optional[str] = Field(None, alias="language_level")
    language_level_context: Optional[str] = Field(None, alias="language_level_context")
    ko_criteria: Optional[List[Dict[str, str]]] = Field(None, alias="ko_criteria")
    field_values: Optional[Dict[str, Any]] = Field(None, alias="field_values")


class SelectionFlowState(EveAIFlowState):
    """Flow state for RAG specialist that automatically updates from task outputs"""
    input: Optional[SelectionInput] = None
    ai_question: Optional[str] = None
    rag_output: Optional[RAGOutput] = None
    current_ko_criterium: Optional[str] = None
    current_ko_criterium_idx: Optional[int] = None
    ko_criteria_scores: Optional[List[SelectionKOCriteriumScore]] = None
    personal_contact_data: Optional[PersonalContactData] = None
    contact_time_prefs: Optional[ContactTimePreferences] = None
    citations: Optional[List[Dict[str, Any]]] = None
    interview_phase: Optional[str] = None
    interview_mode: Optional[str] = None
    affirmative_answer: Optional[bool] = None


class SelectionResult(SpecialistResult):
    ai_question: Optional[str] = None
    rag_output: Optional[RAGOutput] = Field(None, alias="rag_output")
    ko_criteria_scores: Optional[List[SelectionKOCriteriumScore]] = Field(None, alias="ko_criteria_scores")
    personal_contact_data: Optional[PersonalContactData] = Field(None, alias="personal_contact_data")
    contact_time_prefs: Optional[ContactTimePreferences] = None
    interview_phase: Optional[str] = None
    interview_mode: Optional[str] = None
    affirmative_answer: Optional[bool] = None


class SelectionFlow(EveAICrewAIFlow[SelectionFlowState]):
    def __init__(self,
                 specialist_executor: CrewAIBaseSpecialistExecutor,
                 rag_crew: EveAICrewAICrew,
                 determination_crew: EveAICrewAICrew,
                 affirmative_answer_crew: EveAICrewAICrew,
                 **kwargs):
        super().__init__(specialist_executor, "Selection Specialist Flow", **kwargs)
        self.specialist_executor = specialist_executor
        self.rag_crew = rag_crew
        self.determination_crew = determination_crew
        self.affirmative_answer_crew = affirmative_answer_crew
        self.exception_raised = False

    @start()
    def process_inputs(self):
        return ""

    @listen(process_inputs)
    async def execute_determination(self):
        try:
            inputs = self.state.input.model_dump()
            crew_output = await self.determination_crew.kickoff_async(inputs=inputs)
            self.specialist_executor.log_tuning("Determination Crew Output", crew_output.model_dump())
            output_pydantic = crew_output.pydantic
            if not output_pydantic:
                raw_json = json.loads(crew_output.raw)
                output_pydantic = SelectionResult(**raw_json)
            self.state.interview_mode = output_pydantic.mode
            return output_pydantic

        except Exception as e:
            current_app.logger.error(f"Determination Crew Error: {e}")
            self.exception_raised = True
            raise e

    @router(execute_determination)
    def interview_mode_routing(self):
        # interview mode can be RAG or CHECK
        return self.state.interview_mode

    @listen("RAG")
    async def execute_rag(self):
        try:
            inputs = self.state.input.model_dump()
            crew_output = await self.rag_crew.kickoff_async(inputs=inputs)
            self.specialist_executor.log_tuning("Advanced RAG Crew Output", crew_output.model_dump())
            output_pydantic = crew_output.pydantic
            if not output_pydantic:
                raw_json = json.loads(crew_output.raw)
                output_pydantic = RAGOutput.model_validate(raw_json)
            self.state.rag_output = output_pydantic
            return output_pydantic
        except Exception as e:
            current_app.logger.error(f"CREW rag_crew Error: {str(e)}")
            self.exception_raised = True
            raise e

    @listen("CHECK")
    async def check_affirmative_answer(self):
        try:
            inputs = self.state.input.model_dump()
            crew_output = await self.affirmative_answer_crew.kickoff_async(inputs=inputs)
            self.specialist_executor.log_tuning("Traicie Check Affirmative Answer Crew Output", crew_output.model_dump())
            output_pydantic = crew_output.pydantic
            if not output_pydantic:
                raw_json = json.loads(crew_output.raw)
                output_pydantic = RAGOutput.model_validate(raw_json)
            self.state.affirmative_answer = output_pydantic.affirmative
            return output_pydantic
        except Exception as e:
            current_app.logger.error(f"CREW rag_crew Error: {str(e)}")
            self.exception_raised = True
            raise e

    async def kickoff_async(self, inputs=None):
        self.state.input = SelectionInput.model_validate(inputs)
        result = await super().kickoff_async(inputs)
        self.specialist_executor.log_tuning("Specialist Executor Output", self.state.model_dump())
        return self.state
