# new event classes for conversions, these must be triggered by player or other events
import random,uuid,datetime,requests,json

class conversationMessage():
    def __init__(self,message,answerOptions=None,sender=None,sentiment=None,date=None,time=None):
        self.id = uuid.uuid4().hex
        self.message = message
        self.answerOptions = answerOptions
        self.sender = sender
        self.datetime = str(datetime.datetime.now())
        self.date = date
        self.time = time
        self.sentiment = sentiment
    def addAnswerOption(self,option):
        self.answerOptions.append(option)

class conversationObj():
    def __init__(self,character=None,cType=None):
        self.id = uuid.uuid4().hex
        self.type = "conversationEvent"
        self.cType = cType
        self.character = character
        self.conversation = []
        self.question = 0
        self.unread = True
    def addMessage(self,message,sender=None,data=None,sentiment=None,date=None,time=None):
        if (len(message) > 1):
            if (sender == None):
                sender = self.character
            message = conversationMessage(message,sender=sender,sentiment=sentiment,date=date,time=time)
            if (data):
                message.data = data
            self.conversation.append(message)
    def getConversation(self):
        return self.conversation
    def addAnswerOption(self,option):
        self.conversation[-1].addAnswerOption(option)
    def setAnswerOptions(self,options):
        self.conversation[-1].answerOptions = options
    def getAnswerOptions(self):
        return self.conversation[-1].answerOptions
    def createAnswerOptions(self,prompt):
        responses = ["Can you tell me more?", "That's all, goodbye."]
        self.setAnswerOptions(responses)


async def parseConversations(player, character):
    import types
    # Get all objects in the current module
    objects = globals().values()

    # Filter functions - only include functions defined in THIS module (not imported)
    current_module = __name__
    functions = [obj for obj in objects
                 if isinstance(obj, types.FunctionType)
                 and obj.__module__ == current_module]

    # Filter out parseConversations itself to avoid recursion
    # Also filter out helper functions that don't match the conversation event signature
    excluded_functions = [
        parseConversations.__name__,
        conversationInit.__name__,
        'getOpenAIResponse',
        'sendCharacterMessage',
        'getFallbackResponse',
        'detect_verbosity_level',
        'get_verbosity_prompt_hint',
        'getOpenAIDescription',
        'getPersonDescription',
        'saveConversationMessage'
    ]
    functions = [fn for fn in functions if fn.__name__ not in excluded_functions]

    results = []

    # Run functions and store results
    for function in functions:
        try:
            result = await function(player, character, False, True)
            if result != False and result.get('button'):
                results.append(result)
        except Exception as e:
            print(f"Function {function.__name__} raised an error: {str(e)}")

    return results



def conversationInit(player, character, cType=None, response=False):
    # Get function from global scope and call it with parameters
    if cType and cType in globals():
        return globals()[cType](player, character, response)

# def askDate(player,character,response=False,check=False):
#     fname = 'askDate'
#     button = "Ask on a date"
#     if check:
#         check = character.status == 'alive' and character.partner == None and character.affinity > 20
#         if (not check):
#             return False
#         return {'button':button,'fname':fname}
#     from functions import get_person
#     if not response:
#         conversation = conversationObj(character,fname)
#         character = get_person(player,character)
#         player.conversations.append(conversation)
#         firstname = character.firstname.lower().capitalize()
#         message = "Hey " + firstname + ", I was wondering if you'd want to go on a date with me?"
#         conversation.addMessage(message,player.c.id)
        
#         if (character.familyLevel != 0):
#             conversation.addMessage("Umm, I'm not really interested in dating a family member... that's kind of weird.")
#             character.affinity += -10
#             return conversation
#         if (character.sex == player.c.sex):
#             conversation.addMessage("Hey I don't swing that way... but I appreciate the offer. Maybe we can just be friends?")
#             return conversation
#         if (character.ageYears - player.c.ageYears > 2 or player.c.ageYears - character.ageYears > 2):
#             conversation.addMessage("Hey, I'm not really interested in dating someone that much older/younger than me...")
#             character.affinity += -10
#             return conversation
            
#         if (character.familiarity > 50 and character.affinity > 20):
#             conversation.addMessage("Sure, I'd love to! What did you have in mind?")
#             conversation.setAnswerOptions(["Dinner and a movie","A walk in the park","A study session"])
#             character.affinity += 10
#         else:
#             conversation.addMessage("I'm sorry, but we don't really get along.")
#             character.affinity += -10
#     if response:
#         response = int(response)
#         conversation = player.conversations[-1]
#         responseText = conversation.getAnswerOptions()[response]
#         conversation.addMessage(responseText,player.c.id)
#         conversation.question += 1
#         character = get_person(player,conversation.character)
#         if (conversation.question == 1):
#             if (response == 0 or response == 1 or random.randint(0,9) > 5 or character.affinity > 60):
#                 conversation.addMessage("Great! I'll meet you there.")
#                 player.messageQueue.append("You have a date with " + character.firstname + " at the " + responseText + ".")
#                 message = "You have a date with " + character.firstname + " at the " + responseText + "."
#                 from functions import oneTimeEvent,upcoming_saturday
#                 player.c.oneTimeEvents.append(oneTimeEvent("Date",message,upcoming_saturday(player.date),location=False,hour=0))
#                 character.affinity += 10
#             else:
#                 conversation.addMessage("Umm, do you think we could do something else?")
#                 conversation.setAnswerOptions(["Dinner and a movie","A walk in the park"])
#                 conversation.question -= 1
#                 character.affinity += -10
#     return conversation

async def activity(player,character,response=False,check=False):
    fname = 'activity'
    button = "What's up?"
    affinityChange = 10
    if check:
        check = character.status == 'alive' 
        if (not check):
            return False
        return {'button':button,'fname':fname,'affinityChange':affinityChange}
    from functions import get_person
    if not response:
        conversation = conversationObj(character,fname)
        character = get_person(player,character)
        character.affinity += affinityChange
        player.conversations.append(conversation)
        messages = ["What have you been up to lately?", "What kind of things have you been doing?"]
        conversation.addMessage(random.choice(messages),player.c.id, date = player.date, time = player.time)
        await getOpenAIResponse(conversation,character,player)
        
    if response:
        conversation = player.conversations[-1]
        try:
            # Try to find response in answer options
            response_index = conversation.getAnswerOptions().index(response)
            responseText = conversation.getAnswerOptions()[response_index]
        except (ValueError, AttributeError):
            # Response is not in list (AI-generated or custom text), use as-is
            responseText = response

        conversation.addMessage(responseText,player.c.id, date = player.date, time = player.time)
        conversation.question += 1
        character = get_person(player,conversation.character)
        if (conversation.question >= 0):
            await getOpenAIResponse(conversation,character,player)
    return conversation

# def bully(player,character,response=False,check=False):
#     fname = 'bully'
#     button = "Bully"
#     affinityChange = -10
#     if check:
#         check = character.status == 'alive' and character.occupation == 'student'
#         if (not check):
#             return False
#         return {'button':button,'fname':fname,'affinityChange':affinityChange}
#     from functions import get_person
#     if not response:
#         conversation = conversationObj(character,fname)
#         character = get_person(player,character)
#         character.affinity += affinityChange
#         player.conversations.append(conversation)
#         firstname = character.firstname.lower.capitalize()
#         messages = ["Hey loser, I was wondering if you'd want to give me your lunch money?", "Wow, you're such a loser. I bet you don't even have any friends.", "Umm, what kind of loser wears that?"]
#         conversation.addMessage(random.choice(messages),player.c.id)
#         await getOpenAIResponse(conversation,character,player,"You are a human who can't stand up for themselves, give a max 8 word unique response to this:")
#         character.affinity += -10
#     if response:
#         conversation = player.conversations[-1]
#         response = conversation.getAnswerOptions().index(response)
#         responseText = conversation.getAnswerOptions()[response]
#         conversation.addMessage(responseText,player.c.id)
#         conversation.question += 1
#         character = get_person(player,conversation.character)
#         if (conversation.question >= 0):
#             await getOpenAIResponse(conversation,character,player)
#     return conversation

