"""
Daily Quest System

Generates and tracks daily quests for player engagement.
Players receive 3 quests daily (easy/medium/hard) that award diamonds on completion.
"""

from typing import Dict, Any, List, Optional
from datetime import date, datetime
import logging
import random
import asyncio


# Quest template definitions (matching database)
QUEST_TEMPLATES = [
    # Easy quests (10 energy, quick)
    {'type': 'talk_to_characters', 'desc': 'Talk to 3 different characters', 'required': 3,
     'reward': 10, 'difficulty': 'easy', 'energy': 9, 'icon': 'message'},
    {'type': 'buy_item', 'desc': 'Buy an item from the store', 'required': 1,
     'reward': 5, 'difficulty': 'easy', 'energy': 0, 'icon': 'cart'},
    {'type': 'attend_class', 'desc': 'Attend 2 classes', 'required': 2,
     'reward': 8, 'difficulty': 'easy', 'energy': 6, 'icon': 'book'},
    {'type': 'socialize', 'desc': 'Socialize with friends', 'required': 2,
     'reward': 10, 'difficulty': 'easy', 'energy': 6, 'icon': 'person.2'},

    # Medium quests (20-30 energy)
    {'type': 'work_hours', 'desc': 'Work for 6 hours', 'required': 6,
     'reward': 15, 'difficulty': 'medium', 'energy': 18, 'icon': 'briefcase'},
    {'type': 'go_on_date', 'desc': 'Go on a date', 'required': 1,
     'reward': 20, 'difficulty': 'medium', 'energy': 15, 'icon': 'heart'},
    {'type': 'complete_activities', 'desc': 'Complete 5 activities', 'required': 5,
     'reward': 18, 'difficulty': 'medium', 'energy': 25, 'icon': 'checkmark.circle'},
    {'type': 'study', 'desc': 'Study for 4 hours', 'required': 4,
     'reward': 15, 'difficulty': 'medium', 'energy': 12, 'icon': 'book.fill'},

    # Hard quests (40+ energy)
    {'type': 'spend_energy', 'desc': 'Spend 50 energy on activities', 'required': 50,
     'reward': 25, 'difficulty': 'hard', 'energy': 50, 'icon': 'bolt'},
    {'type': 'earn_money', 'desc': 'Earn $500', 'required': 500,
     'reward': 30, 'difficulty': 'hard', 'energy': 40, 'icon': 'dollarsign'},
    {'type': 'increase_affinity', 'desc': 'Increase affinity by 20 points total', 'required': 20,
     'reward': 35, 'difficulty': 'hard', 'energy': 45, 'icon': 'heart.fill'},
]


async def initialize_quest_templates():
    """
    Insert quest template definitions into database.
    Called during server startup.
    Uses async database pool for proper connection management.
    """
    from database_async import execute_query

    try:
        for quest in QUEST_TEMPLATES:
            await execute_query(
                """INSERT INTO daily_quest_templates
                   (quest_type, description, progress_required, diamond_reward, difficulty, energy_cost, icon_name)
                   VALUES (%s, %s, %s, %s, %s, %s, %s) AS new_quest
                   ON DUPLICATE KEY UPDATE
                   description=new_quest.description,
                   progress_required=new_quest.progress_required,
                   diamond_reward=new_quest.diamond_reward""",
                (quest['type'], quest['desc'], quest['required'], quest['reward'],
                 quest['difficulty'], quest['energy'], quest['icon'])
            )

        logging.info(f"Initialized {len(QUEST_TEMPLATES)} daily quest templates")

    except Exception as e:
        logging.error(f"Error initializing quest templates: {e}", exc_info=True)


