"""
Enhanced Date Activities System

This module provides:
- Multiple date activity types (dinner, movies, luxury options)
- Mini-game conversation mechanics
- Performance-based affinity gains
- Premium diamond-gated date options
- Comprehensive tracking of date history
"""

from typing import Dict, Any, List, Optional
import logging
from database import get_database_connection
from database.transactions import transaction
from monetization.diamond_economy import deduct_diamonds
from retention.daily_quests import update_quest_progress


# In-memory cache of activities (loaded from database on first use)
_activities_cache = {}


def get_activity(activity_name: str) -> Optional[Dict[str, Any]]:
    """
    Get activity definition by name.

    Loads from database and caches for performance.

    Args:
        activity_name: Name of the activity (e.g., 'dinner', 'luxury_restaurant')

    Returns:
        Activity dictionary or None if not found
    """
    global _activities_cache

    # Return from cache if available
    if activity_name in _activities_cache:
        return _activities_cache[activity_name]

    conn = None
    cursor = None

    try:
        conn = get_database_connection()
        cursor = conn.cursor(dictionary=True)

        cursor.execute("""
            SELECT activity_name, base_cost, premium_version, diamond_cost,
                   affinity_gain_min, affinity_gain_max, energy_cost,
                   minigame_questions
            FROM date_activities
            WHERE activity_name = %s
        """, (activity_name,))

        activity = cursor.fetchone()

        if activity:
            # Cache the result
            _activities_cache[activity_name] = activity

        return activity

    except Exception as e:
        logging.error(f"Error getting activity {activity_name}: {e}")
        return None

    finally:
        if cursor:
            cursor.close()
        if conn:
            conn.close()


def get_all_activities(include_premium: bool = True) -> List[Dict[str, Any]]:
    """
    Get all available date activities.

    Args:
        include_premium: Whether to include premium (diamond-gated) activities

    Returns:
        List of activity dictionaries
    """
    conn = None
    cursor = None

    try:
        conn = get_database_connection()
        cursor = conn.cursor(dictionary=True)

        if include_premium:
            cursor.execute("""
                SELECT activity_name, base_cost, premium_version, diamond_cost,
                       affinity_gain_min, affinity_gain_max, energy_cost
                FROM date_activities
                ORDER BY diamond_cost, activity_name
            """)
        else:
            cursor.execute("""
                SELECT activity_name, base_cost, premium_version, diamond_cost,
                       affinity_gain_min, affinity_gain_max, energy_cost
                FROM date_activities
                WHERE premium_version = FALSE
                ORDER BY activity_name
            """)

        return cursor.fetchall()

    except Exception as e:
        logging.error(f"Error getting all activities: {e}")
        return []

    finally:
        if cursor:
            cursor.close()
        if conn:
            conn.close()


def validate_date_prerequisites(player_id: int, npc_id: int, activity_name: str) -> Dict[str, Any]:
    """
    Validate that the player can go on this date.

    Checks:
    - Activity exists
    - Player has enough energy
    - Player has enough diamonds (if premium)
    - NPC exists and is available

    Args:
        player_id: Player's ID
        npc_id: NPC's ID
        activity_name: Name of the activity

    Returns:
        dict with 'valid' (bool) and 'message' (str) keys
    """
    # Get activity
    activity = get_activity(activity_name)
    if not activity:
        return {'valid': False, 'message': f'Activity "{activity_name}" not found'}

    conn = None
    cursor = None

    try:
        conn = get_database_connection()
        cursor = conn.cursor(dictionary=True)

        # Check player's energy and diamonds
        cursor.execute("""
            SELECT energy, diamonds
            FROM players
            WHERE id = %s
        """, (player_id,))

        player = cursor.fetchone()
        if not player:
            return {'valid': False, 'message': 'Player not found'}

        # Check energy
        if player['energy'] < activity['energy_cost']:
            return {
                'valid': False,
                'message': f'Not enough energy. Need {activity["energy_cost"]}, have {player["energy"]}'
            }

        # Check diamonds for premium activities
        if activity['premium_version'] and activity['diamond_cost'] > 0:
            if player['diamonds'] < activity['diamond_cost']:
                return {
                    'valid': False,
                    'message': f'Not enough diamonds. Need {activity["diamond_cost"]}, have {player["diamonds"]}'
                }

        # Check NPC exists (basic validation - can be extended)
        cursor.execute("SELECT id FROM persons WHERE id = %s", (npc_id,))
        npc = cursor.fetchone()
        if not npc:
            return {'valid': False, 'message': 'NPC not found'}

        return {'valid': True, 'message': 'Prerequisites met'}

    except Exception as e:
        logging.error(f"Error validating date prerequisites: {e}")
        return {'valid': False, 'message': 'Server error'}

    finally:
        if cursor:
            cursor.close()
        if conn:
            conn.close()


