# Using Telltale Handlers

## Overview

`TelltaleHandler` implements choreographic effects over session-typed channels. This guide documents setup patterns, API surface, and operational behavior.

## Quick Start

### Basic Two-Party Protocol

```rust
use serde::{Deserialize, Serialize};
use telltale::{Message, Role};
use telltale_runtime::{
    ChoreoHandler, LabelId, RoleId, RoleName, TelltaleEndpoint, TelltaleHandler, TelltaleSession,
};

// Define roles
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
enum MyRole {
    Alice,
    Bob,
}

// Define a label type for choices (unused in this example)
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
enum Choice {
    Default,
}

impl LabelId for Choice {
    fn as_str(&self) -> &'static str {
        "Default"
    }

    fn from_str(label: &str) -> Option<Self> {
        match label {
            "Default" => Some(Choice::Default),
            _ => None,
        }
    }
}

impl RoleId for MyRole {
    type Label = Choice;

    fn role_name(&self) -> RoleName {
        match self {
            MyRole::Alice => RoleName::from_static("Alice"),
            MyRole::Bob => RoleName::from_static("Bob"),
        }
    }
}

impl Role for MyRole {
    type Message = MyMessage;

    fn seal(&mut self) {}

    fn is_sealed(&self) -> bool {
        false
    }
}

// Define messages
#[derive(Debug, Clone, Serialize, Deserialize)]
struct MyMessage {
    content: String,
}

impl Message<Box<dyn std::any::Any + Send>> for MyMessage {
    fn upcast(msg: Box<dyn std::any::Any + Send>) -> Self {
        *msg.downcast::<MyMessage>().unwrap()
    }

    fn downcast(self) -> Result<Box<dyn std::any::Any + Send>, Self> {
        Ok(Box::new(self))
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create endpoints
    let mut alice_ep = TelltaleEndpoint::new(MyRole::Alice);
    let mut bob_ep = TelltaleEndpoint::new(MyRole::Bob);

    // Setup sessions
    let (alice_session, bob_session) = TelltaleSession::pair();
    alice_ep.register_session(MyRole::Bob, alice_session);
    bob_ep.register_session(MyRole::Alice, bob_session);

    // Create handlers
    let mut alice_handler = TelltaleHandler::<MyRole, MyMessage>::new();
    let mut bob_handler = TelltaleHandler::<MyRole, MyMessage>::new();

    // Send and receive
    let msg = MyMessage {
        content: "Hello!".to_string(),
    };
    alice_handler.send(&mut alice_ep, MyRole::Bob, &msg).await?;

    let received: MyMessage = bob_handler.recv(&mut bob_ep, MyRole::Alice).await?;
    println!("Received: {}", received.content);

    Ok(())
}
```

This example creates two endpoints and connects them with a `TelltaleSession` pair. It demonstrates direct `send` and `recv` calls through the handler.

## Simulator Regression Lane

Use `telltale-simulator` to regression test projects that mix `TelltaleHandler` style protocol logic with protocol-machine execution.
The harness API gives one stable path for scenario setup and contract checks.
This avoids project-specific guard boilerplate for common replay and trace assertions.

```rust
let harness = SimulationHarness::new(&DirectAdapter::new(&handler));
let result = harness.run(&spec)?;
assert_contracts(&result, &ContractCheckConfig::default())?;
```

This check complements endpoint unit tests. It validates protocol-machine behavior under the same scenario middleware used by simulator parity lanes.

---

## Core Concepts

### Roles

Roles represent participants in the choreography. They must implement:
- `telltale::Role`
- `RoleId` (from `telltale_runtime::effects`)
- `Clone`, `Copy`, `Debug`, `PartialEq`, `Eq`, `Hash`

`RoleId` requires an associated label type that implements `LabelId`. You only need labels if your protocol uses choice.

### Messages

Messages are the data exchanged between roles. They must:
- Implement `Serialize` and `Deserialize` (via serde)
- Implement `telltale::Message`
- Be `Send` and `Sync`

### Endpoints

`TelltaleEndpoint<R>` manages the channels and session state for a role:
- One endpoint per role in the protocol
- Contains channels to all peers
- Tracks session metadata (operation counts, state descriptions)

### Sessions

`TelltaleSession` provides the canonical communication boundary:
- Created in pairs for in-process tests: `TelltaleSession::pair()`
- Or built from custom sink/stream transports with `TelltaleSession::from_sink_stream()`
- The handler serializes and deserializes messages with bincode

### Handlers

`TelltaleHandler<R, M>` interprets choreographic effects:
- Stateless (can be shared across operations)
- Implements `ChoreoHandler` trait
- Provides send, recv, choose, offer operations

---

## API Reference

### TelltaleEndpoint