async def generate_daily_quests(player_id: int) -> List[Dict[str, Any]]:
    """
    Assign 3 random quests to player each day (1 easy, 1 medium, 1 hard).
    Called at midnight or on first login of the day.

    Args:
        player_id: The player's ID

    Returns:
        List of assigned quest dictionaries
    """
    from database_async import fetch_one, fetch_dict_all, get_connection
    today = date.today()

    try:
        # Check if already has quests for today
        result = await fetch_one(
            "SELECT COUNT(*) as count FROM player_daily_quests WHERE player_id = %s AND assigned_date = %s",
            (player_id, today)
        )

        if result and result[0] > 0:
            logging.info(f"Player {player_id} already has quests for today")
            return await get_active_quests(player_id)  # Return existing quests

        # Get all quest templates by difficulty
        all_quests = await fetch_dict_all("SELECT * FROM daily_quest_templates")

        if not all_quests:
            logging.warning("No quest templates found in database")
            return []

        # Organize by difficulty
        easy_quests = [q for q in all_quests if q['difficulty'] == 'easy']
        medium_quests = [q for q in all_quests if q['difficulty'] == 'medium']
        hard_quests = [q for q in all_quests if q['difficulty'] == 'hard']

        if not easy_quests or not medium_quests or not hard_quests:
            logging.error(f"Missing quest templates - easy: {len(easy_quests)}, medium: {len(medium_quests)}, hard: {len(hard_quests)}")
            return []

        # Randomly select: 1 easy, 1 medium, 1 hard
        selected = [
            random.choice(easy_quests),
            random.choice(medium_quests),
            random.choice(hard_quests)
        ]

        # Assign to player - need transaction for lastrowid
        assigned_quests = []
        async with get_connection() as conn:
            async with conn.cursor() as cursor:
                for quest in selected:
                    await cursor.execute(
                        """INSERT INTO player_daily_quests
                           (player_id, quest_template_id, assigned_date, progress, completed)
                           VALUES (%s, %s, %s, 0, FALSE)""",
                        (player_id, quest['id'], today)
                    )

                    assigned_quests.append({
                        'id': cursor.lastrowid,
                        'quest_type': quest['quest_type'],
                        'description': quest['description'],
                        'progress': 0,
                        'progress_required': quest['progress_required'],
                        'diamond_reward': quest['diamond_reward'],
                        'difficulty': quest['difficulty'],
                        'icon_name': quest['icon_name'],
                        'completed': False
                    })

        logging.info(f"Assigned {len(selected)} daily quests to player {player_id}")

        return assigned_quests

    except Exception as e:
        logging.error(f"Error generating daily quests for player {player_id}: {e}", exc_info=True)
        return []


async def update_quest_progress(player_id: int, quest_type: str, amount: int = 1) -> Optional[Dict[str, Any]]:
    """
    Update progress on quest of given type.
    Called after relevant player actions.

    Args:
        player_id: The player's ID
        quest_type: Type of quest to update (e.g., 'talk_to_characters')
        amount: Amount to increment progress (default 1)

    Returns:
        Updated quest dict if quest exists and was updated, None otherwise
    """
    from database_async import fetch_dict_one, execute_query
    today = date.today()

    try:
        # Find active quest of this type
        quest = await fetch_dict_one(
            """SELECT pq.*, qt.quest_type, qt.description, qt.progress_required, qt.diamond_reward, qt.difficulty, qt.icon_name
               FROM player_daily_quests pq
               JOIN daily_quest_templates qt ON pq.quest_template_id = qt.id
               WHERE pq.player_id = %s AND pq.assigned_date = %s
               AND pq.completed = FALSE AND qt.quest_type = %s""",
            (player_id, today, quest_type)
        )

        if not quest:
            # No active quest of this type today
            return None

        # Update progress (cap at required amount)
        new_progress = min(quest['progress'] + amount, quest['progress_required'])

        await execute_query(
            "UPDATE player_daily_quests SET progress = %s WHERE id = %s",
            (new_progress, quest['id'])
        )

        logging.info(f"Player {player_id} quest '{quest_type}' progress: {new_progress}/{quest['progress_required']}")

        # Check if completed
        if new_progress >= quest['progress_required']:
            await complete_quest(player_id, quest['id'], quest['diamond_reward'])
            return {
                'id': quest['id'],
                'quest_type': quest['quest_type'],
                'description': quest['description'],
                'progress': new_progress,
                'progress_required': quest['progress_required'],
                'diamond_reward': quest['diamond_reward'],
                'difficulty': quest['difficulty'],
                'icon_name': quest['icon_name'],
                'completed': True,
                'just_completed': True
            }
        else:
            return {
                'id': quest['id'],
                'quest_type': quest['quest_type'],
                'description': quest['description'],
                'progress': new_progress,
                'progress_required': quest['progress_required'],
                'diamond_reward': quest['diamond_reward'],
                'difficulty': quest['difficulty'],
                'icon_name': quest['icon_name'],
                'completed': False
            }

    except Exception as e:
        logging.error(f"Error updating quest progress for player {player_id}, quest '{quest_type}': {e}", exc_info=True)
        return None