def calculate_affinity_gain(activity: Dict[str, Any], minigame_results: List[Dict[str, Any]]) -> int:
    """
    Calculate affinity gain based on mini-game performance.

    Args:
        activity: Activity dictionary with affinity_gain_min/max
        minigame_results: List of mini-game results with 'correct' boolean

    Returns:
        Affinity gain amount
    """
    if not minigame_results:
        # If no mini-game results, give base affinity (50% performance)
        min_affinity = activity['affinity_gain_min']
        max_affinity = activity['affinity_gain_max']
        return int(min_affinity + (max_affinity - min_affinity) * 0.5)

    # Calculate performance score
    correct_answers = sum(1 for r in minigame_results if r.get('correct', False))
    total_questions = len(minigame_results)
    performance = correct_answers / total_questions if total_questions > 0 else 0.5

    # Scale affinity gain based on performance
    min_affinity = activity['affinity_gain_min']
    max_affinity = activity['affinity_gain_max']
    base_gain = min_affinity + (max_affinity - min_affinity) * performance

    # Bonus for perfect score (20% boost)
    if performance == 1.0:
        base_gain *= 1.2

    return int(base_gain)


def generate_date_feedback(performance: float, activity_name: str) -> str:
    """
    Generate contextual feedback based on date performance.

    Args:
        performance: Performance score (0.0 to 1.0)
        activity_name: Name of the activity

    Returns:
        Feedback message
    """
    if performance == 1.0:
        messages = {
            'dinner': "Perfect date! Your conversation flowed naturally and you connected deeply.",
            'movies': "Amazing evening! You both enjoyed the movie and felt comfortable together.",
            'coffee': "Wonderful coffee date! You listened attentively and made them feel special.",
            'walk_in_park': "Beautiful walk! You shared many moments of connection and laughter.",
            'luxury_restaurant': "Exceptional evening! The ambiance was perfect and so was your connection.",
            'weekend_getaway': "Unforgettable trip! You created lasting memories together.",
            'cooking_together': "Fantastic cooking session! You worked as a great team.",
            'concert': "Incredible night! The music brought you closer together."
        }
        return messages.get(activity_name, "Perfect date! You both had an amazing time together.")

    elif performance >= 0.75:
        messages = {
            'dinner': "Great date! You had good conversation and enjoyed each other's company.",
            'movies': "Nice evening! You both enjoyed the experience.",
            'coffee': "Good coffee date! You connected on several topics.",
            'walk_in_park': "Pleasant walk! You enjoyed spending time together.",
            'luxury_restaurant': "Lovely evening! You both felt comfortable in the elegant setting.",
            'weekend_getaway': "Great trip! You made some wonderful memories.",
            'cooking_together': "Fun cooking experience! You worked well together.",
            'concert': "Great show! You both enjoyed the music."
        }
        return messages.get(activity_name, "Great date! You had a good time together.")

    elif performance >= 0.5:
        messages = {
            'dinner': "Decent date. Some good moments, though a few awkward silences.",
            'movies': "Okay evening. The movie was fine but the connection was just okay.",
            'coffee': "Average coffee date. Some conversation but nothing deep.",
            'walk_in_park': "Nice walk, though not much happened.",
            'luxury_restaurant': "Pleasant evening, though you could have connected more.",
            'weekend_getaway': "Good trip, but some missed opportunities for connection.",
            'cooking_together': "Fun enough, though coordination could have been better.",
            'concert': "Decent show, but you didn't connect much."
        }
        return messages.get(activity_name, "Decent date. It went okay.")

    else:
        messages = {
            'dinner': "Awkward date. The conversation didn't flow well.",
            'movies': "Uncomfortable evening. The chemistry wasn't there.",
            'coffee': "Stiff coffee date. You struggled to connect.",
            'walk_in_park': "Uncomfortable walk with many awkward silences.",
            'luxury_restaurant': "Tense evening despite the nice setting.",
            'weekend_getaway': "Difficult trip with several awkward moments.",
            'cooking_together': "Frustrating cooking session with poor coordination.",
            'concert': "Awkward night. The music couldn't save the lack of connection."
        }
        return messages.get(activity_name, "Awkward date. It didn't go well.")


