"""
Unit Tests for Holiday Events

Tests holiday and date-specific events from ws/events/holidays/annual.py including:
- Major Holidays: christmas, newYear, thanksgiving, halloween, valentinesDay
- Independence Days: independenceday
- Seasonal Events: easterEggHunt, summerFirstDayPool, snowDay
- School Events: backToSchoolShopping
- Life Events: birthday
- Household Events: springCleaning
- Shopping Events: blackFridayChaos

Test Pattern (per TESTING_PLAN.md Section 4.4):
- test_event_triggers_on_correct_date - Day-of-year triggering
- test_event_triggers_at_correct_age - Event only triggers in age range
- test_event_not_duplicate - Event fname not in player.events
- test_event_applies_costs_correctly - Money/energy/stats updated
- test_event_choice_consequences - Different answers -> different outcomes
"""

import pytest
import random
from unittest.mock import patch

from ws.core.models import playerClass, personClass
from ws.events.base import messageEvent, questionEvent, answerOption

# Import holiday event functions
from ws.events.holidays import annual as holiday_events

christmas = holiday_events.christmas
newYear = holiday_events.newYear
thanksgiving = holiday_events.thanksgiving
blackfriday = holiday_events.blackfriday
independenceday = holiday_events.independenceday
birthday = holiday_events.birthday
halloween = holiday_events.halloween
valentinesDay = holiday_events.valentinesDay
easterEggHunt = holiday_events.easterEggHunt
backToSchoolShopping = holiday_events.backToSchoolShopping
springCleaning = holiday_events.springCleaning
summerFirstDayPool = holiday_events.summerFirstDayPool
snowDay = holiday_events.snowDay
newYearsResolution = holiday_events.newYearsResolution
blackFridayChaos = holiday_events.blackFridayChaos


# ============================================================================
# FIXTURES
# ============================================================================

@pytest.fixture
def child_player():
    """Create a player with child character (age 8)"""
    player = playerClass()
    player.c = personClass()
    player.c.firstname = "Child"
    player.c.lastname = "TestKid"
    player.c.ageYears = 8
    player.c.ageDays = 365 * 8
    player.c.energy = 100
    player.c.happiness = 75
    player.c.social = 50
    player.c.money = 100
    player.c.location = "home123"
    player.c.occupation = "student"
    player.events = set()
    player.askedQuestions = set()
    player.messageQueue = []
    player.date = "12-25"
    player.hourOfDay = 12
    player.dayEvent = None
    player.gameSpeed = 1000
    player.previousGameSpeed = 1000
    player.r = []
    return player


@pytest.fixture
def teen_player():
    """Create a player with teenager character (age 16)"""
    player = playerClass()
    player.c = personClass()
    player.c.firstname = "Teen"
    player.c.lastname = "TestKid"
    player.c.ageYears = 16
    player.c.ageDays = 365 * 16
    player.c.energy = 100
    player.c.happiness = 75
    player.c.social = 50
    player.c.money = 500
    player.c.location = "home123"
    player.c.occupation = "student"
    player.events = set()
    player.askedQuestions = set()
    player.messageQueue = []
    player.date = "12-25"
    player.hourOfDay = 12
    player.dayEvent = None
    player.gameSpeed = 1000
    player.previousGameSpeed = 1000
    player.r = []
    return player


@pytest.fixture
def adult_player():
    """Create a player with adult character (age 30)"""
    player = playerClass()
    player.c = personClass()
    player.c.firstname = "Adult"
    player.c.lastname = "TestPerson"
    player.c.ageYears = 30
    player.c.ageDays = 365 * 30
    player.c.energy = 80
    player.c.happiness = 70
    player.c.social = 60
    player.c.money = 2000
    player.c.stress = 30
    player.c.location = "home123"
    player.c.occupation = "work"
    player.events = set()
    player.askedQuestions = set()
    player.messageQueue = []
    player.date = "12-25"
    player.hourOfDay = 12
    player.dayEvent = None
    player.gameSpeed = 1000
    player.previousGameSpeed = 1000
    player.r = []
    return player


# ============================================================================
# MAJOR HOLIDAY EVENTS TESTS
# ============================================================================

