"""
Integration tests for full game loop scenarios.

Tests complete game simulation workflows from character creation through
various life stages and milestones. Uses real game engine with mock storage/output.

Run with: pytest tests/integration/test_full_game_loop.py -v
"""

import pytest
import asyncio
from ws.game_engine import GameEngine
from ws.core.models import playerClass
from ws.config import config


class TestFullGameLoop:
    """
    Test complete game loop scenarios from birth to various life stages.

    These integration tests verify that the game engine correctly processes
    time progression, events, and state transitions across extended simulations.
    """

    @pytest.mark.asyncio
    async def test_new_game_creation_to_first_birthday(
        self, newborn_player, game_engine, mock_output
    ):
        """
        Test creating a new character and simulating to first birthday.

        Arrange: New player with newborn character
        Act: Simulate 365 days
        Assert: Character turns 1 year old, birthday event triggered
        """
        player = newborn_player
        assert player.c.ageYears == 0
        assert player.c.ageDays == 0

        # Simulate 365 days (1 year) at fast speed
        player.gameSpeed = 1  # Fast simulation
        days_simulated = 0

        while days_simulated < 365:
            # Run 24 hours worth of ticks
            for _ in range(24):
                player = await game_engine.run_game_tick(player, force_update=True)

            days_simulated += 1

        # Verify age incremented
        assert player.c.ageYears == 1
        assert player.c.ageDays >= 365

        # Verify birthday message was sent
        events = mock_output.get_events()
        assert len(events) > 0

    @pytest.mark.asyncio
    async def test_childhood_to_elementary_school(
        self, newborn_player, game_engine, mock_output
    ):
        """
        Test progression from birth to elementary school enrollment.

        Arrange: Newborn character
        Act: Simulate to age 5
        Assert: Character enrolled in elementary school
        """
        player = newborn_player
        player.gameSpeed = 1

        # Fast forward to age 5 (5 years * 365 days)
        target_age_days = 5 * 365
        player.c.ageDays = target_age_days
        player.c.ageYears = 5
        player.c.ageHours = target_age_days * 24

        # Run a day to trigger school-related events
        for _ in range(24):
            player = await game_engine.run_game_tick(player, force_update=True)

        # Verify character is school-age
        assert player.c.ageYears >= 5

        # School enrollment happens around age 5-6
        # Verify occupation updated or school-related events occurred
        assert player.c.ageYears == 5

    @pytest.mark.asyncio
    async def test_elementary_to_high_school_transition(
        self, child_player, game_engine, mock_output
    ):
        """
        Test transition from elementary to high school.

        Arrange: Child at age 8
        Act: Simulate to age 14
        Assert: Character transitions to high school
        """
        player = child_player
        assert player.c.ageYears == 8

        # Fast forward to age 14
        years_to_add = 14 - 8
        player.c.ageDays += years_to_add * 365
        player.c.ageYears = 14
        player.c.ageHours = player.c.ageDays * 24
        player.gameSpeed = 1

        # Run a few days to trigger transition events
        for _ in range(3 * 24):  # 3 days
            player = await game_engine.run_game_tick(player, force_update=True)

        # Verify age progression
        assert player.c.ageYears == 14

    @pytest.mark.asyncio
    async def test_high_school_graduation(
        self, teen_player, game_engine, mock_output
    ):
        """
        Test high school graduation process.

        Arrange: Teen at age 16
        Act: Simulate to age 18
        Assert: Graduation event triggered
        """
        player = teen_player
        assert player.c.ageYears == 16

        # Fast forward to age 18
        years_to_add = 18 - 16
        player.c.ageDays += years_to_add * 365
        player.c.ageYears = 18
        player.c.ageHours = player.c.ageDays * 24
        player.c.occupation = "high_school"
        player.gameSpeed = 1

        # Run simulation for graduation period
        for _ in range(7 * 24):  # 1 week
            player = await game_engine.run_game_tick(player, force_update=True)

        # Verify age reached 18
        assert player.c.ageYears == 18

    @pytest.mark.asyncio
    async def test_college_enrollment_and_graduation(
        self, teen_player, game_engine, mock_output
    ):
        """
        Test enrolling in college and graduating.

        Arrange: Teen at age 18
        Act: Simulate college years (4 years)
        Assert: College graduation occurs
        """
        player = teen_player
        player.c.ageYears = 18
        player.c.ageDays = 18 * 365
        player.c.ageHours = 18 * 365 * 24
        player.c.occupation = "college"
        player.c.education = "College"
        player.gameSpeed = 1

        # Simulate 4 years of college
        initial_age = player.c.ageYears

        # Fast forward 4 years
        player.c.ageDays += 4 * 365
        player.c.ageYears = 22
        player.c.ageHours = player.c.ageDays * 24

        # Run a week to trigger graduation events
        for _ in range(7 * 24):
            player = await game_engine.run_game_tick(player, force_update=True)

        # Verify graduated from college
        assert player.c.ageYears == 22
        assert initial_age < player.c.ageYears

    @pytest.mark.asyncio
    async def test_first_job_application_and_employment(
        self, adult_player, game_engine, mock_output
    ):
        """
        Test job application, hiring, and salary receipt.

        Arrange: Adult character without job
        Act: Simulate job start and first payday
        Assert: Salary added to money
        """
        player = adult_player
        player.c.occupation = "unemployed"
        initial_money = player.c.money
        player.gameSpeed = 1

        # Set a job
        from ws.jobs.job_manager import setJob
        setJob(player.c, "Professional")

        # Simulate to next Monday (weekly payday)
        # Move to Monday hour 0
        while player.dayOfWeek != 1 or player.hourOfDay != 0:
            player = await game_engine.run_game_tick(player, force_update=True)

        # Salary should be added (handleFinances runs on Monday hour 0)
        # Note: Money may increase due to salary
        assert player.c.occupation != "unemployed"

    @pytest.mark.asyncio
    async def test_romantic_relationship_lifecycle(
        self, adult_player, game_engine, mock_output
    ):
        """
        Test romantic relationship from meeting to breakup.

        Arrange: Adult with potential partner
        Act: Start romance, simulate relationship, breakup
        Assert: Relationship states transition correctly
        """
        from ws.character.character_manager import create_character
        from ws.relationships.relationship_manager import romance, breakUp, updateAffinity

        player = adult_player

        # Create a potential partner
        partner = create_character(player, "Partner", 30, "Male")
        player.r.append(partner)

        # Increase affinity to high level
        updateAffinity(player, player.c, partner, 80)

        # Start romance
        romance(player, player.c, partner)

        # Verify romance started
        from ws.relationships.relationship_manager import getActiveRelationship
        active_rel = getActiveRelationship(player, player.c)
        assert active_rel is not None

        # Simulate some time together
        player.gameSpeed = 1
        for _ in range(24 * 7):  # 1 week
            player = await game_engine.run_game_tick(player, force_update=True)

        # Break up
        breakUp(player, player.c, partner)

        # Verify relationship ended
        active_rel = getActiveRelationship(player, player.c)
        # After breakup, there should be no active relationship

    @pytest.mark.asyncio
    async def test_full_life_simulation_birth_to_death(
        self, newborn_player, game_engine, mock_output
    ):
        """
        Test complete life simulation from birth to death.

        Arrange: Newborn character
        Act: Simulate until death (forced at age 121)
        Assert: Death event triggered, game ends properly
        """
        player = newborn_player
        player.gameSpeed = 1

        # Fast forward to very old age (121 years - guaranteed death)
        player.c.ageDays = 121 * 365
        player.c.ageYears = 121
        player.c.ageHours = 121 * 365 * 24

        # Run a day to trigger death
        for _ in range(24):
            player = await game_engine.run_game_tick(player, force_update=True)

        # Death should occur at age > 120
        assert player.c.status == "dead"

    @pytest.mark.asyncio
    async def test_daily_activity_cycle(
        self, adult_player, game_engine, mock_output
    ):
        """
        Test full 24-hour daily activity cycle.

        Arrange: Adult player at start of day
        Act: Simulate 24 hours
        Assert: All daily activities execute, stats update correctly
        """
        player = adult_player
        player.hourOfDay = 0
        player.minuteOfHour = 0
        player.gameSpeed = 1

        initial_energy = player.c.energy
        initial_hour = player.hourOfDay

        # Simulate 24 hours
        for _ in range(24):
            player = await game_engine.run_game_tick(player, force_update=True)

        # Verify day completed
        assert player.hourOfDay == 0  # Should wrap back to 0
        assert player.dayOfYear > 0

        # Energy should have changed during the day
        # (either increased from sleep or decreased from activity)

    @pytest.mark.asyncio
    async def test_weekly_routine(
        self, adult_player, game_engine, mock_output
    ):
        """
        Test week-long simulation with weekend events.

        Arrange: Adult player at start of week
        Act: Simulate 7 days
        Assert: Weekend detection works, weekly events trigger
        """
        player = adult_player
        player.dayOfWeek = 1  # Monday
        player.hourOfDay = 0
        player.gameSpeed = 1

        # Track weekend detection
        weekend_detected = False

        # Simulate 7 days
        for day in range(7):
            for hour in range(24):
                player = await game_engine.run_game_tick(player, force_update=True)

                # Check if weekend detected (Saturday=6, Sunday=7)
                if player.dayOfWeek in [6, 7]:
                    weekend_detected = True

        # Verify week completed
        assert weekend_detected

    @pytest.mark.asyncio
    async def test_monthly_progression(
        self, adult_player, game_engine, mock_output
    ):
        """
        Test month-long simulation with salary and bills.

        Arrange: Adult with job
        Act: Simulate 30 days
        Assert: Monthly financial events occur
        """
        player = adult_player
        player.c.occupation = "professional"
        initial_money = player.c.money
        player.gameSpeed = 1

        # Simulate 30 days
        days_simulated = 0
        monday_count = 0

        while days_simulated < 30:
            # Run 24 hours
            for _ in range(24):
                player = await game_engine.run_game_tick(player, force_update=True)

                # Count Mondays (payday)
                if player.dayOfWeek == 1 and player.hourOfDay == 0:
                    monday_count += 1

            days_simulated += 1

        # Should have encountered multiple Mondays (paydays)
        assert monday_count >= 4  # At least 4 weeks in a month

    @pytest.mark.asyncio
    async def test_yearly_events(
        self, child_player, game_engine, mock_output
    ):
        """
        Test year-long simulation with birthday and holidays.

        Arrange: Child player
        Act: Simulate 365 days
        Assert: Birthday occurs, holiday events trigger
        """
        player = child_player
        initial_age = player.c.ageYears
        player.gameSpeed = 1

        # Simulate 1 year (365 days)
        days_simulated = 0

        while days_simulated < 365:
            # Run 24 hours
            for _ in range(24):
                player = await game_engine.run_game_tick(player, force_update=True)

            days_simulated += 1

        # Verify age incremented by 1
        assert player.c.ageYears == initial_age + 1

        # Verify events were triggered
        events = mock_output.get_events()
        assert len(events) > 0