def execute_date(player_id: int, npc_id: int, activity_name: str, minigame_results: List[Dict[str, Any]]) -> Dict[str, Any]:
    """
    Execute a date with mini-game scoring.

    This is the main function that:
    1. Validates prerequisites (energy, diamonds)
    2. Deducts costs (energy, diamonds)
    3. Calculates affinity gain based on mini-game performance
    4. Updates affinity
    5. Records date history
    6. Updates daily quests

    Args:
        player_id: Player's ID
        npc_id: NPC's ID
        activity_name: Name of the activity
        minigame_results: List of mini-game results [{'correct': bool}, ...]

    Returns:
        dict with result information:
        {
            'success': bool,
            'affinity_gain': int,
            'performance': float,
            'feedback': str,
            'message': str (if error)
        }
    """
    # Validate prerequisites
    validation = validate_date_prerequisites(player_id, npc_id, activity_name)
    if not validation['valid']:
        return {
            'success': False,
            'message': validation['message']
        }

    # Get activity data
    activity = get_activity(activity_name)
    if not activity:
        return {
            'success': False,
            'message': f'Activity "{activity_name}" not found'
        }

    # Check and deduct diamonds before transaction (to avoid nested transaction issues)
    diamonds_spent = 0
    if activity['premium_version'] and activity['diamond_cost'] > 0:
        diamond_result = deduct_diamonds(
            player_id,
            f"Date activity: {activity_name}",
            activity['diamond_cost']
        )
        if not diamond_result['success']:
            return {
                'success': False,
                'message': diamond_result['message']
            }
        diamonds_spent = activity['diamond_cost']

    # Calculate affinity gain
    affinity_gain = calculate_affinity_gain(activity, minigame_results)

    # Calculate performance for feedback
    if minigame_results:
        correct_answers = sum(1 for r in minigame_results if r.get('correct', False))
        performance = correct_answers / len(minigame_results)
    else:
        performance = 0.5

    try:
        # Use transaction context manager for atomic database operations
        with transaction() as (conn, cursor):
            # Deduct energy
            cursor.execute("""
                UPDATE players
                SET energy = energy - %s
                WHERE id = %s
            """, (activity['energy_cost'], player_id))

            # Update affinity in persons table (assuming there's an affinity column)
            # Note: This might need adjustment based on your actual schema
            cursor.execute("""
                UPDATE persons
                SET affinity = COALESCE(affinity, 0) + %s
                WHERE id = %s
            """, (affinity_gain, npc_id))

            # Record date history
            cursor.execute("""
                INSERT INTO date_history
                (player_id, npc_id, activity_name, performance_score,
                 affinity_gained, energy_spent, diamonds_spent)
                VALUES (%s, %s, %s, %s, %s, %s, %s)
            """, (player_id, npc_id, activity_name, performance,
                  affinity_gain, activity['energy_cost'], diamonds_spent))

            # Transaction automatically commits on success

        # Update daily quests (after transaction succeeds)
        try:
            update_quest_progress(player_id, 'go_on_date', 1)
            update_quest_progress(player_id, 'increase_affinity', affinity_gain)
        except Exception as e:
            logging.warning(f"Could not update quests: {e}")

        # Generate feedback
        feedback = generate_date_feedback(performance, activity_name)

        logging.info(f"Date executed: Player {player_id} + NPC {npc_id}, "
                    f"Activity: {activity_name}, Performance: {performance:.2f}, "
                    f"Affinity gained: {affinity_gain}")

        return {
            'success': True,
            'affinity_gain': affinity_gain,
            'performance': performance,
            'feedback': feedback,
            'energy_spent': activity['energy_cost'],
            'diamonds_spent': diamonds_spent
        }

    except Exception as e:
        # Transaction context manager handles rollback automatically
        logging.error(f"Error executing date: {e}", exc_info=True)
        return {
            'success': False,
            'message': 'Server error during date execution'
        }