async def checkIn(player,character,response=False,check=False):
    fname = 'checkIn'
    button = "Check In"
    affinityChange = 10
    if check:
        check = character.status == 'alive' and character.occupation == 'student'
        if (not check):
            return False
        return {'button':button,'fname':fname,'affinityChange':affinityChange}
    from functions import get_person
    if not response:
        conversation = conversationObj(character,fname)
        character = get_person(player,character)
        character.affinity += affinityChange
        player.conversations.append(conversation)
        messages = ["Hey! How are you doing?", "Hey, how's it going?", "Hey, how are you?"]
        conversation.addMessage(messages[random.randint(0,2)],player.c.id,date=player.date,time=player.time)
        await getOpenAIResponse(conversation,character,player)
        
    if response:
        conversation = player.conversations[-1]
        try:
            # Try to find response in answer options
            response_index = conversation.getAnswerOptions().index(response)
            responseText = conversation.getAnswerOptions()[response_index]
        except (ValueError, AttributeError):
            # Response is not in list (AI-generated or custom text), use as-is
            responseText = response
        conversation.addMessage(responseText,player.c.id,date=player.date,time=player.time)
        conversation.question += 1
        character = get_person(player,conversation.character)
        if (conversation.question >= 0):
            await getOpenAIResponse(conversation,character,player)
    return conversation

async def askAboutDay(player,character,response=False,check=False):
    fname = 'askAboutDay'
    button = "Ask About Day"
    affinityChange = 10
    if check:
        check = character.status == 'alive'
        if (not check):
            return False
        return {'button':button,'fname':fname,'affinityChange':affinityChange}
    from functions import get_person
    if not response:
        conversation = conversationObj(character,fname)
        character = get_person(player,character)
        character.affinity += affinityChange
        player.conversations.append(conversation)
        messages = ["Hey, how was your day?", "Hey, how has your day been?", "Hey, how are you feeling today?"]
        conversation.addMessage(messages[random.randint(0,2)],player.c.id,date=player.date,time=player.time)
        await getOpenAIResponse(conversation,character,player)
    if response:
        conversation = player.conversations[-1]
        try:
            # Try to find response in answer options
            response_index = conversation.getAnswerOptions().index(response)
            responseText = conversation.getAnswerOptions()[response_index]
        except (ValueError, AttributeError):
            # Response is not in list (AI-generated or custom text), use as-is
            responseText = response
        conversation.addMessage(responseText,player.c.id,date=player.date,time=player.time)
        conversation.question += 1
        character = get_person(player,conversation.character)
        if (conversation.question >= 0):
            await getOpenAIResponse(conversation,character,player)
    return conversation

# def tellJoke(player,character,response=False,check=False):
#     fname = 'tellJoke'
#     button = "Tell Joke"
#     affinityChange = 10
#     if check:
#         check = character.status == 'alive' 
#         if (not check):
#             return False
#         return {'button':button,'fname':fname,'affinityChange':affinityChange}
#     from functions import get_person
#     if not response:
#         conversation = conversationObj(character,fname)
#         character = get_person(player,character)
#         character.affinity += affinityChange
#         print(str(character.affinity) + " " + character.firstname)
#         player.conversations.append(conversation)
#         messages = ["Hey, I heard a funny joke today. Wanna hear it?", "Hey, I heard a funny joke today. Wanna hear it?", "Hey, I heard a funny joke today. Wanna hear it?"]
#         conversation.addMessage(messages[random.randint(0,2)],player.c.id)
#         await getOpenAIResponse(conversation,character,player)
#     if response:
#         conversation = player.conversations[-1]
#         response = conversation.getAnswerOptions().index(response)
#         responseText = conversation.getAnswerOptions()[response]
#         conversation.addMessage(responseText,player.c.id)
#         conversation.question += 1
#         character = get_person(player,conversation.character)
#         if (conversation.question >= 0):
#             await getOpenAIResponse(conversation,character,player)
#     return conversation

async def flatter(player,character,response=False,check=False):
    fname = 'flatter'
    button = "Flatter"
    affinityChange = 10
    if check:
        check = character.status == 'alive' 
        if (not check):
            return False
        return {'button':button,'fname':fname,'affinityChange':affinityChange}
    from functions import get_person
    if not response:
        conversation = conversationObj(character,fname)
        character = get_person(player,character)
        character.affinity += affinityChange
        player.conversations.append(conversation)
        messages = ["Hey, you look really nice today!", 
            "Hey, I really like your outfit!", 
            "You know, you're really smart!",
            "Your creativity really shines in your work!",
            "You have an incredible sense of humor.",
            "Your perspective always brings fresh insights.",
            "I admire your dedication and passion.",
            "You are really good at what you do.",
            "You always know how to lighten the mood.",
            "Your confidence is really inspiring.",
            "I appreciate your thoughtfulness and kindness.",
            "You have a talent for making people feel comfortable.",
            "Your positivity is infectious."]

        conversation.addMessage(random.choice(messages),player.c.id, date = player.date, time = player.time)
        character.affinity += 15
    if response:
        conversation = player.conversations[-1]
        conversation.question += 1
        character = get_person(player,conversation.character)
        if (conversation.question >= 0):
            await getOpenAIResponse(conversation,character,player)
    return conversation

async def studySession(player,character,response=False,check=False):
    fname = 'studySession'
    button = "Ask to study"
    affinityChange = 10
    if check:
        check = character.status == 'alive' and character.occupation == 'student'
        if (not check):
            return False
        return {'button':button,'fname':fname,'affinityChange':affinityChange}
    from functions import get_person
    if not response:
        conversation = conversationObj(character,fname)
        character = get_person(player,character)
        player.conversations.append(conversation)
        firstname = character.firstname
        message = "Hey " + firstname + ", I was wondering if you'd want to study for the upcoming test with me?"
        conversation.addMessage(message,player.c.id,date=player.date,time=player.time)
        if ('classmate' not in character.relationships):
            conversation.addMessage("Hey, I'm not your classmate...",date=player.date,time=player.time)
            return conversation
            
        if (character.familiarity > 50):
            if (character.affinity > 20):
                conversation.addMessage("Sure, I'd love to! Where do you want to meet?",date=player.date,time=player.time)
                conversation.setAnswerOptions(["The library","The park"])
                conversation.addAnswerOption("My place")
            elif (character.affinity < -20):
                conversation.addMessage("Umm, I don't think that's a good idea.",date=player.date,time=player.time)
            else:
                conversation.addMessage("I'm sorry, but we don't really get along.",date=player.date,time=player.time)
        else:
            conversation.addMessage("I'm sorry, I don't really know you that well.",date=player.date,time=player.time)
    if response:
        response = int(response)
        conversation = player.conversations[-1]
        responseText = conversation.getAnswerOptions()[response]
        conversation.addMessage(responseText,player.c.id,date=player.date,time=player.time)
        conversation.question += 1
        character = get_person(player,conversation.character)
        if (conversation.question == 1):
            if (response == 0 or response == 1 or random.randint(0,9) > 5 or character.affinity > 60):
                conversation.addMessage("Great! I'll meet you there.",date=player.date,time=player.time)
                player.messageQueue.append("You have a study session with " + character.firstname + " at the " + responseText + ".")
                character.affinity += 10
            else:
                conversation.addMessage("Umm, I don't think I'm comfortable with that.",date=player.date,time=player.time)
                character.affinity += -10
    return conversation
                