async def complete_quest(player_id: int, quest_id: int, reward: int) -> bool:
    """
    Complete quest and award diamonds.

    Args:
        player_id: The player's ID
        quest_id: The quest record ID
        reward: Diamond reward amount

    Returns:
        True if successful, False otherwise
    """
    from database_async import execute_query

    try:
        # Mark quest as completed
        await execute_query(
            """UPDATE player_daily_quests
               SET completed = TRUE, completed_date = NOW()
               WHERE id = %s AND player_id = %s""",
            (quest_id, player_id)
        )

        # Award diamonds
        from monetization.diamond_economy import award_diamonds
        await award_diamonds(player_id, f"daily_quest_{quest_id}", reward)

        logging.info(f"Player {player_id} completed quest {quest_id}, awarded {reward} diamonds")

        return True

    except Exception as e:
        logging.error(f"Error completing quest {quest_id} for player {player_id}: {e}", exc_info=True)
        return False


async def get_active_quests(player_id: int) -> List[Dict[str, Any]]:
    """
    Get player's current daily quests.

    Args:
        player_id: The player's ID

    Returns:
        List of quest dictionaries
    """
    from database_async import fetch_dict_all
    today = date.today()

    try:
        quests = await fetch_dict_all(
            """SELECT pq.id, pq.progress, pq.completed, pq.completed_date,
                      qt.quest_type, qt.description, qt.progress_required,
                      qt.diamond_reward, qt.difficulty, qt.icon_name
               FROM player_daily_quests pq
               JOIN daily_quest_templates qt ON pq.quest_template_id = qt.id
               WHERE pq.player_id = %s AND pq.assigned_date = %s
               ORDER BY qt.difficulty DESC""",  # Hard, Medium, Easy
            (player_id, today)
        )

        # Format for client
        result = []
        for quest in quests:
            result.append({
                'id': quest['id'],
                'quest_type': quest['quest_type'],
                'description': quest['description'],
                'progress': quest['progress'],
                'progress_required': quest['progress_required'],
                'diamond_reward': quest['diamond_reward'],
                'difficulty': quest['difficulty'],
                'icon_name': quest['icon_name'],
                'completed': bool(quest['completed']),
                'completed_date': quest['completed_date'].isoformat() if quest['completed_date'] else None
            })

        return result

    except Exception as e:
        logging.error(f"Error getting active quests for player {player_id}: {e}", exc_info=True)
        return []


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

    Args:
        player_id: The player's ID

    Returns:
        Dict with quest statistics
    """
    conn = None
    cursor = None

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

        # Get total completed quests
        cursor.execute(
            """SELECT
                   COUNT(*) as total_completed,
                   SUM(qt.diamond_reward) as total_diamonds_earned,
                   COUNT(DISTINCT pq.assigned_date) as days_completed
               FROM player_daily_quests pq
               JOIN daily_quest_templates qt ON pq.quest_template_id = qt.id
               WHERE pq.player_id = %s AND pq.completed = TRUE""",
            (player_id,)
        )
        stats = cursor.fetchone()

        # Get completion rate by difficulty
        cursor.execute(
            """SELECT qt.difficulty,
                      COUNT(*) as completed_count
               FROM player_daily_quests pq
               JOIN daily_quest_templates qt ON pq.quest_template_id = qt.id
               WHERE pq.player_id = %s AND pq.completed = TRUE
               GROUP BY qt.difficulty""",
            (player_id,)
        )
        by_difficulty = cursor.fetchall()

        return {
            'total_completed': stats['total_completed'] if stats else 0,
            'total_diamonds_earned': int(stats['total_diamonds_earned']) if stats and stats['total_diamonds_earned'] else 0,
            'days_with_quests': stats['days_completed'] if stats else 0,
            'by_difficulty': {item['difficulty']: item['completed_count'] for item in by_difficulty}
        }

    except Exception as e:
        logging.error(f"Error getting quest statistics for player {player_id}: {e}", exc_info=True)
        return {
            'total_completed': 0,
            'total_diamonds_earned': 0,
            'days_with_quests': 0,
            'by_difficulty': {}
        }

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


def format_quest_for_client(quest: Dict[str, Any]) -> Dict[str, Any]:
    """
    Format quest data to match iOS DailyQuest model expectations.

    Args:
        quest: Raw quest dictionary from database

    Returns:
        Formatted quest dictionary matching iOS model
    """
    # Map database fields to iOS model fields
    from datetime import datetime

    # Determine category from difficulty
    category_map = {
        'easy': 'activities',
        'medium': 'career',
        'hard': 'wealth'
    }

    # Map quest_type to appropriate category
    type_to_category = {
        'talk_to_characters': 'social',
        'socialize': 'social',
        'go_on_date': 'social',
        'increase_affinity': 'social',
        'work_hours': 'career',
        'earn_money': 'wealth',
        'buy_item': 'wealth',
        'attend_class': 'education',
        'study': 'education',
        'complete_activities': 'activities',
        'spend_energy': 'activities'
    }

    quest_type = quest.get('quest_type', '')
    category = type_to_category.get(quest_type, category_map.get(quest.get('difficulty', 'easy'), 'activities'))

    # Check if quest is claimed (completed_date is set)
    claimed = quest.get('completed', False) and quest.get('completed_date') is not None

    return {
        'id': str(quest['id']),
        'name': quest['description'].split(' - ')[0] if ' - ' in quest['description'] else quest['description'][:30],
        'description': quest['description'],
        'category': category.capitalize(),
        'reward': {
            'diamonds': quest['diamond_reward'],
            'energy': None,
            'money': None
        },
        'progress': quest['progress'],
        'target': quest['progress_required'],
        'completed': quest['completed'],
        'claimed': claimed
    }


def claim_quest_reward(player_id: int, quest_id: str) -> Dict[str, Any]:
    """
    Claim reward for a completed quest.

    Args:
        player_id: The player's ID
        quest_id: The quest ID to claim

    Returns:
        Dictionary with success status and reward info
    """
    conn = None
    cursor = None

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

        # Get quest details
        cursor.execute(
            """SELECT pq.*, qt.diamond_reward
               FROM player_daily_quests pq
               JOIN daily_quest_templates qt ON pq.quest_template_id = qt.id
               WHERE pq.id = %s AND pq.player_id = %s""",
            (int(quest_id), player_id)
        )
        quest = cursor.fetchone()

        if not quest:
            return {
                'success': False,
                'message': 'Quest not found',
                'reward': None
            }

        if not quest['completed']:
            return {
                'success': False,
                'message': 'Quest not completed yet',
                'reward': None
            }

        if quest['completed_date']:
            # Already claimed (completed_date is set when reward is claimed)
            return {
                'success': False,
                'message': 'Reward already claimed',
                'reward': None
            }

        # Mark as claimed by setting completed_date
        cursor.execute(
            """UPDATE player_daily_quests
               SET completed_date = NOW()
               WHERE id = %s AND player_id = %s""",
            (int(quest_id), player_id)
        )

        # Award diamonds
        from monetization.diamond_economy import award_diamonds
        award_diamonds(player_id, quest['diamond_reward'], "Daily Quest Reward")

        conn.commit()

        logging.info(f"Player {player_id} claimed quest {quest_id} reward: {quest['diamond_reward']} diamonds")

        return {
            'success': True,
            'message': f"Claimed {quest['diamond_reward']} diamonds!",
            'reward': {
                'diamonds': quest['diamond_reward'],
                'energy': None,
                'money': None
            }
        }

    except Exception as e:
        if conn:
            conn.rollback()
        logging.error(f"Error claiming quest reward for player {player_id}, quest {quest_id}: {e}", exc_info=True)
        return {
            'success': False,
            'message': 'Failed to claim reward',
            'reward': None
        }

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


def handle_daily_quest_check(player_id: int, send_to_client):
    """
    WebSocket handler for daily quest check.
    Called when player connects to server or at midnight.

    Args:
        player_id: The player's ID
        send_to_client: Function to send messages to client (player_id, message_dict)
    """
    try:
        from datetime import date, datetime, timedelta

        # Check if player has quests for today
        active_quests = get_active_quests(player_id)

        if not active_quests:
            # Generate new quests
            active_quests = generate_daily_quests(player_id)

        # Format quests for client
        formatted_quests = [format_quest_for_client(q) for q in active_quests] if active_quests else []

        # Calculate reset times
        today = date.today()
        tomorrow = today + timedelta(days=1)
        last_reset = datetime.combine(today, datetime.min.time()).isoformat()
        next_reset = datetime.combine(tomorrow, datetime.min.time()).isoformat()

        # Send complete state matching iOS DailyQuestsState model
        send_to_client(player_id, {
            'type': 'dailyQuestsStatus',
            'quests': formatted_quests,
            'lastResetDate': last_reset,
            'nextResetDate': next_reset
        })

        logging.info(f"Sent daily quests state to player {player_id}: {len(formatted_quests)} quests")

    except Exception as e:
        logging.error(f"Error handling daily quest check for player {player_id}: {e}", exc_info=True)


# ============================================
# INTEGRATION HOOKS
# ============================================
# These examples show how to integrate quest progress tracking
# into existing game systems.
# ============================================

def example_integration_conversation():
    """
    Example: Update quest when player talks to a character

    Integration point: ws/conversationEvents.py or wherever conversations are handled

    Add this after a successful conversation:
        from retention.daily_quests import update_quest_progress
        update_quest_progress(player_id, 'talk_to_characters', 1)
    """
    pass


def example_integration_work():
    """
    Example: Update quest when player works

    Integration point: ws/intradayActivity.py or work event handlers

    Add this after work hours are completed:
        from retention.daily_quests import update_quest_progress

        # Track work hours
        update_quest_progress(player_id, 'work_hours', hours_worked)

        # Track activity completion
        update_quest_progress(player_id, 'complete_activities', 1)
    """
    pass


def example_integration_study():
    """
    Example: Update quest when player studies or attends class

    Integration point: Education/school event handlers

    Add this after attending class:
        from retention.daily_quests import update_quest_progress
        update_quest_progress(player_id, 'attend_class', 1)
        update_quest_progress(player_id, 'study', hours_studied)
    """
    pass


def example_integration_shopping():
    """
    Example: Update quest when player makes a purchase

    Integration point: Store/purchase handlers

    Add this after successful purchase:
        from retention.daily_quests import update_quest_progress
        update_quest_progress(player_id, 'buy_item', 1)
    """
    pass


def example_integration_dating():
    """
    Example: Update quest when player goes on a date

    Integration point: Dating/relationship event handlers

    Add this when date activity starts:
        from retention.daily_quests import update_quest_progress
        update_quest_progress(player_id, 'go_on_date', 1)
    """
    pass


def example_integration_energy():
    """
    Example: Update quest when player spends energy

    Integration point: Activity/energy deduction handlers

    Add this when energy is spent:
        from retention.daily_quests import update_quest_progress
        update_quest_progress(player_id, 'spend_energy', energy_amount)
    """
    pass


def example_integration_money():
    """
    Example: Update quest when player earns money

    Integration point: Work/income event handlers

    Add this when money is earned:
        from retention.daily_quests import update_quest_progress
        update_quest_progress(player_id, 'earn_money', amount_earned)
    """
    pass


def example_integration_affinity():
    """
    Example: Update quest when affinity increases

    Integration point: Relationship/affinity update handlers

    Add this when affinity increases:
        from retention.daily_quests import update_quest_progress
        update_quest_progress(player_id, 'increase_affinity', affinity_increase)
    """
    pass


def example_integration_socialize():
    """
    Example: Update quest when player socializes

    Integration point: Social activity handlers

    Add this when social activity completes:
        from retention.daily_quests import update_quest_progress
        update_quest_progress(player_id, 'socialize', 1)
    """
    pass
