# Service Pattern Implementation Summary

## Problem Solved

The school demo had a **circular dependency** issue:
- `Student` class needed to reference `SchoolWorld` to access services
- `SchoolWorld` needed to create `Student` instances
- This created import cycles and tight coupling

## Solution: Service Pattern

We implemented a **Service** base class and **ServiceRegistry** that allows:
1. Agents to access specific services they need without knowing about the World
2. Services to be registered and retrieved by type or ID
3. Clean separation of concerns and loose coupling

## Changes Made

### 1. Core Framework (`src/layer1_core/managers/service.py`)

Created two new classes:

- **`Service`**: Base class for all services
  - Has a unique `service_id`
  - Can optionally reference the world if needed
  - Provides shared functionality to agents

- **`ServiceRegistry`**: Central registry for managing services
  - Stores services by ID and by type
  - Allows retrieval via `get(service_id)` or `get_by_type(ServiceType)`

### 2. Updated WorldBuilder (`src/layer2_simulation/world_builder.py`)

- Added `self.services = ServiceRegistry()` to WorldBuilder initialization
- All subclasses of WorldBuilder now have access to the service registry

### 3. Refactored School Demo

#### `FriendshipManager` → Service
```python
class FriendshipManager(Service, NetworkManager):
    def __init__(self, service_id: str = "friendship_manager", ...):
        Service.__init__(self, service_id)
        NetworkManager.__init__(self)
        # ... rest of implementation
```

#### `Student` → No World Reference
```python
class Student(Agent):
    def __init__(self, agent_id: str, age: int, race: str,
                 neighborhood: Optional[Neighborhood] = None):
        # No self.world anymore!
        # Services are passed as parameters when needed
```

#### `SchoolWorld` → Register Services
```python
class SchoolWorld(WorldBuilder):
    def __init__(self, param_dict) -> None:
        super().__init__(param_dict)  # Initialize ServiceRegistry

        # Create and register services
        self.friendshipManager = FriendshipManager("friendship_manager")
        self.services.register(self.friendshipManager)

        # Create population without passing world
        self.neighborhoods = self.make_neighborhoods(...)
        self.population = SchoolPopulation(param_dict, self.neighborhoods)
```

#### `SchoolPopulation` → No World Reference
```python
class SchoolPopulation(Population):
    def __init__(self, param_dict: dict, neighborhoods: List[Neighborhood]):
        # Receives only what it needs, not the entire world
        self.neighborhoods = neighborhoods
        # Creates students without passing world
```

## Benefits

1. **No Circular Dependencies**: Agents don't import or reference World classes
2. **Loose Coupling**: Agents only depend on specific services they use
3. **Better Testability**: Can mock services in unit tests
4. **Clear Dependencies**: Service requirements are explicit in method signatures
5. **Flexibility**: Easy to swap service implementations
6. **Scalability**: Easy to add new services without modifying agents

## Usage Pattern

### Registering a Service
```python
# In WorldBuilder subclass
my_service = MyService("service_id")
self.services.register(my_service)
```

### Accessing a Service
```python
# By ID
service = world.services.get("friendship_manager")

# By type
friendship_mgr = world.services.get_by_type(FriendshipManager)
```

### Passing Services to Agents
```python
# Services are passed as parameters when needed
student.make_friend(other_student, friendship_manager)

# Or in event scheduling
friendship_mgr = self.services.get_by_type(FriendshipManager)
self.schedule.add_event(
    step=step,
    event=lambda s=student, fm=friendship_mgr: s.do_something(fm)
)
```

## Test Results

All tests pass successfully:
- ✓ Basic simulation setup and execution
- ✓ Student mood changes during simulation
- ✓ Full simulation with 100 students, 1000 steps
- ✓ Friendship network formation and dynamics
- ✓ Disciplinary actions and racial bias modeling

## Next Steps

You can now:
1. Create additional services (e.g., `NeighborhoodService`, `CounselingService`)
2. Add service-based interactions between agents
3. Test services independently from the full simulation
4. Extend the pattern to other demos (infection_sim, etc.)

## Example: Creating a New Service

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

class CounselingService(Service):
    """Service for managing counselor-student interactions."""

    def __init__(self, service_id: str = "counseling_service"):
        super().__init__(service_id)
        self.counseling_sessions = []

    def schedule_session(self, student: Student, counselor: Counselor):
        self.counseling_sessions.append((student, counselor))

    def conduct_sessions(self):
        for student, counselor in self.counseling_sessions:
            counselor.counsel(student)
        self.counseling_sessions.clear()
```

Then register it in SchoolWorld:
```python
self.counselingService = CounselingService()
self.services.register(self.counselingService)
```