class TestChristmas:
    """Test christmas event (12-25)"""

    def test_christmas_triggers_on_correct_date(self, child_player):
        """Event should trigger on December 25"""
        child_player.date = "12-25"
        result = christmas(child_player)
        assert result is not None
        assert isinstance(result, messageEvent)
        assert "christmas" in result.message.lower()

    def test_christmas_not_on_wrong_date(self, child_player):
        """Event should not trigger on other dates"""
        child_player.date = "12-24"
        result = christmas(child_player)
        assert result is None

    def test_christmas_year_specific_fname(self, child_player):
        """Event should use year-specific fname to allow multiple celebrations"""
        child_player.date = "12-25"
        child_player.c.ageYears = 8
        result = christmas(child_player)
        expected_fname = f'christmas_{child_player.c.ageYears}'
        assert expected_fname in child_player.events

    def test_christmas_sets_day_event(self, child_player):
        """Event should set player.dayEvent"""
        child_player.date = "12-25"
        result = christmas(child_player)
        assert player.dayEvent == 'christmas'


class TestNewYear:
    """Test newYear event (01-01)"""

    def test_new_year_triggers_on_correct_date(self, child_player):
        """Event should trigger on January 1"""
        child_player.date = "01-01"
        result = newYear(child_player)
        assert result is not None
        assert isinstance(result, messageEvent)

    def test_new_year_not_duplicate_same_year(self, child_player):
        """Event should not trigger if already occurred this year"""
        child_player.date = "01-01"
        child_player.c.ageYears = 10
        child_player.events.add('newyear_10')
        result = newYear(child_player)
        assert result is None


class TestHalloween:
    """Test halloween questionEvent (10-31, ages 3-16)"""

    def test_halloween_triggers_on_correct_date(self, child_player):
        """Event should trigger on October 31"""
        child_player.date = "10-31"
        child_player.c.ageYears = 8
        result = halloween(child_player)
        assert result is not None
        assert isinstance(result, questionEvent)

    def test_halloween_not_before_age(self, child_player):
        """Event should not trigger before age 3"""
        child_player.date = "10-31"
        child_player.c.ageYears = 2
        result = halloween(child_player)
        assert result is None

    def test_halloween_not_after_age(self, teen_player):
        """Event should not trigger after age 16"""
        teen_player.date = "10-31"
        teen_player.c.ageYears = 17
        result = halloween(teen_player)
        assert result is None

    def test_halloween_age_dependent_costumes(self, child_player):
        """Event should have different costume options for different ages"""
        child_player.date = "10-31"
        child_player.c.ageYears = 8
        result = halloween(child_player)
        # Young children get princess/superhero/monster/funny options
        assert any("Princess" in opt or "Superhero" in opt for opt in result.answerOptions)

    def test_halloween_answer_group_costume(self, teen_player):
        """Group costume should increase happiness and social"""
        teen_player.date = "10-31"
        teen_player.c.ageYears = 14
        teen_player.c.happiness = 60
        teen_player.c.social = 50
        response = {'option': 'Group costume with friends'}
        halloween(teen_player, type='answer', response=response)
        assert teen_player.c.happiness == 80
        assert teen_player.c.social == 60


class TestValentinesDay:
    """Test valentinesDay questionEvent (02-14, ages 10+)"""

    def test_valentines_day_triggers_on_correct_date(self, teen_player):
        """Event should trigger on February 14"""
        teen_player.date = "02-14"
        teen_player.c.ageYears = 16
        result = valentinesDay(teen_player)
        assert result is not None
        assert isinstance(result, questionEvent)

    def test_valentines_day_not_before_age(self, child_player):
        """Event should not trigger before age 10"""
        child_player.date = "02-14"
        child_player.c.ageYears = 9
        result = valentinesDay(child_player)
        assert result is None

    @patch('character.character_manager.get_partner', return_value=None)
    def test_valentines_day_single_options(self, mock_partner, teen_player):
        """Event should have different options if single"""
        teen_player.date = "02-14"
        result = valentinesDay(teen_player)
        # Single people get friend/self-care options
        assert any("Galentine" in str(opt) or "Treat yourself" in str(opt)
                   for opt in result.answerOptions)


# ============================================================================
# SEASONAL EVENTS TESTS
# ============================================================================

class TestEasterEggHunt:
    """Test easterEggHunt messageEvent (04-10, ages 2-10)"""

    def test_easter_triggers_on_correct_date(self, child_player):
        """Event should trigger on April 10"""
        child_player.date = "04-10"
        child_player.c.ageYears = 6
        result = easterEggHunt(child_player)
        assert result is not None
        assert isinstance(result, messageEvent)

    def test_easter_not_after_age(self, teen_player):
        """Event should not trigger after age 10"""
        teen_player.date = "04-10"
        teen_player.c.ageYears = 11
        result = easterEggHunt(teen_player)
        assert result is None

    def test_easter_increases_happiness(self, child_player):
        """Event should increase happiness"""
        child_player.date = "04-10"
        child_player.c.ageYears = 6
        child_player.c.happiness = 60
        result = easterEggHunt(child_player)
        assert child_player.c.happiness == 75


