"""
Relationship Events System for BaoLife

This module handles dynamic relationship events like arguments, anniversaries,
jealousy, proposals, and breakup threats. Events trigger based on relationship
conditions and offer resolution options with affinity and diamond costs.
"""

import random
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Any
from database import get_database_connection
from database.transactions import transaction
from events import questionEvent, answerOption
from config import config


# Relationship event definitions
RELATIONSHIP_EVENTS = [
    {
        'type': 'argument',
        'trigger': 'affinity_drop_15',
        'description': "You and {partner_name} had a disagreement. They seem upset.",
        'options': [
            {'choice': 'apologize', 'label': 'Apologize sincerely', 'affinity_change': 10, 'diamond_cost': 0},
            {'choice': 'buy_gift', 'label': 'Buy a gift to make up', 'affinity_change': 25, 'diamond_cost': 20},
            {'choice': 'ignore', 'label': 'Give them space', 'affinity_change': -10, 'diamond_cost': 0}
        ]
    },
    {
        'type': 'anniversary',
        'trigger': 'days_together_365',
        'description': "It's your {years}year anniversary with {partner_name}!",
        'options': [
            {'choice': 'dinner_date', 'label': 'Take them to dinner', 'affinity_change': 5, 'diamond_cost': 0},
            {'choice': 'luxury_date', 'label': 'Plan a luxury date', 'affinity_change': 15, 'diamond_cost': 50},
            {'choice': 'forgot', 'label': 'You forgot...', 'affinity_change': -20, 'diamond_cost': 0}
        ]
    },
    {
        'type': 'jealousy',
        'trigger': 'talked_to_other_person',
        'description': "{partner_name} seems jealous of your interactions with others.",
        'options': [
            {'choice': 'reassure', 'label': 'Reassure them', 'affinity_change': 5, 'diamond_cost': 0},
            {'choice': 'dismiss', 'label': 'Dismiss their concerns', 'affinity_change': -15, 'diamond_cost': 0},
            {'choice': 'grand_gesture', 'label': 'Make a grand gesture', 'affinity_change': 20, 'diamond_cost': 30}
        ]
    },
    {
        'type': 'proposal',
        'trigger': 'affinity_above_90',
        'description': "{partner_name} wants to take your relationship to the next level.",
        'options': [
            {'choice': 'accept', 'label': 'Accept the proposal', 'affinity_change': 30, 'diamond_cost': 0},
            {'choice': 'wait', 'label': 'Say you need more time', 'affinity_change': -5, 'diamond_cost': 0},
            {'choice': 'decline', 'label': 'Decline politely', 'affinity_change': -40, 'diamond_cost': 0}
        ]
    },
    {
        'type': 'breakup_threat',
        'trigger': 'affinity_below_20',
        'description': "{partner_name} is threatening to end the relationship.",
        'options': [
            {'choice': 'fix_it', 'label': 'Promise to do better', 'affinity_change': 15, 'diamond_cost': 0},
            {'choice': 'therapy', 'label': 'Suggest couples therapy', 'affinity_change': 25, 'diamond_cost': 40},
            {'choice': 'let_go', 'label': "Accept it's over", 'affinity_change': 0, 'diamond_cost': 0}
        ]
    },
    {
        'type': 'gift_request',
        'trigger': 'birthday_or_holiday',
        'description': "{partner_name} mentioned wanting something special.",
        'options': [
            {'choice': 'thoughtful_gift', 'label': 'Give a thoughtful gift', 'affinity_change': 10, 'diamond_cost': 0},
            {'choice': 'expensive_gift', 'label': 'Buy an expensive gift', 'affinity_change': 20, 'diamond_cost': 25},
            {'choice': 'no_gift', 'label': 'No gift this time', 'affinity_change': -15, 'diamond_cost': 0}
        ]
    },
    {
        'type': 'family_meeting',
        'trigger': 'relationship_serious',
        'description': "{partner_name} wants you to meet their family.",
        'options': [
            {'choice': 'excited', 'label': 'I would love to!', 'affinity_change': 15, 'diamond_cost': 0},
            {'choice': 'nervous', 'label': "I'm a bit nervous...", 'affinity_change': 5, 'diamond_cost': 0},
            {'choice': 'refuse', 'label': 'Not ready for that', 'affinity_change': -20, 'diamond_cost': 0}
        ]
    }
]


