# Service-Centric Architecture

## Overview

The ABM framework now uses a **service-centric architecture** where:

1. **Agents are data containers** - They hold state (age, mood, race, etc.) with minimal behavior
2. **Services contain all behavior logic** - All actions, interactions, and computations happen in services
3. **Only services schedule events** - Services are responsible for scheduling their own interactions
4. **Agents are passed as arguments** - Services act on agents rather than agents calling services

## Architecture Principles

### Agents = State
```python
class Student(Agent):
    """A student - primarily a data/state container."""

    def __init__(self, agent_id: str, age: int, race: str, neighborhood=None):
        super().__init__(agent_id)
        # State attributes only
        self.age = age
        self.race = race
        self.mood = 0.5
        self.disciplinaryRecord = []
        self.neighborhood = neighborhood
        # No behavior methods!
```

### Services = Behavior
```python
class DisciplineService(Service):
    """Service for handling all discipline-related behavior."""

    def evaluate_misbehavior(self, student: Student, teachers: List[Teacher]):
        """Service method that acts on agents."""
        teacher = choice(teachers)
        prob_misbehave = 1 - (student.mood * teacher.attitude)
        if random() < prob_misbehave:
            self.record_misbehavior(student, teacher)

    def record_misbehavior(self, student: Student, teacher: Teacher):
        """Service modifies agent state."""
        severity = random()
        # Apply bias calculation
        bias_element = teacher.bias
        if student.race == "NHBlack":
            bias_element *= 2
        # ... more logic
        student.disciplinaryRecord.append(response)
        student.mood -= (0.1 * response)
```

### Services Schedule Events
```python
class DisciplineService(Service):
    def schedule_behavior_evaluations(self, schedule, students, teachers,
                                      start_step, end_step):
        """Service schedules its own events."""
        for step in range(start_step, end_step):
            student = choice(students)
            # Service method is scheduled, agents are arguments
            schedule.add_event(
                step=step,
                event=lambda s=student, t=teachers: self.evaluate_misbehavior(s, t)
            )
```

## Implemented Services

### 1. FriendshipManager
**Purpose**: Manages all friendship-related behavior

**Key Methods**:
- `make_friend(student1, student2, initial_strength)` - Create a friendship
- `strengthen_friendship(friendship, amount)` - Increase friendship strength
- `weaken_friendship(friendship, amount)` - Decrease friendship strength
- `affect_student_moods_from_friendship(friendship)` - Modify student moods based on friendship
- `process_friendship_interaction(friendship)` - Random strengthen/weaken + mood effects
- `schedule_friendship_interactions(schedule, start, end)` - Schedule all friendship events

**Usage**:
```python
# Create and register service
friendship_mgr = FriendshipManager("friendship_manager")
world.services.register(friendship_mgr)

# Use service to create friendships
friendship_mgr.make_friend(student1, student2, strength=0.7)

# Service schedules its own interactions
friendship_mgr.schedule_friendship_interactions(schedule, 1, 1000)
```

### 2. DisciplineService
**Purpose**: Handles all discipline and misbehavior

**Key Methods**:
- `evaluate_misbehavior(student, teachers)` - Check if student misbehaves
- `record_misbehavior(student, teacher)` - Record incident with racial bias
- `schedule_behavior_evaluations(schedule, students, teachers, start, end)` - Schedule evaluations

**Usage**:
```python
# Create and register service
discipline = DisciplineService("discipline_service")
world.services.register(discipline)

# Service schedules behavior checks
discipline.schedule_behavior_evaluations(schedule, students, teachers, 1, 1000)
```

### 3. CounselingService
**Purpose**: Handles counseling interactions

**Key Methods**:
- `provide_counseling(student, counselor)` - Conduct counseling session
- `adjust_mood(student, amount)` - Modify student mood

**Usage**:
```python
counseling = CounselingService("counseling_service")
world.services.register(counseling)

# Call service method with agents as arguments
counseling.provide_counseling(student, counselor)
```

### 4. HousingService
**Purpose**: Manages housing disruptions and neighborhood effects

**Key Methods**:
- `evaluate_housing_disruption(student)` - Check for housing issues
- `apply_housing_disruption(student)` - Apply effects of disruption

**Usage**:
```python
housing = HousingService("housing_service")
world.services.register(housing)

# Service can schedule monthly evaluations
for step in monthly_steps:
    schedule.add_event(
        step=step,
        event=lambda s=student: housing.evaluate_housing_disruption(s)
    )
```

## World Setup Pattern

```python
class SchoolWorld(WorldBuilder):
    def __init__(self, param_dict):
        super().__init__(param_dict)  # Initializes ServiceRegistry

        # 1. Create services
        self.friendshipManager = FriendshipManager("friendship_manager")
        self.disciplineService = DisciplineService("discipline_service")
        self.counselingService = CounselingService("counseling_service")
        self.housingService = HousingService("housing_service")

        # 2. Register all services
        self.services.register(self.friendshipManager)
        self.services.register(self.disciplineService)
        self.services.register(self.counselingService)
        self.services.register(self.housingService)

        # 3. Create agent populations (just data, no behavior)
        self.population = SchoolPopulation(param_dict, neighborhoods)

        # 4. Use services to set up initial state
        self.friendshipManager.make_random_friendships(self.population, 50)

    def setup_events(self):
        """Services schedule their own events."""
        start_step, end_step = 1, 1001

        # Each service schedules what it needs
        self.disciplineService.schedule_behavior_evaluations(
            self.schedule, self.population.students,
            self.population.teachers, start_step, end_step
        )

        self.friendshipManager.schedule_friendship_interactions(
            self.schedule, start_step, end_step
        )
```