async def chat(player, character, response=False, check=False):
    fname = 'chat'
    button = "Chat"
    # add prereqs
    if check:
        check = character.status == 'alive'
        if not check:
            return False
        return {'button': button, 'fname': fname}

    from functions import get_person

    # Check if a conversation with this character already exists
    existing_conversation = next((conv for conv in player.conversations if conv.character == character), None)

    if not existing_conversation:
        print("no existing conversation")
        conversation = conversationObj(character, fname)
        player.conversations.append(conversation)
        character = get_person(player, character)
        firstname = character.firstname.lower().capitalize()
        messages = ["Hey " + firstname + ", what keeps you busy in life?",
                    "Hey " + firstname + ", tell me about yourself.",
                    "Hey " + firstname + ", what do you like to do for fun?"]
        message = random.choice(messages)
        if (response != False):
            message = response
        print("Message: " + message)

        conversation.addMessage(message, player.c.id, date = player.date, time = player.time)

    else:
        print("response")
        # Use the existing conversation for response
        conversation = existing_conversation
        character = get_person(player, conversation.character)
        saveConversationMessage(response,character.id,player.c.id,player.c.id,player)
        await getOpenAIResponse(conversation, character, player)

    return conversation


# def summarize_conversation(conversation):
#     import openai
#     # Combine all messages into a single string
#     conversation_text = ' '.join([f"{message.sender}: {message.message}" for message in conversation.conversation])
    
#     # Call OpenAI to get a summary of the conversation
#     response = openai.Completion.create(
#         engine="davinci",
#         prompt=f"Summarize the following conversation:\n{conversation_text}",
#         max_tokens=100,
#         temperature=0.2,
#         top_p=1.0
#     )
    
#     # Extract and return the summary from the response
#     summary = response['choices'][0]['text'].strip()
#     return summary


import asyncio, random
from openai import AsyncOpenAI
from character.character_manager import getOpenAIDescription, getPersonDescription
from utils.helpers import remove_text_before_first_colon
from database.db_operations import saveConversationMessage
from config import config
from rate_limiter import check_openai_rate_limit
from character_memory import CharacterMemory
from api_usage_tracker import tracker as api_tracker

# Create OpenAI async client
openai_client = AsyncOpenAI(api_key=config.OPENAI_API_KEY)

async def sendCharacterMessage(player, character):
    # this is when the character messages you!
    print("sending character message")
    characterID = character.id
    # find conersation from player.activeConversations where the character id matches
    conversation = next((conv for conv in player.conversations if conv.character == characterID), None)
    if not conversation:
        #create a new conversation
        conversation = conversationObj(characterID, "chat")
        player.conversations.append(conversation)

    # Import relationship templates
    from conversation_templates import get_relationship_config
    from messaging_style import get_messaging_style_prompt
    from functions import get_relationship

    # Get relationship-specific configuration
    relationship_config = get_relationship_config(character)

    # Get relationship object for messaging style modifiers
    relationship = get_relationship(player, character.id)

    # Generate messaging style prompt based on character traits + relationship modifiers
    messaging_style_prompt = get_messaging_style_prompt(character, relationship, player)

    # Constructing the descriptions
    character_description = getOpenAIDescription(character)
    player_description = getPersonDescription(player.c)
    affinity_description = "positive" if character.affinity > 0 else "negative" if character.affinity < 0 else "neutral"
    familiarity_description = "close" if character.familiarity > 50 else "casual" if 25 <= character.familiarity <= 50 else "distant"
    character_relationship = character.relationships[0] if character.relationships else "acquaintance"

    prompt_template = (
        "Talk like a human would on a messaging app. Phrase things casually, keeping your responses under 150 characters. "
        "Don't start messages with greetings like Hey, Hi, or Hello frequently. "
        f"{relationship_config['prompt']} "
        f"{relationship_config['boundaries']} "
        f"{messaging_style_prompt} "  # Inject messaging style traits
        "Maintain a {affinity_description} attitude towards the player, with whom you have a {familiarity_description} relationship. "
        "Never reveal that you are an AI. If the player is rude, be rude back. If the player is inappropriate, tell them they'll be reported. "
        "At the end of your message, indicate whether your opinion of the player has improved, declined, or remained neutral "
        "by appending <<positive>>, <<negative>>, or <<neutral>> respectively. "
        "{character_description}. You are talking to: {player_description}. Remember: you are a human, never break character. "
        "Message them as a cold message, you are not responding to anything. This is the first message in the conversation. "
        "Send a fun, unique message that will make them want to respond. Make sure your message is fitting for your relationship as a '{character_relationship}' of the player."
    )

    # Formatting the prompt
    formatted_prompt = prompt_template.format(
        character_description=character_description,
        player_description=player_description,
        affinity_description=affinity_description,
        familiarity_description=familiarity_description,
        character_relationship=character_relationship
    )

    messageList = []
    for message in conversation.conversation[-10:]:  # Only take the last 6 messages
        role = "user" if message.sender == player.c.id else "assistant"
        content = message.message.strip()  # Ensure message is trimmed
        messageList.append({"role": role, "content": content})
    messageList.insert(0, {"role": "system", "content": formatted_prompt})
    # Use the global OpenAI client
    try:
        # Build API parameters based on model type
        model_name = config.CONVERSATION_MODEL_NAME
        supports_verbosity = model_name.startswith('gpt-5')
        tokens = 75 + random.randint(0, 100)

        if supports_verbosity:
            # GPT-5 parameters
            api_params = {
                'model': model_name,
                'messages': messageList,
                'max_completion_tokens': tokens
            }
        else:
            # GPT-4 and earlier models
            api_params = {
                'model': model_name,
                'messages': messageList,
                'max_tokens': tokens,
                'temperature': 0.8,
                'frequency_penalty': 1,
                'presence_penalty': 0.5
            }

        result = await asyncio.wait_for(
            openai_client.chat.completions.create(**api_params),
            timeout=15
        )
    except asyncio.TimeoutError:
        print("OpenAI API call timed out")
    
    response_content = result.choices[0].message.content
    response_content = remove_text_before_first_colon(response_content)
    sentiment = "unknown"  # Default sentiment
    sentiment_keys = {"<<positive>>": "positive", "<<negative>>": "negative", "<<neutral>>": "neutral"}

    for key, value in sentiment_keys.items():
        if key in response_content:
            sentiment = value
            response_content = response_content.replace(key, "").strip()
            break

    conversation.addMessage(response_content, sentiment=sentiment, date=player.date, time=player.time)
    saveConversationMessage(response_content, characterID, player.c.id, characterID, player)