def get_affinity_change_24h(player_id: int, npc_id: int) -> int:
    """
    Calculate affinity change in last 24 hours

    Args:
        player_id: Player character ID
        npc_id: NPC character ID

    Returns:
        Affinity change (can be negative)
    """
    try:
        conn = get_database_connection()
        cursor = conn.cursor(dictionary=True)

        yesterday = datetime.now() - timedelta(days=1)

        cursor.execute("""
            SELECT SUM(affinity_change) as total_change
            FROM player_relationship_events
            WHERE player_id = %s
                AND npc_id = %s
                AND occurred_date >= %s
        """, (player_id, npc_id, yesterday))

        result = cursor.fetchone()
        cursor.close()
        conn.close()

        return result['total_change'] if result and result['total_change'] else 0
    except Exception as e:
        print(f"Error calculating affinity change: {e}")
        return 0


def check_relationship_triggers(player, npc) -> Optional[Dict]:
    """
    Check if any relationship events should trigger for a given NPC

    Args:
        player: Player object
        npc: NPC person object

    Returns:
        Event dict if triggered, None otherwise
    """
    # Skip if NPC is not in a romantic relationship
    if 'partner' not in getattr(npc, 'relationships', []) and \
       'dating' not in getattr(npc, 'relationships', []):
        return None

    # Get NPC affinity
    affinity = getattr(npc, 'affinity', 0)

    # Check argument trigger (affinity drop)
    affinity_change = get_affinity_change_24h(player.c.id, npc.id)
    if affinity_change <= -15:
        # Check if we haven't had this event recently (cooldown)
        if not has_recent_event(player.c.id, npc.id, 'argument', days=7):
            return get_event_by_type('argument', npc)

    # Check anniversary trigger
    if hasattr(player, 'relData'):
        for rel in player.relData:
            if rel.person1_id == npc.id or rel.person2_id == npc.id:
                days_together = (datetime.now() - rel.started_date).days
                if days_together > 0 and days_together % 365 == 0:
                    # Check if anniversary not already triggered this year
                    if not has_recent_event(player.c.id, npc.id, 'anniversary', days=365):
                        years = days_together // 365
                        return get_event_by_type('anniversary', npc, years=years)

    # Check jealousy trigger (random with some relationship context)
    if random.random() < 0.01 and affinity < 70:  # 1% chance if affinity not high
        if not has_recent_event(player.c.id, npc.id, 'jealousy', days=14):
            return get_event_by_type('jealousy', npc)

    # Check proposal trigger (high affinity)
    if affinity >= 90:
        # Only if not married and relationship is serious
        if not hasattr(player.c, 'married') or not player.c.married:
            if not has_recent_event(player.c.id, npc.id, 'proposal', days=180):
                return get_event_by_type('proposal', npc)

    # Check breakup threat trigger (low affinity)
    if affinity <= 20 and affinity > -50:  # In danger zone but not completely hostile
        if not has_recent_event(player.c.id, npc.id, 'breakup_threat', days=30):
            return get_event_by_type('breakup_threat', npc)

    # Check gift request trigger (birthdays, holidays)
    # This would need integration with calendar system
    if random.random() < 0.005:  # 0.5% chance per check
        if not has_recent_event(player.c.id, npc.id, 'gift_request', days=30):
            return get_event_by_type('gift_request', npc)

    # Check family meeting trigger (serious relationships)
    if affinity >= 70 and not has_met_family(player.c.id, npc.id):
        if random.random() < 0.02:  # 2% chance
            return get_event_by_type('family_meeting', npc)

    return None


def get_event_by_type(event_type: str, npc, **kwargs) -> Dict:
    """
    Get event definition by type and format with NPC data

    Args:
        event_type: Type of event
        npc: NPC person object
        **kwargs: Additional formatting parameters

    Returns:
        Formatted event dict
    """
    for event in RELATIONSHIP_EVENTS:
        if event['type'] == event_type:
            # Clone the event to avoid modifying the original
            formatted_event = event.copy()

            # Format description with NPC name
            partner_name = f"{npc.firstname} {npc.lastname}"
            description = event['description'].format(
                partner_name=partner_name,
                **kwargs
            )
            formatted_event['description'] = description
            formatted_event['npc_id'] = npc.id
            formatted_event['npc_name'] = partner_name

            return formatted_event

    return None