## Benefits of Service-Centric Design

### 1. **True Separation of Concerns**
- **Agents**: Pure data/state (age, mood, race, attributes)
- **Services**: All behavior logic (friendships, discipline, movement)
- **World**: Orchestrates services
- **Schedule**: Executes service-scheduled events

### 2. **Zero Circular Dependencies**
- Agents don't import or reference services or world
- Agents can be created independently with zero dependencies
- Services reference agents through type hints only

### 3. **All Domain Logic in One Place**
```python
# All friendship logic in FriendshipManager
# All discipline logic in DisciplineService
# Want to change how friendships work? Edit one file!
```

### 4. **Easy Testing**
```python
# Test service behavior in isolation
def test_discipline_service():
    service = DisciplineService()
    student = Student("1", 15, "NHBlack")
    teacher = Teacher("T1", "NHwhite")

    service.record_misbehavior(student, teacher)
    assert len(student.disciplinaryRecord) > 0
    assert student.mood < 0.5
```

### 5. **Service Composability**
```python
# Services can call other services
class AdvancedDisciplineService(DisciplineService):
    def record_misbehavior(self, student, teacher):
        super().record_misbehavior(student, teacher)

        # Trigger counseling if too many incidents
        if len(student.disciplinaryRecord) > 5:
            counseling_svc = self.get_world().services.get_by_type(CounselingService)
            counseling_svc.schedule_session(student)
```

### 6. **Clear Event Scheduling**
```python
# Before (agent-centric):
schedule.add_event(step, lambda: student.chance_to_misbehave(teachers))
# - Hard to find where behavior is defined
# - Mixed concerns

# After (service-centric):
discipline_service.schedule_behavior_evaluations(schedule, students, teachers, 1, 1000)
# - Clear ownership
# - Service manages its own scheduling
```

## Comparison: Agent-Centric vs Service-Centric

### Agent-Centric (Old Way - Avoid This)
```python
class Student(Agent):
    def __init__(self, world):
        self.world = world  # ❌ Circular dependency

    def make_friend(self, other, friendship_mgr):
        # ❌ Behavior in agent
        friendship_mgr.register(Friendship(self, other))

    def misbehave(self, teacher):
        # ❌ Complex logic in agent
        severity = random()
        if self.race == "NHBlack":
            severity *= 2
        # ... more logic

# In setup_events:
schedule.add_event(step, lambda: student.misbehave(teacher))
# ❌ Agent schedules itself
```

### Service-Centric (New Way - Use This)
```python
class Student(Agent):
    def __init__(self, age, race):
        self.age = age  # ✅ Just data
        self.race = race
        self.mood = 0.5
        # No world reference, no behavior methods

class DisciplineService(Service):
    def record_misbehavior(self, student, teacher):
        # ✅ All behavior logic in service
        severity = random()
        if student.race == "NHBlack":
            severity *= 2
        student.disciplinaryRecord.append(severity)

    def schedule_behavior_evaluations(self, schedule, students, teachers, start, end):
        # ✅ Service schedules its own events
        for step in range(start, end):
            student = choice(students)
            schedule.add_event(
                step, lambda s=student: self.evaluate_misbehavior(s, teachers)
            )

# In setup_events:
discipline_service.schedule_behavior_evaluations(schedule, students, teachers, 1, 1000)
# ✅ Clean delegation to service
```

## Creating New Services

```python
from layer1_core.managers.service import Service

class MyNewService(Service):
    """Service for handling X behavior."""

    def __init__(self, service_id: str = "my_service"):
        super().__init__(service_id)
        # Initialize service state if needed

    def do_something(self, agent: Agent, other_arg):
        """Service method that acts on agents.

        Args:
            agent: The agent to act on
            other_arg: Other parameters needed
        """
        # All logic here
        # Modify agent state as needed
        agent.some_attribute = new_value

    def schedule_events(self, schedule, agents, start_step, end_step):
        """Service schedules its own events.

        Args:
            schedule: The simulation schedule
            agents: List of agents
            start_step: First step
            end_step: Last step
        """
        for step in range(start_step, end_step):
            agent = choice(agents)
            schedule.add_event(
                step=step,
                event=lambda a=agent: self.do_something(a, some_value)
            )
```

## Summary

The service-centric architecture provides:
- ✅ **Agents as data containers** - Clean, simple, testable
- ✅ **Services with all behavior** - Centralized domain logic
- ✅ **Services schedule events** - Clear ownership and organization
- ✅ **Zero circular dependencies** - Clean architecture
- ✅ **Easy to test** - Mock services independently
- ✅ **Easy to extend** - Add new services without touching agents
- ✅ **Clear separation of concerns** - Each layer has one responsibility

This pattern scales well for complex ABM simulations and keeps the codebase maintainable as it grows.