class ConversationContextManager:
    """
    Manages conversation context with intelligent message compaction.
    Keeps recent messages and summarizes older ones to maintain full context
    while optimizing token usage.
    """

    def __init__(self, max_recent_messages=5, summary_interval=10):
        """
        Initialize context manager.

        Args:
            max_recent_messages: Number of recent messages to keep in full (default: 5)
            summary_interval: Number of old messages before triggering summarization (default: 10)

        Note: Summaries can be very comprehensive (up to 20,000 tokens) for long conversations
        to preserve context while keeping recent messages unsummarized for quality.
        """
        self.max_recent_messages = max_recent_messages
        self.summary_interval = summary_interval

    async def build_context(self, conversation, character, player):
        """
        Build optimized context for API call with smart message compaction.
        Only summarizes when approaching context limit.
        Keeps up to 50% of context limit as recent unsummarized messages.

        Returns list of messages including summary if needed to fit context.
        """
        messages = conversation.conversation

        # Get model context limit
        model_name = config.CONVERSATION_MODEL_NAME
        context_limit = self._get_context_limit(model_name)

        # Estimate current token usage (rough estimate: ~4 chars per token)
        all_messages_formatted = self._format_messages(messages, player)
        estimated_tokens = sum(len(msg['content']) for msg in all_messages_formatted) // 4

        # Use 75% of context limit as threshold for summarization
        summarization_threshold = int(context_limit * 0.75)

        print(f"Conversation tokens: ~{estimated_tokens}/{context_limit} (threshold: {summarization_threshold})")

        # If we're under the threshold, return all messages
        if estimated_tokens < summarization_threshold:
            print(f"Under context limit - using all {len(messages)} messages without summarization")
            return all_messages_formatted

        # We're approaching context limit - summarize older messages
        print(f"Approaching context limit - summarizing older messages")

        # Calculate how many recent messages to keep (up to 50% of context limit)
        recent_messages_budget = int(context_limit * 0.5)

        # Work backwards from most recent message to find how many fit in 50% budget
        recent_messages = []
        cumulative_tokens = 0

        for msg in reversed(messages):
            msg_formatted = self._format_messages([msg], player)[0]
            msg_tokens = len(msg_formatted['content']) // 4

            if cumulative_tokens + msg_tokens <= recent_messages_budget:
                recent_messages.insert(0, msg)  # Insert at beginning to maintain order
                cumulative_tokens += msg_tokens
            else:
                break

        # Ensure we keep at least 5 messages for quality
        if len(recent_messages) < 5 and len(messages) >= 5:
            recent_messages = messages[-5:]

        # Calculate old messages to summarize
        num_recent = len(recent_messages)
        old_messages = messages[:-num_recent] if num_recent > 0 else messages

        print(f"Keeping {num_recent} recent messages (~{cumulative_tokens} tokens), summarizing {len(old_messages)} older messages")

        # Get or create summary of old messages
        summary = await self._get_or_create_summary(conversation, old_messages, character, player)

        # Build context with summary + recent messages
        context = []

        # Add summary as system message if available
        if summary:
            context.append({
                "role": "system",
                "content": f"Previous conversation summary: {summary}"
            })

        # Add recent messages
        context.extend(self._format_messages(recent_messages, player))

        return context

    def _get_context_limit(self, model_name):
        """Get the context window size for the model."""
        # Context limits for different models
        if 'gpt-5' in model_name.lower():
            return 400000  # 400k tokens
        elif 'gpt-4o' in model_name.lower() or 'gpt-4-turbo' in model_name.lower():
            return 128000  # 128k tokens
        elif 'gpt-4' in model_name.lower():
            return 8192    # 8k tokens for base GPT-4
        elif 'gpt-3.5' in model_name.lower():
            return 16385   # 16k tokens
        else:
            return 128000  # Default to 128k for unknown models

    def _format_messages(self, messages, player):
        """Format messages for API call"""
        formatted = []
        for message in messages:
            role = "user" if message.sender == player.c.id else "assistant"
            formatted.append({
                "role": role,
                "content": message.message.strip()
            })
        return formatted

    async def _get_or_create_summary(self, conversation, old_messages, character, player):
        """
        Get cached summary or create new one.
        """
        # Check if summary exists and is up-to-date
        if hasattr(conversation, 'summary') and hasattr(conversation, 'summary_message_count'):
            if conversation.summary_message_count == len(old_messages):
                print(f"Using cached summary (covers {conversation.summary_message_count} messages)")
                return conversation.summary

        # Create new summary
        print(f"Creating summary for {len(old_messages)} old messages...")
        summary = await self._summarize_messages(conversation, old_messages, character, player)

        # Cache summary in conversation object
        conversation.summary = summary
        conversation.summary_message_count = len(old_messages)

        return summary

    async def _summarize_messages(self, conversation, messages, character, player):
        """
        Use AI to summarize older messages into comprehensive context.
        Allows large summaries (up to 20,000 tokens) for long conversations.
        """
        if not messages:
            return ""

        # Format messages for summarization
        conversation_text = "\n".join([
            f"{'Player' if msg.sender == player.c.id else character.firstname}: {msg.message}"
            for msg in messages
        ])

        # Calculate dynamic token limit based on conversation length
        # For longer conversations, allow more comprehensive summaries
        num_messages = len(messages)
        if num_messages < 20:
            max_summary_tokens = 500  # Short conversations: brief summary
        elif num_messages < 50:
            max_summary_tokens = 2000  # Medium conversations: moderate summary
        elif num_messages < 100:
            max_summary_tokens = 5000  # Long conversations: detailed summary
        else:
            max_summary_tokens = 20000  # Very long conversations: comprehensive summary

        # Create comprehensive summarization prompt
        summary_prompt = f"""Provide a comprehensive summary of this conversation between the player and {character.firstname}.

For this {num_messages}-message conversation, capture:
- All major topics discussed and how they evolved
- Important decisions, plans, or commitments made
- Emotional moments, relationship developments, and sentiment changes
- Key facts, preferences, or information shared
- Any conflicts, resolutions, or turning points
- Recurring themes or inside jokes

Be thorough and preserve important context. The summary can be detailed.

Conversation:
{conversation_text}

Comprehensive Summary:"""

        try:
            # Build API params based on model type
            summary_params = {
                'model': config.CONVERSATION_MODEL_NAME,
                'messages': [{"role": "user", "content": summary_prompt}]
            }

            # GPT-5 uses max_completion_tokens, earlier models use max_tokens
            # Also, GPT-5 doesn't support temperature
            if config.CONVERSATION_MODEL_NAME.startswith('gpt-5'):
                summary_params['max_completion_tokens'] = max_summary_tokens
            else:
                summary_params['max_tokens'] = max_summary_tokens
                summary_params['temperature'] = 0.3  # Lower temp for factual accuracy

            # Longer timeout for comprehensive summaries (scales with expected tokens)
            timeout_seconds = min(60, 10 + (max_summary_tokens / 1000))  # ~10-60 seconds

            result = await asyncio.wait_for(
                openai_client.chat.completions.create(**summary_params),
                timeout=timeout_seconds
            )

            summary = result.choices[0].message.content.strip()
            print(f"Created summary: {summary[:100]}...")

            # Track summarization API usage
            try:
                conversation_id = conversation.id if hasattr(conversation, 'id') else None
                cost = api_tracker.track_usage(
                    player.c.id,
                    conversation_id,
                    config.CONVERSATION_MODEL_NAME,
                    {
                        'prompt_tokens': result.usage.prompt_tokens,
                        'completion_tokens': result.usage.completion_tokens,
                        'total_tokens': result.usage.total_tokens
                    },
                    purpose='summarization'
                )
                print(f"Summarization cost: ${cost:.6f}")
            except Exception as e:
                print(f"Failed to track summarization usage: {e}")

            return summary

        except Exception as e:
            print(f"Failed to create summary: {e}")
            # Fallback: Create simple summary from first few messages
            preview_messages = messages[:3]
            simple_summary = "Earlier conversation topics: " + ", ".join([
                msg.message[:30] + "..." if len(msg.message) > 30 else msg.message
                for msg in preview_messages
            ])
            return simple_summary