def has_recent_event(player_id: int, npc_id: int, event_type: str, days: int = 7) -> bool:
    """
    Check if an event has occurred recently (within N days)

    Args:
        player_id: Player ID
        npc_id: NPC ID
        event_type: Type of event to check
        days: Number of days to check back

    Returns:
        True if event occurred recently, False otherwise
    """
    try:
        conn = get_database_connection()
        cursor = conn.cursor(dictionary=True)

        cutoff_date = datetime.now() - timedelta(days=days)

        cursor.execute("""
            SELECT COUNT(*) as count
            FROM player_relationship_events pre
            JOIN relationship_events re ON pre.event_id = re.id
            WHERE pre.player_id = %s
                AND pre.npc_id = %s
                AND re.event_type = %s
                AND pre.occurred_date >= %s
        """, (player_id, npc_id, event_type, cutoff_date))

        result = cursor.fetchone()
        cursor.close()
        conn.close()

        return result['count'] > 0 if result else False
    except Exception as e:
        print(f"Error checking recent events: {e}")
        return False


def has_met_family(player_id: int, npc_id: int) -> bool:
    """
    Check if player has already met NPC's family

    Args:
        player_id: Player ID
        npc_id: NPC ID

    Returns:
        True if family meeting has occurred, False otherwise
    """
    try:
        conn = get_database_connection()
        cursor = conn.cursor(dictionary=True)

        cursor.execute("""
            SELECT COUNT(*) as count
            FROM player_relationship_events pre
            JOIN relationship_events re ON pre.event_id = re.id
            WHERE pre.player_id = %s
                AND pre.npc_id = %s
                AND re.event_type = 'family_meeting'
                AND pre.resolution_chosen IN ('excited', 'nervous')
        """, (player_id, npc_id))

        result = cursor.fetchone()
        cursor.close()
        conn.close()

        return result['count'] > 0 if result else False
    except Exception as e:
        print(f"Error checking family meeting: {e}")
        return False


def trigger_event(player, npc_id: int, event: Dict) -> Dict:
    """
    Trigger a relationship event and create it in the game

    Args:
        player: Player object
        npc_id: NPC ID
        event: Event dictionary

    Returns:
        Event data to be sent to client
    """
    # Create question event for the player
    question = questionEvent()
    question.id = f"relationship_event_{event['type']}_{npc_id}"
    question.message = event['description']
    question.type = 'relationshipEvent'
    question.npc_id = npc_id
    question.event_type = event['type']

    # Create answer options
    answers = []
    for option in event['options']:
        answer = answerOption(
            option['label'],
            option['choice'],
            energyCost=0,
            diamondCost=option['diamond_cost'],
            moneyCost=0
        )
        # Store affinity change in answer for later processing
        answer.affinityChange = option['affinity_change']
        answers.append(answer)

    question.answers = answers

    # Pause game for decision
    player.previousGameSpeed = player.gameSpeed
    player.gameSpeed = config.SPEED_QUESTION_PAUSE

    return question