def get_date_history(player_id: int, limit: int = 20) -> List[Dict[str, Any]]:
    """
    Get player's recent date history.

    Args:
        player_id: Player's ID
        limit: Maximum number of records to return

    Returns:
        List of date history dictionaries
    """
    conn = None
    cursor = None

    try:
        conn = get_database_connection()
        cursor = conn.cursor(dictionary=True)

        cursor.execute("""
            SELECT dh.*, p.firstname, p.lastname
            FROM date_history dh
            JOIN persons p ON dh.npc_id = p.id
            WHERE dh.player_id = %s
            ORDER BY dh.date_timestamp DESC
            LIMIT %s
        """, (player_id, limit))

        return cursor.fetchall()

    except Exception as e:
        logging.error(f"Error getting date history for player {player_id}: {e}")
        return []

    finally:
        if cursor:
            cursor.close()
        if conn:
            conn.close()


def get_date_statistics(player_id: int) -> Dict[str, Any]:
    """
    Get player's date statistics.

    Args:
        player_id: Player's ID

    Returns:
        Dictionary with date statistics
    """
    conn = None
    cursor = None

    try:
        conn = get_database_connection()
        cursor = conn.cursor(dictionary=True)

        cursor.execute("""
            SELECT
                COUNT(*) as total_dates,
                AVG(performance_score) as avg_performance,
                SUM(affinity_gained) as total_affinity_gained,
                MAX(performance_score) as best_performance,
                SUM(diamonds_spent) as total_diamonds_spent
            FROM date_history
            WHERE player_id = %s
        """, (player_id,))

        stats = cursor.fetchone()

        if not stats or stats['total_dates'] == 0:
            return {
                'total_dates': 0,
                'avg_performance': 0.0,
                'total_affinity_gained': 0,
                'best_performance': 0.0,
                'total_diamonds_spent': 0
            }

        return {
            'total_dates': stats['total_dates'],
            'avg_performance': round(float(stats['avg_performance']), 2),
            'total_affinity_gained': stats['total_affinity_gained'],
            'best_performance': round(float(stats['best_performance']), 2),
            'total_diamonds_spent': stats['total_diamonds_spent'] or 0
        }

    except Exception as e:
        logging.error(f"Error getting date statistics for player {player_id}: {e}")
        return {
            'total_dates': 0,
            'avg_performance': 0.0,
            'total_affinity_gained': 0,
            'best_performance': 0.0,
            'total_diamonds_spent': 0
        }

    finally:
        if cursor:
            cursor.close()
        if conn:
            conn.close()


def clear_activities_cache():
    """Clear the activities cache (useful for testing)."""
    global _activities_cache
    _activities_cache = {}