async def getFallbackResponse(conversation, character, player):
    """
    Generate fallback response when API is unavailable or rate limited.
    Uses context-appropriate canned responses based on conversation type and relationship.
    """
    print("Using fallback response (API unavailable or rate limited)")

    # Get conversation context
    conv_type = conversation.cType if hasattr(conversation, 'cType') else 'chat'
    affinity = character.affinity if hasattr(character, 'affinity') else 50
    last_messages = conversation.conversation[-3:] if len(conversation.conversation) >= 3 else conversation.conversation

    # Fallback responses based on conversation type and affinity
    high_affinity_responses = [
        "I'm a bit busy right now, can we talk later?",
        "Sorry, I got distracted. What were we talking about?",
        "Hey, I need to run but let's catch up soon!",
        "I'm tied up at the moment, but I'll message you later!",
        "Can we continue this conversation another time?"
    ]

    medium_affinity_responses = [
        "I have to go, talk later.",
        "Busy right now, sorry.",
        "Let's talk another time.",
        "I can't chat right now.",
        "Maybe later, I'm busy."
    ]

    low_affinity_responses = [
        "I don't have time for this.",
        "Whatever, I'm busy.",
        "I have to go.",
        "Not now.",
        "I'm busy."
    ]

    # Select response based on affinity
    if affinity > 60:
        response_text = random.choice(high_affinity_responses)
        sentiment = "neutral"
    elif affinity > 20:
        response_text = random.choice(medium_affinity_responses)
        sentiment = "neutral"
    else:
        response_text = random.choice(low_affinity_responses)
        sentiment = "negative"
        character.affinity -= 2  # Slight penalty for low affinity interaction

    # Add message to conversation
    conversation.addMessage(response_text, sentiment=sentiment, date=player.date, time=player.time)
    conversation.createAnswerOptions(response_text)

    # Save to database
    saveConversationMessage(response_text, character.id, player.c.id, character.id, player)

    # Return mock result object similar to OpenAI response
    class MockResult:
        class Choice:
            class Message:
                def __init__(self, content):
                    self.content = content
            def __init__(self, content):
                self.message = self.Message(content)

        def __init__(self, content):
            self.choices = [self.Choice(content)]

    return MockResult(response_text)


def detect_verbosity_level(conversation, character, player):
    """
    Analyze conversation context to determine appropriate response length.

    Returns: tuple (verbosity_level: str, max_tokens: int)
        - 'low': 30-60 tokens (quick replies, banter)
        - 'medium': 60-120 tokens (normal conversation)
        - 'high': 120-200 tokens (deep topics, explanations)
    """
    if not conversation.conversation or len(conversation.conversation) == 0:
        return ('low', 60)  # First message, keep it brief

    # Get last message from player
    last_messages = conversation.conversation[-3:]  # Look at last 3 messages
    last_player_message = None

    for msg in reversed(last_messages):
        if msg.sender == player.c.id:
            last_player_message = msg
            break

    if not last_player_message:
        return ('low', 60)

    message_text = last_player_message.message
    message_length = len(message_text)

    # Indicators for longer response
    has_question = '?' in message_text
    is_long_message = message_length > 100
    has_multiple_sentences = message_text.count('.') > 1 or message_text.count('!') > 1

    # Deep topic keywords (warrant thoughtful responses)
    deep_topics = ['why', 'how do you', 'what do you think', 'tell me about',
                   'can you explain', 'feelings', 'relationship', 'future',
                   'dream', 'goal', 'problem', 'worried', 'scared', 'excited']
    has_deep_topic = any(topic in message_text.lower() for topic in deep_topics)

    # Calculate score
    score = 0
    if has_question: score += 2
    if is_long_message: score += 2
    if has_multiple_sentences: score += 1
    if has_deep_topic: score += 2

    # Determine verbosity
    if score >= 4:
        return ('high', 180)  # Deep conversation
    elif score >= 2:
        return ('medium', 100)  # Normal engagement
    else:
        return ('low', 60)  # Quick reply


def get_verbosity_prompt_hint(verbosity_level):
    """
    Generate prompt-based verbosity instruction for models without native verbosity parameter.
    """
    hints = {
        'low': "Keep your response very brief - one or two sentences max, like a quick text message (10-20 words).",
        'medium': "Respond naturally with a full thought, but stay concise like a text conversation (20-40 words).",
        'high': "Take your time to give a thoughtful, complete response. This topic deserves depth (40-65 words)."
    }
    return hints.get(verbosity_level, hints['low'])