def process_resolution(player, npc_id: int, event_type: str, resolution: str,
                       affinity_change: int, diamond_cost: int) -> Dict:
    """
    Process the player's resolution choice for a relationship event

    Args:
        player: Player object
        npc_id: NPC ID
        event_type: Type of event
        resolution: Chosen resolution
        affinity_change: Affinity change from this choice
        diamond_cost: Diamond cost for this choice

    Returns:
        Result dict with success status and message
    """
    # Check if player has enough diamonds and deduct them
    if diamond_cost > 0:
        from monetization.diamond_economy import deduct_diamonds
        diamond_result = deduct_diamonds(player.c.id, f"relationship_event_{event_type}", diamond_cost)
        if not diamond_result['success']:
            return diamond_result

    # Update NPC affinity
    for person in player.r:
        if person.id == npc_id:
            person.affinity += affinity_change
            # Cap affinity at -100 to 100
            person.affinity = max(-100, min(100, person.affinity))
            break

    # Persist affinity change and record event in a single transaction
    try:
        with transaction() as (conn, cursor):
            # Update affinity in database
            cursor.execute("UPDATE persons SET affinity = %s WHERE id = %s", (person.affinity, npc_id))

            # Get event_id from relationship_events table
            cursor.execute("""
                SELECT id FROM relationship_events
                WHERE event_type = %s
                LIMIT 1
            """, (event_type,))

            result = cursor.fetchone()
            event_id = result[0] if result else None

            if event_id:
                cursor.execute("""
                    INSERT INTO player_relationship_events
                    (player_id, npc_id, event_id, resolution_chosen, affinity_change, diamond_cost)
                    VALUES (%s, %s, %s, %s, %s, %s)
                """, (player.c.id, npc_id, event_id, resolution, affinity_change, diamond_cost))

            # Transaction automatically commits on success
    except Exception as e:
        print(f"Error updating affinity and recording relationship event: {e}")

    # Resume game speed
    player.gameSpeed = player.previousGameSpeed

    # Generate result message
    npc_name = None
    for person in player.r:
        if person.id == npc_id:
            npc_name = f"{person.firstname} {person.lastname}"
            break

    affinity_text = ""
    if affinity_change > 0:
        affinity_text = f" Their affinity increased by {affinity_change}!"
    elif affinity_change < 0:
        affinity_text = f" Their affinity decreased by {abs(affinity_change)}."

    return {
        'success': True,
        'message': f"You resolved the situation with {npc_name}.{affinity_text}",
        'affinity_change': affinity_change,
        'diamond_cost': diamond_cost
    }


def get_event_history(player_id: int, npc_id: Optional[int] = None,
                     limit: int = 10) -> List[Dict]:
    """
    Get relationship event history for a player

    Args:
        player_id: Player ID
        npc_id: Optional NPC ID to filter by
        limit: Maximum number of events to return

    Returns:
        List of event history dicts
    """
    try:
        conn = get_database_connection()
        cursor = conn.cursor(dictionary=True)

        if npc_id:
            cursor.execute("""
                SELECT
                    pre.id,
                    pre.npc_id,
                    re.event_type,
                    re.description_template,
                    pre.resolution_chosen,
                    pre.affinity_change,
                    pre.diamond_cost,
                    pre.occurred_date
                FROM player_relationship_events pre
                JOIN relationship_events re ON pre.event_id = re.id
                WHERE pre.player_id = %s AND pre.npc_id = %s
                ORDER BY pre.occurred_date DESC
                LIMIT %s
            """, (player_id, npc_id, limit))
        else:
            cursor.execute("""
                SELECT
                    pre.id,
                    pre.npc_id,
                    re.event_type,
                    re.description_template,
                    pre.resolution_chosen,
                    pre.affinity_change,
                    pre.diamond_cost,
                    pre.occurred_date
                FROM player_relationship_events pre
                JOIN relationship_events re ON pre.event_id = re.id
                WHERE pre.player_id = %s
                ORDER BY pre.occurred_date DESC
                LIMIT %s
            """, (player_id, limit))

        results = cursor.fetchall()
        cursor.close()
        conn.close()

        return results
    except Exception as e:
        print(f"Error getting event history: {e}")
        return []


def get_relationship_stats(player_id: int, npc_id: int) -> Dict:
    """
    Get relationship statistics between player and NPC

    Args:
        player_id: Player ID
        npc_id: NPC ID

    Returns:
        Dict with relationship statistics
    """
    try:
        conn = get_database_connection()
        cursor = conn.cursor(dictionary=True)

        cursor.execute("""
            SELECT
                COUNT(*) as total_events,
                SUM(affinity_change) as total_affinity_change,
                SUM(diamond_cost) as total_diamonds_spent,
                MIN(occurred_date) as first_event,
                MAX(occurred_date) as last_event
            FROM player_relationship_events
            WHERE player_id = %s AND npc_id = %s
        """, (player_id, npc_id))

        result = cursor.fetchone()
        cursor.close()
        conn.close()

        return result if result else {
            'total_events': 0,
            'total_affinity_change': 0,
            'total_diamonds_spent': 0,
            'first_event': None,
            'last_event': None
        }
    except Exception as e:
        print(f"Error getting relationship stats: {e}")
        return {
            'total_events': 0,
            'total_affinity_change': 0,
            'total_diamonds_spent': 0,
            'first_event': None,
            'last_event': None
        }