class TestSummerFirstDayPool:
    """Test summerFirstDayPool messageEvent (06-01 to 06-07, ages 5-30)"""

    def test_summer_pool_triggers_in_date_range(self, teen_player):
        """Event should trigger in early June"""
        teen_player.date = "06-01"
        teen_player.c.ageYears = 16
        result = summerFirstDayPool(teen_player)
        assert result is not None
        assert isinstance(result, messageEvent)

    def test_summer_pool_not_outside_range(self, teen_player):
        """Event should not trigger outside date range"""
        teen_player.date = "06-15"
        teen_player.c.ageYears = 16
        result = summerFirstDayPool(teen_player)
        assert result is None

    def test_summer_pool_increases_happiness_decreases_energy(self, teen_player):
        """Event should increase happiness but decrease energy (sunburn)"""
        teen_player.date = "06-01"
        teen_player.c.ageYears = 16
        teen_player.c.happiness = 60
        teen_player.c.energy = 100
        result = summerFirstDayPool(teen_player)
        assert teen_player.c.happiness == 75
        assert teen_player.c.energy == 90


class TestSnowDay:
    """Test snowDay messageEvent (winter months, ages 5-65, student/work)"""

    @patch('random.random', return_value=0.0)
    def test_snow_day_triggers_in_winter(self, mock_random, child_player):
        """Event should trigger in winter months (Dec, Jan, Feb)"""
        child_player.date = "01-15"
        child_player.c.ageYears = 10
        child_player.c.occupation = "student"
        result = snowDay(child_player)
        assert result is not None
        assert isinstance(result, messageEvent)

    @patch('random.random', return_value=0.0)
    def test_snow_day_not_in_summer(self, mock_random, child_player):
        """Event should not trigger outside winter"""
        child_player.date = "07-15"
        child_player.c.ageYears = 10
        child_player.c.occupation = "student"
        result = snowDay(child_player)
        assert result is None

    @patch('random.random', return_value=0.0)
    def test_snow_day_requires_student_or_work(self, mock_random, adult_player):
        """Event should require student or work occupation"""
        adult_player.date = "01-15"
        adult_player.c.ageYears = 30
        adult_player.c.occupation = "work"
        result = snowDay(adult_player)
        assert result is not None


# ============================================================================
# SCHOOL & SHOPPING EVENTS TESTS
# ============================================================================

class TestBackToSchoolShopping:
    """Test backToSchoolShopping questionEvent (08-20 to 08-31, ages 5-18)"""

    def test_back_to_school_triggers_in_date_range(self, child_player):
        """Event should trigger in late August"""
        child_player.date = "08-25"
        child_player.c.ageYears = 10
        result = backToSchoolShopping(child_player)
        assert result is not None
        assert isinstance(result, questionEvent)

    def test_back_to_school_not_before_date(self, child_player):
        """Event should not trigger before August 20"""
        child_player.date = "08-19"
        child_player.c.ageYears = 10
        result = backToSchoolShopping(child_player)
        assert result is None

    def test_back_to_school_answer_cool_backpack(self, child_player):
        """Cool backpack should cost money but increase happiness and social"""
        child_player.c.money = 200
        child_player.c.happiness = 60
        child_player.c.social = 50
        response = {'option': 'Cool new backpack'}
        backToSchoolShopping(child_player, type='answer', response=response)
        assert child_player.c.money == 150
        assert child_player.c.happiness == 70
        assert child_player.c.social == 55


class TestBlackFridayChaos:
    """Test blackFridayChaos questionEvent (11-24, ages 16+)"""

    def test_black_friday_chaos_triggers_on_date(self, teen_player):
        """Event should trigger on November 24"""
        teen_player.date = "11-24"
        teen_player.c.ageYears = 18
        result = blackFridayChaos(teen_player)
        assert result is not None
        assert isinstance(result, questionEvent)

    def test_black_friday_chaos_not_before_age(self, child_player):
        """Event should not trigger before age 16"""
        child_player.date = "11-24"
        child_player.c.ageYears = 15
        result = blackFridayChaos(child_player)
        assert result is None

    def test_black_friday_answer_wake_up_early(self, adult_player):
        """Waking up early should cost energy and money but increase happiness"""
        adult_player.c.energy = 100
        adult_player.c.money = 500
        adult_player.c.happiness = 60
        response = {'option': 'Yes, wake up early!'}
        blackFridayChaos(adult_player, type='answer', response=response)
        assert adult_player.c.energy == 80
        assert adult_player.c.money == 300
        assert adult_player.c.happiness == 75