async def getOpenAIResponse(conversation, character, player, prompt=False, rescueMessage=False):
    print("getting open ai response")

    # Send typing indicator to client
    from app import get_websocket_for_player, sendDict
    websocket = get_websocket_for_player(str(player.id))
    if websocket:
        await sendDict(websocket, {
            'type': 'typingStatus',
            'characterId': character.id,
            'isTyping': True
        })

    # Check rate limit BEFORE making API call
    if not check_openai_rate_limit(player.c.id):
        print(f"Rate limit exceeded for player {player.c.id}, using fallback response")

        # Send rate limit warning to client
        if websocket:
            await sendDict(websocket, {
                'type': 'rateLimitWarning',
                'message': 'AI responses temporarily limited. You may receive shorter replies.',
                'retryAfter': 60
            })
            # Stop typing indicator
            await sendDict(websocket, {
                'type': 'typingStatus',
                'characterId': character.id,
                'isTyping': False
            })

        return await getFallbackResponse(conversation, character, player)

    # Import relationship templates
    from conversation_templates import get_relationship_config
    from messaging_style import get_messaging_style_prompt, update_conversation_messaging_modifiers
    from functions import get_relationship

    # Get relationship-specific configuration
    relationship_config = get_relationship_config(character)

    # Get relationship object for messaging style modifiers
    relationship = get_relationship(player, character.id)

    # Generate messaging style prompt based on character traits + relationship modifiers
    messaging_style_prompt = get_messaging_style_prompt(character, relationship, player)

    # Detect verbosity needs based on conversation context
    verbosity_level, max_tokens = detect_verbosity_level(conversation, character, player)

    # Constructing the descriptions
    character_description = getOpenAIDescription(character)
    player_description = getPersonDescription(player.c)
    affinity_description = "positive" if character.affinity > 0 else "negative" if character.affinity < 0 else "neutral"
    familiarity_description = "close" if character.familiarity > 50 else "casual" if 25 <= character.familiarity <= 50 else "distant"

    # Build relationship-aware prompt
    if rescueMessage:
        prompt_template = (
            "Please change the topic in a way that makes sense. Talk like a human would on a messaging app. "
            "Don't start messages with a greeting. Don't start messages with Hey, Hi, or Hello. You have already been talking. "
            f"{messaging_style_prompt} "  # Inject messaging style traits
            "{character_description}. You are talking to: {player_description}. "
            f"{relationship_config['prompt']} {relationship_config['boundaries']}"
        )
    else:
        # Check if using GPT-5 (supports verbosity parameter)
        model_provider = config.CONVERSATION_MODEL_PROVIDER
        model_name = config.CONVERSATION_MODEL_NAME
        supports_verbosity = model_name.startswith('gpt-5')

        # Build base prompt with relationship awareness and messaging style
        prompt_template = (
            "You are not an assistant. You are a real person having a text conversation. "
            "Don't start messages with greetings like Hey, Hi, or Hello frequently. "
            f"{relationship_config['prompt']} "
            f"{relationship_config['boundaries']} "
            f"{messaging_style_prompt} "  # Inject messaging style traits
            "Maintain a {affinity_description} attitude towards them, with a {familiarity_description} relationship. "
            "You will never reveal that you are an AI. If they're rude, you can be rude back. "

            # Natural conversation flow - not always questions
            "Respond naturally like humans do in text conversations. Mix up your response types:\n"
            "- Statements: 'Yeah I totally get that' or 'That sounds exhausting tbh'\n"
            "- Reactions: 'omg no way' or 'haha that's hilarious'\n"
            "- Observations: 'You always know how to make me laugh'\n"
            "- Just sharing: 'I had the worst day today' or 'same thing happened to me once'\n"
            "- Questions (when genuinely curious): 'Wait what happened after that?'\n"
            "Ask questions ONLY when you're genuinely curious about something they said, not just to keep the conversation going. "
            "Not every message needs to prompt a response. Sometimes the best reply is just a reaction or sharing your own thoughts. "
            "Sometimes conversations naturally wind down - it's okay to end with a statement if the topic feels complete. "

            "At the end of your message, indicate whether your opinion improved, declined, or stayed neutral "
            "by appending <<positive>>, <<negative>>, or <<neutral>>. "
            "{character_description}. You are talking to: {player_description}. Remember: you are a human, never break character."
        )

        # If model doesn't support verbosity parameter, add prompt hint
        if not supports_verbosity:
            verbosity_hint = get_verbosity_prompt_hint(verbosity_level)
            prompt_template = f"{verbosity_hint}\n\n{prompt_template}"

    # Load character memory for context
    memory = CharacterMemory(character.id, player.c.id)
    memory_context = memory.get_memory_context()

    # Formatting the prompt with memory context
    formatted_prompt = prompt_template.format(
        character_description=character_description,
        player_description=player_description,
        affinity_description=affinity_description,
        familiarity_description=familiarity_description
    )

    # Append memory context if available
    if memory_context:
        formatted_prompt += f"\n\n{memory_context}"

    # Get model configuration (already set above if not rescueMessage)
    if rescueMessage or 'model_provider' not in locals():
        model_provider = config.CONVERSATION_MODEL_PROVIDER
        model_name = config.CONVERSATION_MODEL_NAME
        supports_verbosity = model_name.startswith('gpt-5')

    # Use ConversationContextManager for intelligent message compaction
    # Keep last 5 messages in full, summarize the rest with generous token budget
    context_manager = ConversationContextManager(max_recent_messages=5)
    messageList = await context_manager.build_context(conversation, character, player)

    # Add system prompt (OpenAI format)
    messageList.insert(0, {"role": "system", "content": formatted_prompt})

    # Retry logic with exponential backoff
    retry_count = 0
    max_retries = 3
    backoff_delays = [1, 2, 4]  # Exponential backoff: 1s, 2s, 4s

    while retry_count < max_retries:
        try:
            if retry_count > 0:
                print(f"Retrying (attempt {retry_count + 1}/{max_retries})...")

            print(f"Calling {model_provider} API with model {model_name}... (verbosity: {verbosity_level}, max_tokens: {max_tokens})")

            # Currently only OpenAI is supported
            # To add new providers, add elif blocks here
            if model_provider == "openai":
                # Build API parameters
                # GPT-5 doesn't support temperature, presence_penalty, frequency_penalty
                if supports_verbosity:
                    # GPT-5 parameters
                    api_params = {
                        'model': model_name,
                        'messages': messageList,
                        'max_completion_tokens': max_tokens,
                        'verbosity': verbosity_level
                    }
                else:
                    # GPT-4 and earlier models
                    api_params = {
                        'model': model_name,
                        'messages': messageList,
                        'max_tokens': max_tokens,
                        'temperature': 0.8,
                        'frequency_penalty': 1,
                        'presence_penalty': 0.5
                    }

                result = await asyncio.wait_for(
                    openai_client.chat.completions.create(**api_params),
                    timeout=3.5
                )
                response_content = result.choices[0].message.content
            else:
                raise ValueError(f"Unsupported model provider: {model_provider}. Currently only 'openai' is supported.")
            if "Sorry, I can't" in response_content or "Sorry, I cannot" in response_content or "sorry, I can't" in response_content or "sorry, I cannot" in response_content or "Sorry," in response_content or "language model" in response_content or " AI " in response_content or "AI," in response_content:
                print("Undesired response detected, retrying...")
                retry_count += 1
                continue  # Skip to the next iteration to retry

            sentiment = ""
            for sentiment_key in ["<<positive>>", "<<negative>>", "<<neutral>>"]:
                if sentiment_key in response_content:
                    sentiment = sentiment_key.strip("<<>>")
                    response_content = response_content.replace(sentiment_key, "").strip()
                    break

            if sentiment == "positive":
                character.affinity += 5
            elif sentiment == "negative":
                character.affinity -= 5
            else:
                player.c.social += 1

            # Update messaging style modifiers based on conversation dynamics
            if relationship:
                conversation_length = len(conversation.conversation)
                update_conversation_messaging_modifiers(
                    relationship,
                    sentiment if sentiment else 'neutral',
                    conversation_length
                )

            response_content = remove_text_before_first_colon(response_content)

            conversation.addMessage(response_content, sentiment=sentiment, date=player.date, time=player.time)
            conversation.createAnswerOptions(response_content)
            saveConversationMessage(response_content,character.id,player.c.id,character.id,player)

            # Track API usage
            try:
                conversation_id = conversation.id if hasattr(conversation, 'id') else None
                cost = api_tracker.track_usage(
                    player.c.id,
                    conversation_id,
                    model_name,
                    result.usage._asdict() if hasattr(result.usage, '_asdict') else {
                        'prompt_tokens': result.usage.prompt_tokens,
                        'completion_tokens': result.usage.completion_tokens,
                        'total_tokens': result.usage.total_tokens
                    },
                    purpose='conversation'
                )
                print(f"API cost: ${cost:.6f}")
            except Exception as e:
                print(f"Failed to track API usage: {e}")

            # Extract facts periodically (every 5 messages)
            if len(conversation.conversation) % 5 == 0:
                try:
                    print(f"Extracting facts from conversation (message {len(conversation.conversation)})")
                    await memory.extract_facts(conversation, character)
                except Exception as e:
                    print(f"Failed to extract facts: {e}")

            # Stop typing indicator before returning
            if websocket:
                await sendDict(websocket, {
                    'type': 'typingStatus',
                    'characterId': character.id,
                    'isTyping': False
                })

            return result

        except asyncio.TimeoutError:
            print(f"OpenAI API call timed out (attempt {retry_count + 1}/{max_retries})")
            retry_count += 1
            if retry_count < max_retries:
                # Exponential backoff before retry
                await asyncio.sleep(backoff_delays[retry_count - 1])
            else:
                print("Maximum retry attempts reached due to timeout, using fallback")
                # Stop typing indicator
                if websocket:
                    await sendDict(websocket, {
                        'type': 'typingStatus',
                        'characterId': character.id,
                        'isTyping': False
                    })
                return await getFallbackResponse(conversation, character, player)

        except Exception as e:
            print(f"OpenAI API error: {e}")
            retry_count += 1
            if retry_count < max_retries:
                # Exponential backoff before retry
                await asyncio.sleep(backoff_delays[retry_count - 1])
            else:
                print("Maximum retry attempts reached due to error, using fallback")
                # Stop typing indicator
                if websocket:
                    await sendDict(websocket, {
                        'type': 'typingStatus',
                        'characterId': character.id,
                        'isTyping': False
                    })
                return await getFallbackResponse(conversation, character, player)

    # If we exit the loop without returning (shouldn't happen), use fallback
    print("Unexpected: exited retry loop without returning, using fallback")
    # Stop typing indicator
    if websocket:
        await sendDict(websocket, {
            'type': 'typingStatus',
            'characterId': character.id,
            'isTyping': False
        })
    return await getFallbackResponse(conversation, character, player)

