# Priority System

Simple tuple-based priorities for deterministic execution ordering.

## Basic Concept

Priorities are just **Python tuples**. Python compares tuples element-by-element, left-to-right:

```python
(0, 1000, 5) < (0, 1001, 3)  # True: 0==0, but 1000 < 1001
(0, 1000, 5) < (1, 500, 2)   # True: 0 < 1
(0, 1000) < (0, 1000, 5)     # True: shorter wins if equal so far
```

**Lower values = higher priority** (processed first).

## Priority Categories

```python
class Priority(IntEnum):
    DATA_SOURCE = 0        # Market data, real-time feeds
    TIMER = 1              # Scheduled tasks, cron jobs
    USER_AGENT = 2         # User-agent interactions (chat)
    USER_DATA_REQUEST = 3  # User data requests (charts)
    SYSTEM = 4             # Background tasks, cleanup
    LOW = 5                # Retries after conflicts
```

## Usage Examples

### Simple Priority

```python
# Just use the Priority enum
trigger = MyTrigger("task", priority=Priority.SYSTEM)
await queue.enqueue(trigger)

# Results in tuple: (4, queue_seq)
```

### Compound Priority (Tuple)

```python
# DataSource: sort by event time (older bars first)
trigger = DataUpdateTrigger(
    source_name="binance",
    symbol="BTC/USDT",
    resolution="1m",
    bar_data={"time": 1678896000, "open": 50000, ...}
)
await queue.enqueue(trigger)

# Results in tuple: (0, 1678896000, queue_seq)
#                    ^  ^            ^
#                    |  |            Queue insertion order (FIFO)
#                    |  Event time (candle end time)
#                    DATA_SOURCE priority
```

### Manual Override

```python
# Override at enqueue time
await queue.enqueue(
    trigger,
    priority_override=(Priority.DATA_SOURCE, custom_time, custom_sort)
)

# Queue appends queue_seq: (0, custom_time, custom_sort, queue_seq)
```

## Common Patterns

### Market Data (Process Chronologically)

```python
# Bar from 10:00 → (0, 10:00_timestamp, queue_seq)
# Bar from 10:05 → (0, 10:05_timestamp, queue_seq)
#
# 10:00 bar processes first (earlier event_time)

DataUpdateTrigger(
    ...,
    bar_data={"time": event_timestamp, ...}
)
```

### User Messages (FIFO Order)

```python
# Message #1 → (2, msg1_timestamp, queue_seq)
# Message #2 → (2, msg2_timestamp, queue_seq)
#
# Message #1 processes first (earlier timestamp)

AgentTriggerHandler(
    session_id="user1",
    message_content="...",
    message_timestamp=unix_timestamp  # Optional, defaults to now
)
```

### Scheduled Tasks (By Schedule Time)

```python
# Job scheduled for 9 AM → (1, 9am_timestamp, queue_seq)
# Job scheduled for 2 PM → (1, 2pm_timestamp, queue_seq)
#
# 9 AM job processes first

CronTrigger(
    name="morning_sync",
    inner_trigger=...,
    scheduled_time=scheduled_timestamp
)
```

## Execution Order Example

```
Queue contains:
1. DataSource (BTC @ 10:00) → (0, 10:00, 1)
2. DataSource (BTC @ 10:05) → (0, 10:05, 2)
3. Timer (scheduled 9 AM)   → (1, 09:00, 3)
4. User message #1          → (2, 14:30, 4)
5. User message #2          → (2, 14:35, 5)

Dequeue order:
1. DataSource (BTC @ 10:00)  ← 0 < all others
2. DataSource (BTC @ 10:05)  ← 0 < all others, 10:05 > 10:00
3. Timer (scheduled 9 AM)    ← 1 < remaining
4. User message #1           ← 2 < remaining, 14:30 < 14:35
5. User message #2           ← last
```

## Short Tuple Wins

If tuples are equal up to the length of the shorter one, **shorter tuple has higher priority**:

```python
(0, 1000) < (0, 1000, 5)      # True: shorter wins
(0,) < (0, 1000)              # True: shorter wins
(Priority.DATA_SOURCE,) < (Priority.DATA_SOURCE, 1000)  # True
```

This is Python's default tuple comparison behavior. In practice, we always append `queue_seq`, so this rarely matters (all tuples end up same length).

## Integration with Triggers

### Trigger Sets Its Own Priority

```python
class MyTrigger(Trigger):
    def __init__(self, event_time):
        super().__init__(
            name="my_trigger",
            priority=Priority.DATA_SOURCE,
            priority_tuple=(Priority.DATA_SOURCE.value, event_time)
        )
```

Queue appends `queue_seq` automatically:
```python
# Trigger's tuple: (0, event_time)
# After enqueue:   (0, event_time, queue_seq)
```

### Override at Enqueue

```python
# Ignore trigger's priority, use override
await queue.enqueue(
    trigger,
    priority_override=(Priority.TIMER, scheduled_time)
)
```

## Why Tuples?

✅ **Simple**: No custom classes, just native Python tuples
✅ **Flexible**: Add as many sort keys as needed
✅ **Efficient**: Python's tuple comparison is highly optimized
✅ **Readable**: `(0, 1000, 5)` is obvious what it means
✅ **Debuggable**: Can print and inspect easily

Example:
```python
# Old: CompoundPriority(primary=0, secondary=1000, tertiary=5)
# New: (0, 1000, 5)

# Same semantics, much simpler!
```

## Advanced: Custom Sorting

Want to sort by multiple factors? Just add more elements:

```python
# Sort by: priority → symbol → event_time → queue_seq
priority_tuple = (
    Priority.DATA_SOURCE.value,
    symbol_id,  # e.g., hash("BTC/USDT")
    event_time,
    # queue_seq appended by queue
)
```

## Summary

- **Priorities are tuples**: `(primary, secondary, ..., queue_seq)`
- **Lower = higher priority**: Processed first
- **Element-by-element comparison**: Left-to-right
- **Shorter tuple wins**: If equal up to shorter length
- **Queue appends queue_seq**: Always last element (FIFO within same priority)

That's it! No complex classes, just tuples. 🎯