# ============================================================================
# HOUSEHOLD & PERSONAL EVENTS TESTS
# ============================================================================

class TestSpringCleaning:
    """Test springCleaning questionEvent (03-15 to 04-15, ages 18+)"""

    def test_spring_cleaning_triggers_in_date_range(self, adult_player):
        """Event should trigger between March 15 and April 15"""
        adult_player.date = "03-20"
        adult_player.c.ageYears = 25
        result = springCleaning(adult_player)
        assert result is not None
        assert isinstance(result, questionEvent)

    def test_spring_cleaning_not_before_age(self, teen_player):
        """Event should not trigger before age 18"""
        teen_player.date = "03-20"
        teen_player.c.ageYears = 17
        result = springCleaning(teen_player)
        assert result is None

    def test_spring_cleaning_answer_deep_clean(self, adult_player):
        """Deep cleaning should cost energy but increase happiness"""
        adult_player.c.energy = 100
        adult_player.c.happiness = 60
        response = {'option': 'Deep clean everything'}
        springCleaning(adult_player, type='answer', response=response)
        assert adult_player.c.energy == 70
        assert adult_player.c.happiness == 75


class TestNewYearsResolution:
    """Test newYearsResolution questionEvent (01-01, ages 10+)"""

    def test_new_years_resolution_triggers_on_date(self, teen_player):
        """Event should trigger on January 1"""
        teen_player.date = "01-01"
        teen_player.c.ageYears = 16
        result = newYearsResolution(teen_player)
        assert result is not None
        assert isinstance(result, questionEvent)

    def test_new_years_resolution_answer_exercise(self, adult_player):
        """Exercise resolution should increase happiness"""
        adult_player.c.happiness = 60
        response = {'option': 'Exercise more'}
        newYearsResolution(adult_player, type='answer', response=response)
        assert adult_player.c.happiness == 70
        assert len(adult_player.messageQueue) > 0


# ============================================================================
# BIRTHDAY EVENT TESTS
# ============================================================================

class TestBirthday:
    """Test birthday questionEvent (every 365 days)"""

    @patch('functions.get_random_family', return_value=True)
    def test_birthday_triggers_every_365_days(self, mock_family, child_player):
        """Event should trigger when ageDays is multiple of 365"""
        child_player.c.ageDays = 365 * 9  # Exactly 9 years
        result = birthday(child_player)
        assert result is not None
        assert isinstance(result, questionEvent)

    def test_birthday_not_on_non_birthday(self, child_player):
        """Event should not trigger on non-birthday days"""
        child_player.c.ageDays = 365 * 8 + 100  # Not a birthday
        result = birthday(child_player)
        assert result is None

    @patch('functions.get_random_family')
    @patch('functions.get_random_friend')
    def test_birthday_answer_with_family(self, mock_friend, mock_family, child_player):
        """Spending birthday with family should increase happiness and family affinity"""
        # Create mock family member
        parent = personClass()
        parent.firstname = "Parent"
        parent.lastname = "Test"
        parent.familyLevel = 1
        parent.affinity = 70
        child_player.r.append(parent)

        mock_family.return_value = parent
        child_player.c.happiness = 60
        response = {'option': 'Spend it with family.'}
        birthday(child_player, type='answer', response=response)

        assert child_player.c.happiness == 75
        assert parent.affinity == 85


# ============================================================================
# SUMMARY
# ============================================================================
"""
Test Summary:
=============

Major Holidays (4 events):
- christmas (4 tests)
- newYear (2 tests)
- halloween (5 tests)
- valentinesDay (3 tests)

Seasonal Events (3 events):
- easterEggHunt (3 tests)
- summerFirstDayPool (3 tests)
- snowDay (3 tests)

School & Shopping (2 events):
- backToSchoolShopping (3 tests)
- blackFridayChaos (3 tests)

Household & Personal (2 events):
- springCleaning (3 tests)
- newYearsResolution (2 tests)

Life Events (1 event):
- birthday (4 tests)

Total: 12 holiday/date-specific events tested with 41 test cases

Test Coverage:
- Date-specific triggering (day of year)
- Age range validation
- Duplicate prevention (year-specific fnames)
- Cost/stat application
- Choice consequences
- Seasonal conditions
- Occupation requirements
"""