```rust
impl<R: Role + Eq + Hash + Clone + Debug> TelltaleEndpoint<R>
```

This shows the generic bounds required by the endpoint type.

#### Constructor
```rust
pub fn new(local_role: R) -> Self
```
Create a new endpoint for a role.

#### Role Access
```rust
pub fn local_role(&self) -> &R
```
Get a reference to the local role.

#### Session Management
```rust
pub fn register_session(&mut self, peer: R, session: TelltaleSession)
```
Register a session (for example one produced via `TelltaleSession::pair` or
`TelltaleSession::from_sink_stream`). Use this when you need additional
transport logic such as WebSockets, recording, or custom middleware stacks.

```rust
pub fn has_channel(&self, peer: &R) -> bool
```
Check if a session exists for a peer.

```rust
pub fn close_channel(&mut self, peer: &R) -> bool
```
Close a specific session.

```rust
pub fn close_all_channels(&mut self) -> usize
```
Close all sessions and return count.

```rust
pub fn active_channel_count(&self) -> usize
```
Get number of active sessions.

```rust
pub fn is_all_closed(&self) -> bool
```
Check if all sessions are closed.

#### Metadata Access
```rust
pub fn get_metadata(&self, peer: &R) -> Option<&SessionMetadata>
```
Get session metadata for a peer.

```rust
pub fn all_metadata(&self) -> Vec<(R, &SessionMetadata)>
```
Get metadata for all sessions.

### TelltaleHandler

```rust
impl<R, M> TelltaleHandler<R, M>
```

This shows the handler type parameters. The handler is generic over role and message types.

#### Constructor
```rust
pub fn new() -> Self
```
Create a new handler.

#### ChoreoHandler Implementation

```rust
async fn send<Msg>(&mut self, ep: &mut Endpoint, to: Role, msg: &Msg) -> ChoreoResult<()>
where Msg: Serialize + Send + Sync
```
Send a message to a role.

```rust
async fn recv<Msg>(&mut self, ep: &mut Endpoint, from: Role) -> ChoreoResult<Msg>
where Msg: DeserializeOwned + Send
```
Receive a message from a role.

```rust
async fn choose(&mut self, ep: &mut Endpoint, who: Role, label: Label) -> ChoreoResult<()>
```
Make a choice (internal choice).

```rust
async fn offer(&mut self, ep: &mut Endpoint, from: Role) -> ChoreoResult<Label>
```
Offer a choice (external choice).

```rust
async fn with_timeout<F, T>(&mut self, ep: &mut Endpoint, at: Role, dur: Duration, body: F) -> ChoreoResult<T>
where F: Future<Output = ChoreoResult<T>> + Send
```
Execute operation with timeout.

### TelltaleSession Builders

```rust
TelltaleSession::pair()
```
Builds a connected in-process session pair.

```rust
TelltaleSession::from_sink_stream(sender, receiver)
```
Accepts any async sink and stream pair carrying `Vec<u8>` payloads. It exposes the pair to the handler. Use this for custom transports, then call `endpoint.register_session(peer, session)`.

### SessionMetadata

```rust
pub struct SessionMetadata {
    pub state_description: String,
    pub is_complete: bool,
    pub operation_count: usize,
}
```

This struct records session state for a peer. It is updated as operations run.

Tracks session progression:
- `state_description`: Human-readable current state
- `is_complete`: Whether session has completed
- `operation_count`: Number of operations performed

---

## Usage Patterns

### Pattern 1: Request-Response

```rust
// Client side
let request = Request { query: "data".to_string() };
handler.send(&mut endpoint, Role::Server, &request).await?;
let response: Response = handler.recv(&mut endpoint, Role::Server).await?;
```

This pattern sends a request and waits for a response. It is the simplest round trip flow.

### Pattern 2: Choice with Branches

```rust
// Sender
let decision = if condition {
    Choice::Accept
} else {
    Choice::Reject
};
handler.choose(&mut endpoint, Role::Other, decision).await?;

// Receiver
let choice = handler.offer(&mut endpoint, Role::Other).await?;
match choice {
    Choice::Accept => {
        // Handle accept branch
    }
    Choice::Reject => {
        // Handle reject branch
    }
}
```

This pattern uses `choose` and `offer` to coordinate a branch. The chosen label drives the receiver logic.

### Pattern 3: Sequential Messages

```rust
for item in items {
    handler.send(&mut endpoint, Role::Peer, &item).await?;
    let ack: Ack = handler.recv(&mut endpoint, Role::Peer).await?;
}
```

This pattern sends a batch of items with acknowledgments. Each step waits for the peer response.

### Pattern 4: Multi-Party Coordination