# ============================================================================
# NEW CONVERSATION EVENTS - Section 10 from EVENT_IDEAS_QUICK_WINS.md
# ============================================================================

async def deepConversation(player, character, response=False, check=False):
    fname = 'deepConversation'
    button = "Let's talk about life"
    affinityChange = 15
    if check:
        check = character.status == 'alive' and player.c.ageYears >= 16
        if (not check):
            return False
        return {'button': button, 'fname': fname, 'affinityChange': affinityChange}
    from functions import get_person
    if not response:
        conversation = conversationObj(character, fname)
        character = get_person(player, character)
        character.affinity += affinityChange
        player.conversations.append(conversation)
        messages = [
            "I've been thinking a lot about life lately. Want to talk about it?",
            "Can we have a real conversation about something meaningful?",
            "What do you think the meaning of life is?",
            "I want to talk about something deeper than usual."
        ]
        conversation.addMessage(random.choice(messages), player.c.id, date=player.date, time=player.time)
        await getOpenAIResponse(conversation, character, player)
    if response:
        conversation = player.conversations[-1]
        try:
            response_index = conversation.getAnswerOptions().index(response)
            responseText = conversation.getAnswerOptions()[response_index]
        except (ValueError, AttributeError):
            responseText = response
        conversation.addMessage(responseText, player.c.id, date=player.date, time=player.time)
        conversation.question += 1
        character = get_person(player, conversation.character)
        if (conversation.question >= 0):
            await getOpenAIResponse(conversation, character, player)
    return conversation

async def gossipSession(player, character, response=False, check=False):
    fname = 'gossipSession'
    button = "Did you hear about..."
    affinityChange = 10
    if check:
        # Check if character is alive and player is old enough
        check = character.status == 'alive' and player.c.ageYears >= 12
        if (not check):
            return False
        return {'button': button, 'fname': fname, 'affinityChange': affinityChange}
    from functions import get_person
    if not response:
        conversation = conversationObj(character, fname)
        character = get_person(player, character)
        character.affinity += affinityChange
        player.conversations.append(conversation)
        messages = [
            "Did you hear what happened?",
            "I have some gossip to share...",
            "You'll never believe what I heard!",
            "So I heard something interesting today..."
        ]
        conversation.addMessage(random.choice(messages), player.c.id, date=player.date, time=player.time)
        await getOpenAIResponse(conversation, character, player)
    if response:
        conversation = player.conversations[-1]
        try:
            response_index = conversation.getAnswerOptions().index(response)
            responseText = conversation.getAnswerOptions()[response_index]
        except (ValueError, AttributeError):
            responseText = response
        conversation.addMessage(responseText, player.c.id, date=player.date, time=player.time)
        conversation.question += 1
        character = get_person(player, conversation.character)
        if (conversation.question >= 0):
            await getOpenAIResponse(conversation, character, player)
    return conversation

async def ventingSession(player, character, response=False, check=False):
    fname = 'ventingSession'
    button = "I need to vent"
    affinityChange = 10
    if check:
        # Check if player is stressed or unhappy
        check = character.status == 'alive' and player.c.ageYears >= 12 and (player.c.happiness < 50 or getattr(player.c, 'stress', 0) > 50)
        if (not check):
            return False
        return {'button': button, 'fname': fname, 'affinityChange': affinityChange}
    from functions import get_person
    if not response:
        conversation = conversationObj(character, fname)
        character = get_person(player, character)
        character.affinity += affinityChange
        player.conversations.append(conversation)
        # Apply the stress relief effect
        player.c.happiness += 10
        if hasattr(player.c, 'stress'):
            player.c.stress -= 15
        messages = [
            "I'm so stressed right now, can I just vent for a minute?",
            "I really need to get something off my chest...",
            "I'm having a rough time, can I talk to you about it?",
            "Everything is just overwhelming me right now..."
        ]
        conversation.addMessage(random.choice(messages), player.c.id, date=player.date, time=player.time)
        await getOpenAIResponse(conversation, character, player)
    if response:
        conversation = player.conversations[-1]
        try:
            response_index = conversation.getAnswerOptions().index(response)
            responseText = conversation.getAnswerOptions()[response_index]
        except (ValueError, AttributeError):
            responseText = response
        conversation.addMessage(responseText, player.c.id, date=player.date, time=player.time)
        conversation.question += 1
        character = get_person(player, conversation.character)
        if (conversation.question >= 0):
            await getOpenAIResponse(conversation, character, player)
    return conversation

async def askAdvice(player, character, response=False, check=False):
    fname = 'askAdvice'
    button = "Can I ask your advice?"
    affinityChange = 10
    if check:
        check = character.status == 'alive' and player.c.ageYears >= 10
        if (not check):
            return False
        return {'button': button, 'fname': fname, 'affinityChange': affinityChange}
    from functions import get_person
    if not response:
        conversation = conversationObj(character, fname)
        character = get_person(player, character)
        character.affinity += affinityChange
        player.conversations.append(conversation)
        messages = [
            "I could really use your advice on something...",
            "Can I get your opinion on something?",
            "I have a problem and I'd like to hear your perspective.",
            "What would you do if you were in my situation?"
        ]
        conversation.addMessage(random.choice(messages), player.c.id, date=player.date, time=player.time)
        await getOpenAIResponse(conversation, character, player)
    if response:
        conversation = player.conversations[-1]
        try:
            response_index = conversation.getAnswerOptions().index(response)
            responseText = conversation.getAnswerOptions()[response_index]
        except (ValueError, AttributeError):
            responseText = response
        conversation.addMessage(responseText, player.c.id, date=player.date, time=player.time)
        conversation.question += 1
        character = get_person(player, conversation.character)
        if (conversation.question >= 0):
            await getOpenAIResponse(conversation, character, player)
    return conversation

async def reminisce(player, character, response=False, check=False):
    fname = 'reminisce'
    button = "Remember when..."
    affinityChange = 20
    if check:
        check = character.status == 'alive' and player.c.ageYears >= 10 and character.familiarity > 60
        if (not check):
            return False
        return {'button': button, 'fname': fname, 'affinityChange': affinityChange}
    from functions import get_person
    if not response:
        conversation = conversationObj(character, fname)
        character = get_person(player, character)
        character.affinity += affinityChange
        player.conversations.append(conversation)
        messages = [
            "Remember when we used to hang out all the time?",
            "I was thinking about all the good times we've had together...",
            "Do you remember that time when...?",
            "I miss the old days when we would..."
        ]
        conversation.addMessage(random.choice(messages), player.c.id, date=player.date, time=player.time)
        await getOpenAIResponse(conversation, character, player)
    if response:
        conversation = player.conversations[-1]
        try:
            response_index = conversation.getAnswerOptions().index(response)
            responseText = conversation.getAnswerOptions()[response_index]
        except (ValueError, AttributeError):
            responseText = response
        conversation.addMessage(responseText, player.c.id, date=player.date, time=player.time)
        conversation.question += 1
        character = get_person(player, conversation.character)
        if (conversation.question >= 0):
            await getOpenAIResponse(conversation, character, player)
    return conversation

