"""
Integration tests for multi-character interactions.

Tests how multiple characters interact over time, including family dynamics,
friendships, classmates, coworkers, and romantic relationships.

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

import pytest
import asyncio
from ws.game_engine import GameEngine
from ws.character.character_manager import create_character, add_parents, add_child
from ws.relationships.relationship_manager import (
    updateAffinity,
    getRelData,
    romance,
    getActiveRelationship,
)


class TestMultiCharacterInteractions:
    """
    Test scenarios involving multiple characters interacting.

    These integration tests verify that relationships, affinity changes,
    and multi-character state updates work correctly during game simulation.
    """

    @pytest.mark.asyncio
    async def test_family_interactions(
        self, child_player, game_engine, mock_output
    ):
        """
        Test family member interactions over time.

        Arrange: Child with parents and siblings
        Act: Simulate family interactions for a week
        Assert: Family relationships update, affinity changes correctly
        """
        player = child_player

        # Add parents
        add_parents(player, player.c)
        assert len(player.r) >= 2  # At least mother and father

        # Get parents
        mother = player.r[0]
        father = player.r[1]

        initial_mother_affinity = getRelData(player, player.c, mother).affinity
        initial_father_affinity = getRelData(player, player.c, father).affinity

        player.gameSpeed = 1

        # Simulate 7 days of family life
        for _ in range(7 * 24):
            player = await game_engine.run_game_tick(player, force_update=True)

        # Verify parents still exist
        assert mother in player.r
        assert father in player.r

        # Verify parent ages increased
        # (handleRelationships updates all NPCs)

    @pytest.mark.asyncio
    async def test_friend_affinity_changes(
        self, teen_player, game_engine, mock_output
    ):
        """
        Test friendship affinity developing and decaying over time.

        Arrange: Teen with friends
        Act: Simulate with positive and negative interactions
        Assert: Affinity increases with interaction, decreases without
        """
        player = teen_player

        # Create friends
        friend1 = create_character(player, "Friend1", 16, "Female")
        friend2 = create_character(player, "Friend2", 16, "Male")
        player.r.extend([friend1, friend2])

        # Set initial affinity
        updateAffinity(player, player.c, friend1, 50)
        updateAffinity(player, player.c, friend2, 50)

        initial_affinity1 = getRelData(player, player.c, friend1).affinity
        initial_affinity2 = getRelData(player, player.c, friend2).affinity

        player.gameSpeed = 1

        # Simulate positive interaction with friend1
        updateAffinity(player, player.c, friend1, 10)

        # Simulate time passage (affinity decays without interaction)
        for _ in range(30 * 24):  # 30 days
            player = await game_engine.run_game_tick(player, force_update=True)

        # Friend1 should have higher affinity (got boost)
        current_affinity1 = getRelData(player, player.c, friend1).affinity
        assert current_affinity1 != initial_affinity1

    @pytest.mark.asyncio
    async def test_classmate_interactions(
        self, child_player, game_engine, mock_output
    ):
        """
        Test school events with classmates.

        Arrange: Child in school with classmates
        Act: Simulate school days
        Assert: Classmate interactions occur, relationships develop
        """
        from ws.character.character_manager import create_classmates

        player = child_player
        player.c.occupation = "elementary_school"

        # Create classmates
        create_classmates(player, player.c, count=3)
        assert len(player.r) >= 3

        classmates = [p for p in player.r]
        initial_count = len(classmates)

        player.gameSpeed = 1

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

        # Classmates should still exist
        assert len(player.r) >= initial_count

    @pytest.mark.asyncio
    async def test_coworker_interactions(
        self, adult_player, game_engine, mock_output
    ):
        """
        Test work events with coworkers.

        Arrange: Adult with job and coworkers
        Act: Simulate work week
        Assert: Coworker relationships develop
        """
        from ws.character.character_manager import create_coworkers
        from ws.jobs.job_manager import setJob

        player = adult_player

        # Set job
        setJob(player.c, "Professional")

        # Create coworkers
        create_coworkers(player, player.c, count=3)
        coworker_count = len(player.r)
        assert coworker_count >= 3

        player.gameSpeed = 1

        # Simulate work week
        for _ in range(5 * 24):  # 5 work days
            player = await game_engine.run_game_tick(player, force_update=True)

        # Coworkers should still exist
        assert len(player.r) >= coworker_count

    @pytest.mark.asyncio
    async def test_romantic_partner_daily_interactions(
        self, adult_player, game_engine, mock_output
    ):
        """
        Test daily interactions with romantic partner.

        Arrange: Adult in romantic relationship
        Act: Simulate daily life together
        Assert: Relationship affinity changes appropriately
        """
        player = adult_player

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

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

        initial_affinity = getRelData(player, player.c, partner).affinity

        player.gameSpeed = 1

        # Simulate 14 days together
        for _ in range(14 * 24):
            player = await game_engine.run_game_tick(player, force_update=True)

        # Partner should still exist
        assert partner in player.r

        # Verify relationship is still active
        active_rel = getActiveRelationship(player, player.c)
        assert active_rel is not None

    @pytest.mark.asyncio
    async def test_multiple_relationships_concurrent(
        self, adult_player, game_engine, mock_output
    ):
        """
        Test multiple relationships updating together.

        Arrange: Adult with family, friends, coworkers
        Act: Simulate daily life
        Assert: All relationships update correctly
        """
        from ws.character.character_manager import create_classmates

        player = adult_player

        # Create diverse relationships
        # Family
        add_parents(player, player.c)

        # Friends
        for i in range(3):
            friend = create_character(player, f"Friend{i}", 30, "Female")
            player.r.append(friend)
            updateAffinity(player, player.c, friend, 60)

        # Coworkers
        for i in range(2):
            coworker = create_character(player, f"Coworker{i}", 35, "Male")
            player.r.append(coworker)
            updateAffinity(player, player.c, coworker, 40)

        total_relationships = len(player.r)
        assert total_relationships >= 7

        player.gameSpeed = 1

        # Simulate a week
        for _ in range(7 * 24):
            player = await game_engine.run_game_tick(player, force_update=True)

        # All relationships should still exist
        assert len(player.r) == total_relationships

    @pytest.mark.asyncio
    async def test_relationship_graph_integrity(
        self, adult_player, game_engine, mock_output
    ):
        """
        Test that relationship graph has no orphaned relationships.

        Arrange: Player with multiple relationships
        Act: Simulate time
        Assert: No orphaned relationship objects
        """
        player = adult_player

        # Create relationships
        for i in range(5):
            person = create_character(player, f"Person{i}", 30, "Male")
            player.r.append(person)
            updateAffinity(player, player.c, person, 50)

        player.gameSpeed = 1

        # Simulate a month
        for _ in range(30 * 24):
            player = await game_engine.run_game_tick(player, force_update=True)

        # Verify all relationships are valid
        for person in player.r:
            assert person.id is not None
            assert hasattr(person, 'firstname')
            assert hasattr(person, 'relationships')

    @pytest.mark.asyncio
    async def test_npc_aging(
        self, child_player, game_engine, mock_output
    ):
        """
        Test that NPCs age along with the player.

        Arrange: Child with friends
        Act: Simulate multiple years
        Assert: NPCs age appropriately
        """
        player = child_player

        # Create age-matched friends
        friend = create_character(player, "Friend", 8, "Female")
        player.r.append(friend)

        initial_friend_age = friend.ageYears

        player.gameSpeed = 1

        # Simulate 2 years
        player.c.ageDays += 2 * 365
        player.c.ageYears = 10
        player.c.ageHours = player.c.ageDays * 24

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

        # Player should be 10
        assert player.c.ageYears == 10

        # Note: NPC aging depends on handleRelationships implementation

    @pytest.mark.asyncio
    async def test_npc_life_events(
        self, adult_player, game_engine, mock_output
    ):
        """
        Test that NPCs have their own life events.

        Arrange: Adult with family members
        Act: Simulate extended period
        Assert: NPCs have events, state changes
        """
        player = adult_player

        # Create family member
        sibling = create_character(player, "Sibling", 28, "Male")
        player.r.append(sibling)

        initial_sibling_state = {
            'age': sibling.ageYears,
            'occupation': sibling.occupation,
        }

        player.gameSpeed = 1

        # Simulate 1 year
        for _ in range(365 * 24):
            player = await game_engine.run_game_tick(player, force_update=True)

        # Sibling should still exist
        assert sibling in player.r

        # Time should have passed
        assert player.c.ageYears > 0

    @pytest.mark.asyncio
    async def test_bidirectional_affinity(
        self, adult_player, game_engine, mock_output
    ):
        """
        Test that affinity updates work bidirectionally.

        Arrange: Two characters
        Act: Update affinity from A to B
        Assert: Both A's view of B and B's view of A update
        """
        player = adult_player

        # Create friend
        friend = create_character(player, "Friend", 30, "Female")
        player.r.append(friend)

        # Update affinity
        updateAffinity(player, player.c, friend, 70)

        # Get relationship data
        player_to_friend = getRelData(player, player.c, friend)
        assert player_to_friend is not None
        assert player_to_friend.affinity > 0

        player.gameSpeed = 1

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

        # Relationship should persist
        player_to_friend_after = getRelData(player, player.c, friend)
        assert player_to_friend_after is not None