```rust
// Coordinator
let offer: Offer = handler.recv(&mut endpoint, Role::Buyer).await?;
handler.send(&mut endpoint, Role::Seller, &offer).await?;

let response: Response = handler.recv(&mut endpoint, Role::Seller).await?;
handler.send(&mut endpoint, Role::Buyer, &response).await?;
```

This pattern relays messages between two peers. It keeps the coordinator role in control of ordering.

### Pattern 5: Timeout Protection

```rust
let result = tokio::time::timeout(
    Duration::from_secs(5),
    handler.recv::<Response>(&mut endpoint, Role::Server),
)
.await;

match result {
    Ok(Ok(msg)) => {
        // Process message
    }
    Ok(Err(e)) => {
        // Handle handler errors
    }
    Err(_) => {
        // Handle timeout
    }
}
```

This pattern wraps a receive in a runtime timeout. Generated effect programs use
`with_timeout` internally when sender metadata includes `runtime_timeout` or
when a protocol-level `timeout ... on timeout ...` branch is present.

## Best Practices

### 1. Resource Management

Recommended approach:
```rust
// Close channels explicitly when done
endpoint.close_all_channels();
```

This closes channels explicitly when the protocol is complete.

Recommended alternative:
```rust
// Use Drop to ensure cleanup
{
    let mut endpoint = TelltaleEndpoint::new(role);
    // ... use endpoint ...
} // Drop ensures cleanup
```

This relies on drop to clean up resources at scope end.

Avoid:
```rust
// Don't forget to clean up resources
let mut endpoint = TelltaleEndpoint::new(role);
// ... use endpoint ...
// Forgot to close!
```

This leaves channels open after the protocol.

### 2. Error Handling

Recommended approach:
```rust
match handler.send(&mut ep, role, &msg).await {
    Ok(()) => { /* success */ }
    Err(ChoreographyError::Transport(e)) => {
        // Handle transport error
        tracing::error!("Send failed: {}", e);
    }
    Err(e) => {
        // Handle other errors
    }
}
```

This handles transport errors explicitly. It keeps other errors visible.

Avoid:
```rust
// Don't ignore errors
handler.send(&mut ep, role, &msg).await.unwrap();
```

This panics on failures and hides transport details.

### 3. Session Setup

Recommended approach:
```rust
// Setup all sessions before starting protocol
let (alice_session, bob_session) = TelltaleSession::pair();
alice_ep.register_session(Role::Bob, alice_session);
bob_ep.register_session(Role::Alice, bob_session);

// Then start protocol
protocol_run().await?;
```

This ensures sessions exist before the first send.

Avoid:
```rust
// Don't register sessions mid-protocol
handler.send(&mut ep, role, &msg).await?; // Might not have session!
ep.register_session(role, session); // Too late!
```

This can cause send failures when a session is missing.

### 4. Metadata Usage

Recommended approach:
```rust
// Use metadata for debugging and monitoring
if let Some(meta) = endpoint.get_metadata(&peer) {
    tracing::info!(
        peer = ?peer,
        operations = meta.operation_count,
        state = %meta.state_description,
        "Session status"
    );
}
```

This reports progress and state for each peer. It is useful for debugging.

### 5. Testing

Recommended approach:
```rust
#[tokio::test]
async fn test_protocol() {
    // Setup test environment
    let mut alice_ep = TelltaleEndpoint::new(Role::Alice);
    let mut bob_ep = TelltaleEndpoint::new(Role::Bob);
    
    let (alice_session, bob_session) = TelltaleSession::pair();
    alice_ep.register_session(Role::Bob, alice_session);
    bob_ep.register_session(Role::Alice, bob_session);
    
    // Test protocol
    let msg = TestMessage { data: vec![1, 2, 3] };
    handler.send(&mut alice_ep, Role::Bob, &msg).await.unwrap();
    
    let received: TestMessage = handler.recv(&mut bob_ep, Role::Alice).await.unwrap();
    assert_eq!(received.data, vec![1, 2, 3]);
}
```

This sets up a local session pair and exercises a full send and receive. It validates handler wiring in tests.

## Operational Notes

`TelltaleHandler` is intentionally thin. Most correctness comes from the session-typed state in `TelltaleEndpoint` and from protocol generation. Treat the handler as a transport adapter with stable behavior. Keep role definitions and message definitions close to each protocol module to make type drift easy to detect during review.

For production integrations, prefer explicit lifecycle management even though drop-based cleanup exists. Close sessions when a protocol is complete. Inspect endpoint metadata after important milestones such as branch commits or retries. Metadata captures operation counts and completion status per peer.

When introducing custom transports, keep serialization and framing deterministic. Use one canonical message encoding in both tests and production. Add at least one integration test that runs both endpoints concurrently and one scenario test that exercises failure handling.
