# Message Routing & Station Deep Drill

The `Station` is the "brain" of Kurier. It is responsible for registering message handlers and dispatching incoming "Passengers" to the correct functions based on their type and origin.

## Table of Contents

- [1. How Routing Works](#1-how-routing-works)
- [2. Type-Based Dispatch](#2-type-based-dispatch)
- [3. Explicit Event Handling](#3-explicit-event-handling)
- [4. Accessing Metadata (Context)](#4-accessing-metadata-context)
- [5. Async & Sync Harmony](#5-async--sync-harmony)
- [6. The Dispatch Algorithm (Pseudocode)](#6-the-dispatch-algorithm-pseudocode)

---

## 1. How Routing Works

Kurier uses a **two-tier** routing system, allowing you to be as specific or as general as you need. When a message arrives, the `Station` resolves matching handlers in this order:

1.  **Instance-Level (Specific Line)**: Handlers registered to a specific line name (e.g., `"orders_line"`).
2.  **Category-Level (Bus Type)**: Handlers registered to a broad transport category (e.g., all `BusType.RABBIT_MQ` lines).

### Example: Specific vs. General
```python
# Tier 1: This handler ONLY runs for messages from the 'orders_line'
@Station.add_handler(source="orders_line")
async def handle_specific_orders(message: OrderCreated):
    pass

# Tier 2: This handler runs for ANY message of type 'OrderCreated'
# arriving from ANY RabbitMQ line.
@Station.add_handler(source=BusType.RABBIT_MQ)
async def handle_any_rabbitmq_order(message: OrderCreated):
    pass
```

---

## 2. Type-Based Dispatch

Kurier is **Type-Aware**. You don't need to manually parse strings or route by topic name inside your handlers. The `Station` inspects the **type annotation** of the first parameter.

### Supported Types:
*   **Pydantic Models**: (Recommended) Automatic `model_validate` ensures data integrity.
*   **Dataclasses**: Automatically instantiated with message data.
*   **Primitive Types**: Strings, integers, or dicts can be used for simpler message payloads.

### Collision Prevention:
In large applications, two different modules might define a class with the same name (e.g., `OrderCreated`). Kurier handles this by automatically recording the **Fully Qualified Name** (`module.ClassName`) in the message envelope.

---

## 3. Explicit Event Handling

While type-based routing is standard, you can also route based on explicit "Event Types" (strings). This is useful for cross-language compatibility or legacy systems.

```python
@Station.add_event_handler(event_type="legacy.order.v1", source=BusType.KAFKA)
async def handle_legacy_event(message: dict):
    # 'message' will be the raw payload
    pass
```

---

## 4. Accessing Metadata (Context)

Sometimes you need to access the "Passenger's ticket"—the `correlation_id`, original broker metadata, or custom headers. Handlers can accept a second, untyped parameter for the envelope context:

```python
@Station.add_handler()
async def handle_with_context(message: OrderCreated, *context):
    envelope = context[0] if context else {}
    print(f"Correlation ID: {envelope['correlation_id']}")
    print(f"Original Metadata: {envelope['metadata']}")
```

The `*context` captures the `KurierMessage` envelope dictionary as a variadic argument. Access it via `context[0]`. Using `*context` keeps the parameter optional—handlers work with or without it.

---

## 5. Async & Sync Harmony

Kurier treats `async` and `sync` handlers with equal priority:

*   **Async Handlers**: Awaited directly on the event loop.
*   **Sync Handlers**: Automatically offloaded to a **Thread-Pool Executor**.

This ensures that a slow, synchronous database call inside a handler won't block the `asyncio` event loop or prevent other messages from being processed.

---

## 6. The Dispatch Algorithm (Pseudocode)

1.  Receive `raw_data` from `Line`.
2.  Decode `raw_data` into `KurierMessage` envelope.
3.  Identify `object_type_name` and `qualified_name`.
4.  Look for handlers in `station_registry[line_name]`.
5.  Look for handlers in `station_registry[bus_type]`.
6.  For each matching handler:
    *   Convert `message_payload` to the handler's parameter type.
    *   Invoke handler (async or offloaded to thread-pool).