async def apologizeConvo(player, character, response=False, check=False):
    fname = 'apologizeConvo'
    button = "I need to apologize"
    affinityChange = 25
    if check:
        check = character.status == 'alive' and player.c.ageYears >= 8 and character.affinity < 30
        if (not check):
            return False
        return {'button': button, 'fname': fname, 'affinityChange': affinityChange}
    from functions import get_person
    if not response:
        conversation = conversationObj(character, fname)
        character = get_person(player, character)
        character.affinity += affinityChange
        player.conversations.append(conversation)
        messages = [
            "I owe you an apology...",
            "I'm really sorry about how things have been between us.",
            "I want to make things right between us.",
            "Can we talk? I feel bad about what happened..."
        ]
        conversation.addMessage(random.choice(messages), player.c.id, date=player.date, time=player.time)
        await getOpenAIResponse(conversation, character, player)
    if response:
        conversation = player.conversations[-1]
        try:
            response_index = conversation.getAnswerOptions().index(response)
            responseText = conversation.getAnswerOptions()[response_index]
        except (ValueError, AttributeError):
            responseText = response
        conversation.addMessage(responseText, player.c.id, date=player.date, time=player.time)
        conversation.question += 1
        character = get_person(player, conversation.character)
        if (conversation.question >= 0):
            await getOpenAIResponse(conversation, character, player)
    return conversation

async def shareDream(player, character, response=False, check=False):
    fname = 'shareDream'
    button = "I had the weirdest dream"
    affinityChange = 5
    if check:
        check = character.status == 'alive' and player.c.ageYears >= 8
        if (not check):
            return False
        return {'button': button, 'fname': fname, 'affinityChange': affinityChange}
    from functions import get_person
    if not response:
        conversation = conversationObj(character, fname)
        character = get_person(player, character)
        character.affinity += affinityChange
        player.conversations.append(conversation)
        messages = [
            "I had the strangest dream last night!",
            "You'll never believe the dream I had...",
            "I keep having this weird dream...",
            "So I had this really bizarre dream about..."
        ]
        conversation.addMessage(random.choice(messages), player.c.id, date=player.date, time=player.time)
        await getOpenAIResponse(conversation, character, player)
    if response:
        conversation = player.conversations[-1]
        try:
            response_index = conversation.getAnswerOptions().index(response)
            responseText = conversation.getAnswerOptions()[response_index]
        except (ValueError, AttributeError):
            responseText = response
        conversation.addMessage(responseText, player.c.id, date=player.date, time=player.time)
        conversation.question += 1
        character = get_person(player, conversation.character)
        if (conversation.question >= 0):
            await getOpenAIResponse(conversation, character, player)
    return conversation

async def askFavor(player, character, response=False, check=False):
    fname = 'askFavor'
    button = "Can you help me with something?"
    affinityChange = -5
    if check:
        check = character.status == 'alive' and player.c.ageYears >= 10
        if (not check):
            return False
        return {'button': button, 'fname': fname, 'affinityChange': affinityChange}
    from functions import get_person
    if not response:
        conversation = conversationObj(character, fname)
        character = get_person(player, character)
        character.affinity += affinityChange
        player.conversations.append(conversation)
        messages = [
            "I need a favor... can you help me out?",
            "Would you be able to help me with something?",
            "I could really use your help with something...",
            "Can I ask you for a favor?"
        ]
        conversation.addMessage(random.choice(messages), player.c.id, date=player.date, time=player.time)
        await getOpenAIResponse(conversation, character, player)
    if response:
        conversation = player.conversations[-1]
        try:
            response_index = conversation.getAnswerOptions().index(response)
            responseText = conversation.getAnswerOptions()[response_index]
        except (ValueError, AttributeError):
            responseText = response
        conversation.addMessage(responseText, player.c.id, date=player.date, time=player.time)
        conversation.question += 1
        character = get_person(player, conversation.character)
        if (conversation.question >= 0):
            await getOpenAIResponse(conversation, character, player)
    return conversation

async def debateOpinion(player, character, response=False, check=False):
    fname = 'debateOpinion'
    button = "I disagree about..."
    affinityChange = 5  # Can vary based on AI response
    if check:
        check = character.status == 'alive' and player.c.ageYears >= 14
        if (not check):
            return False
        return {'button': button, 'fname': fname, 'affinityChange': affinityChange}
    from functions import get_person
    if not response:
        conversation = conversationObj(character, fname)
        character = get_person(player, character)
        character.affinity += affinityChange
        player.conversations.append(conversation)
        messages = [
            "I actually disagree with you about that...",
            "Can we debate this for a second? I see it differently.",
            "I have a different perspective on this topic.",
            "I think we might have different opinions on this..."
        ]
        conversation.addMessage(random.choice(messages), player.c.id, date=player.date, time=player.time)
        await getOpenAIResponse(conversation, character, player)
    if response:
        conversation = player.conversations[-1]
        try:
            response_index = conversation.getAnswerOptions().index(response)
            responseText = conversation.getAnswerOptions()[response_index]
        except (ValueError, AttributeError):
            responseText = response
        conversation.addMessage(responseText, player.c.id, date=player.date, time=player.time)
        conversation.question += 1
        character = get_person(player, conversation.character)
        if (conversation.question >= 0):
            await getOpenAIResponse(conversation, character, player)
    return conversation

async def shareExcitement(player, character, response=False, check=False):
    fname = 'shareExcitement'
    button = "You won't believe what happened!"
    affinityChange = 10
    if check:
        check = character.status == 'alive' and player.c.ageYears >= 6 and player.c.happiness > 70
        if (not check):
            return False
        return {'button': button, 'fname': fname, 'affinityChange': affinityChange}
    from functions import get_person
    if not response:
        conversation = conversationObj(character, fname)
        character = get_person(player, character)
        character.affinity += affinityChange
        player.c.happiness += 5  # Sharing excitement makes you happier
        player.conversations.append(conversation)
        messages = [
            "You won't believe what just happened!",
            "I have the best news to share!",
            "I'm so excited, I have to tell you something!",
            "Guess what?! Something amazing happened!"
        ]
        conversation.addMessage(random.choice(messages), player.c.id, date=player.date, time=player.time)
        await getOpenAIResponse(conversation, character, player)
    if response:
        conversation = player.conversations[-1]
        try:
            response_index = conversation.getAnswerOptions().index(response)
            responseText = conversation.getAnswerOptions()[response_index]
        except (ValueError, AttributeError):
            responseText = response
        conversation.addMessage(responseText, player.c.id, date=player.date, time=player.time)
        conversation.question += 1
        character = get_person(player, conversation.character)
        if (conversation.question >= 0):
            await getOpenAIResponse(conversation, character, player)
    return conversation

# Export all conversation event functions
__all__ = [
    'activity',
    'checkIn',
    'askAboutDay',
    'flatter',
    'studySession',
    'chat',
    'deepConversation',
    'gossipSession',
    'ventingSession',
    'askAdvice',
    'reminisce',
    'apologizeConvo',
    'shareDream',
    'askFavor',
    'debateOpinion',
    'shareExcitement',
    'parseConversations',
    'conversationInit'
]
