> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/donnemartin/system-design-primer/llms.txt
> Use this file to discover all available pages before exploring further.

# Design a Call Center

> Implement a call center system with employee hierarchy and call routing

## Problem Statement

Design a call center system that routes incoming calls to available employees based on a hierarchy, with escalation support when employees cannot handle calls.

## Constraints and Assumptions

* **Employee levels**: Operator, Supervisor, Director
* **Call routing**: Operators always get initial calls
* **Escalation**: If no operator is available or they can't handle the call, escalate to supervisors, then directors
* **Director capability**: Directors can handle all calls
* **Queuing**: Calls are queued if nobody can answer
* **No VIP support**: All calls have equal priority
* **Valid inputs**: Assume inputs are valid

## Design Overview

The call center system uses several classes implementing a hierarchy pattern:

1. **Rank**: Enum defining employee levels
2. **Employee**: Abstract base class for all employee types
3. **Operator, Supervisor, Director**: Concrete employee implementations
4. **Call**: Represents an incoming call
5. **CallCenter**: Orchestrates call routing and queuing

<Note>
  This design uses the Chain of Responsibility pattern, where calls are passed through a hierarchy of handlers (operators → supervisors → directors) until one can handle it.
</Note>

## Implementation

### Rank Enumeration

Defines the employee hierarchy:

```python theme={null}
from enum import Enum

class Rank(Enum):
    OPERATOR = 0
    SUPERVISOR = 1
    DIRECTOR = 2
```

### Employee Base Class

Abstract class with common employee functionality:

```python theme={null}
from abc import ABCMeta, abstractmethod

class Employee(metaclass=ABCMeta):

    def __init__(self, employee_id, name, rank, call_center):
        self.employee_id = employee_id
        self.name = name
        self.rank = rank
        self.call = None
        self.call_center = call_center

    def take_call(self, call):
        """Assume the employee will always successfully take the call."""
        self.call = call
        self.call.employee = self
        self.call.state = CallState.IN_PROGRESS

    def complete_call(self):
        self.call.state = CallState.COMPLETE
        self.call_center.notify_call_completed(self.call)

    @abstractmethod
    def escalate_call(self):
        pass

    def _escalate_call(self):
        self.call.state = CallState.READY
        call = self.call
        self.call = None
        self.call_center.notify_call_escalated(call)
```

### Concrete Employee Classes

Each level implements the escalation logic:

```python theme={null}
class Operator(Employee):

    def __init__(self, employee_id, name):
        super(Operator, self).__init__(employee_id, name, Rank.OPERATOR)

    def escalate_call(self):
        self.call.level = Rank.SUPERVISOR
        self._escalate_call()


class Supervisor(Employee):

    def __init__(self, employee_id, name):
        super(Supervisor, self).__init__(employee_id, name, Rank.SUPERVISOR)

    def escalate_call(self):
        self.call.level = Rank.DIRECTOR
        self._escalate_call()


class Director(Employee):

    def __init__(self, employee_id, name):
        super(Director, self).__init__(employee_id, name, Rank.DIRECTOR)

    def escalate_call(self):
        raise NotImplemented('Directors must be able to handle any call')
```

### Call Classes

Represent call state and properties:

```python theme={null}
class CallState(Enum):
    READY = 0
    IN_PROGRESS = 1
    COMPLETE = 2


class Call(object):

    def __init__(self, rank):
        self.state = CallState.READY
        self.rank = rank
        self.employee = None
```

### CallCenter Class

Orchestrates call routing and management:

```python theme={null}
from collections import deque

class CallCenter(object):

    def __init__(self, operators, supervisors, directors):
        self.operators = operators
        self.supervisors = supervisors
        self.directors = directors
        self.queued_calls = deque()

    def dispatch_call(self, call):
        if call.rank not in (Rank.OPERATOR, Rank.SUPERVISOR, Rank.DIRECTOR):
            raise ValueError('Invalid call rank: {}'.format(call.rank))
        employee = None
        if call.rank == Rank.OPERATOR:
            employee = self._dispatch_call(call, self.operators)
        if call.rank == Rank.SUPERVISOR or employee is None:
            employee = self._dispatch_call(call, self.supervisors)
        if call.rank == Rank.DIRECTOR or employee is None:
            employee = self._dispatch_call(call, self.directors)
        if employee is None:
            self.queued_calls.append(call)

    def _dispatch_call(self, call, employees):
        for employee in employees:
            if employee.call is None:
                employee.take_call(call)
                return employee
        return None

    def notify_call_escalated(self, call):  # ...
    def notify_call_completed(self, call):  # ...
    def dispatch_queued_call_to_newly_freed_employee(self, call, employee):  # ...
```

## Key Design Patterns

### Chain of Responsibility

Calls cascade through levels until handled:

```
Incoming Call
     ↓
┌─────────────┐
│  Operators  │ → Available? → Take call
└─────────────┘      ↓ No
     ↓
┌─────────────┐
│ Supervisors │ → Available? → Take call
└─────────────┘      ↓ No
     ↓
┌─────────────┐
│  Directors  │ → Available? → Take call
└─────────────┘      ↓ No
     ↓
┌─────────────┐
│    Queue    │
└─────────────┘
```

### Abstract Factory Pattern

The `Employee` abstract base class defines the interface, with concrete implementations for each rank level.

### Observer Pattern

The `notify_call_completed()` and `notify_call_escalated()` methods allow the call center to react to state changes.

## Complexity Analysis

| Operation        | Time Complexity | Notes                                            |
| ---------------- | --------------- | ------------------------------------------------ |
| dispatch\_call() | O(n)            | Where n is the number of employees at each level |
| take\_call()     | O(1)            | Direct assignment                                |
| escalate\_call() | O(1)            | State update and notification                    |

## Design Considerations

### Advantages

* **Clear hierarchy**: Well-defined escalation path
* **Flexible**: Easy to add new employee types or ranks
* **Separation of concerns**: Each class has a single responsibility
* **Extensible**: Can add features like call priority or specialized routing

### Call Flow

1. **Call arrives** → Assigned OPERATOR rank initially
2. **Dispatch attempt** → Try to find available operator
3. **Escalate if needed** → Move up hierarchy
4. **Queue if all busy** → Store in deque for later
5. **Complete call** → Free employee for next call

### Potential Improvements

1. **Priority queue**: Support VIP or urgent calls
2. **Skills-based routing**: Route calls based on employee expertise
3. **Load balancing**: Distribute calls evenly among available employees
4. **Call statistics**: Track metrics like wait time, handle time, escalation rate
5. **Automated callbacks**: Call customers back when employee becomes available
6. **Concurrent handling**: Use threading for simultaneous call processing
7. **Call recording**: Log conversations for quality assurance
