# Messaging

## Sending a Message (SignalR)

A message typed in the TUI travels through encryption, the SignalR hub,
`ChatService` validation, database storage, and fan-out to both SignalR and IRC
clients.

```mermaid
sequenceDiagram
    participant UI as MainWindow (TUI)
    participant AO as AppOrchestrator
    participant EHC as EchoHubConnection
    participant Hub as ChatHub
    participant CS as ChatService
    participant LE as LinkEmbedService
    participant DB as SQLite
    participant SRB as SignalRBroadcaster
    participant IRCB as IrcBroadcaster
    participant Clients as Other Clients

    UI->>AO: OnMessageSubmitted(channel, text)
    AO->>AO: IsCommand(text)? → No
    AO->>EHC: SendMessageAsync(channel, text)
    EHC->>EHC: Encrypt(text) → ciphertext
    EHC->>Hub: InvokeAsync("SendMessage", channel, ciphertext)
    Hub->>CS: SendMessageAsync(userId, username, channel, ciphertext)
    CS->>CS: Decrypt(ciphertext) → plaintext
    CS->>CS: Validate (length, newlines, channel exists)
    CS->>DB: Check mute status
    CS->>LE: TryGetEmbedsAsync(plaintext)
    LE->>LE: Extract URLs, fetch OG tags
    LE-->>CS: List<EmbedDto> (or null)
    CS->>DB: INSERT Message (encrypted at rest)
    CS->>CS: Re-encrypt plaintext for transport
    CS->>CS: Build MessageDto with embeds

    par Fan-out to all broadcasters
        CS->>SRB: SendMessageToChannelAsync(channel, dto)
        SRB->>Clients: HubContext.Group(channel).ReceiveMessage(dto)
    and
        CS->>IRCB: SendMessageToChannelAsync(channel, dto)
        IRCB->>IRCB: Decrypt → format as PRIVMSG lines
        IRCB->>Clients: Send to each IRC conn (skip sender)
    end
```

---

## Sending a Message (IRC)

Messages from IRC clients follow the same `ChatService` path but enter as
plaintext (no app-layer encryption).

```mermaid
sequenceDiagram
    participant IRC as IRC Client
    participant CH as IrcCommandHandler
    participant CS as ChatService
    participant DB as SQLite
    participant SRB as SignalRBroadcaster
    participant IRCB as IrcBroadcaster

    IRC->>CH: PRIVMSG #channel :Hello world
    CH->>CH: Parse target + content
    CH->>CH: IrcToEchoHubChannel("#channel") → "channel"
    CH->>CS: SendMessageAsync(userId, username, "channel", "Hello world")
    CS->>CS: Decrypt("Hello world") → passthrough (no $ENC$ prefix)
    CS->>CS: Validate, check mute, fetch embeds
    CS->>DB: INSERT Message
    CS->>CS: Encrypt plaintext for SignalR transport

    par Fan-out
        CS->>SRB: SendMessageToChannelAsync (encrypted for SignalR)
    and
        CS->>IRCB: SendMessageToChannelAsync (decrypt → PRIVMSG)
        IRCB->>IRCB: Skip sender (IRC echo suppression)
    end
```

---

## Receiving a Message (TUI Client)

When a message arrives via SignalR, the client decrypts it, adds it to the chat
view, and optionally plays a notification sound for @mentions.

```mermaid
sequenceDiagram
    participant SRB as SignalRBroadcaster
    participant EHC as EchoHubConnection
    participant AO as AppOrchestrator
    participant MM as ChatMessageManager
    participant UI as MainWindow

    SRB->>EHC: ReceiveMessage(MessageDto)
    EHC->>EHC: Decrypt(message.Content)
    EHC-->>AO: OnMessageReceived(decrypted dto)
    AO->>AO: InvokeUI (thread-safe)
    AO->>MM: AddMessage(message)
    MM->>UI: Render in chat ListView
    alt Message contains @username
        AO->>AO: PlayAsync() notification sound
    end
```

---

## Command Execution

Slash commands (`/status`, `/nick`, `/kick`, etc.) are parsed client-side and
dispatched to appropriate handlers, which call REST APIs or SignalR methods.

```mermaid
sequenceDiagram
    participant UI as MainWindow
    participant AO as AppOrchestrator
    participant CMD as CommandHandler
    participant API as ApiClient
    participant EHC as EchoHubConnection
    participant Server as Server (API/Hub)

    UI->>AO: OnMessageSubmitted(channel, "/kick baduser")
    AO->>CMD: IsCommand("/kick baduser")? → true
    AO->>CMD: HandleAsync("/kick baduser")
    CMD->>CMD: Parse → command="kick", args="baduser"

    alt API command (kick, ban, mute, nick, etc.)
        CMD-->>AO: Fire OnKickRequested("baduser")
        AO->>API: KickUserAsync("baduser")
        API->>Server: POST /api/moderation/kick/baduser
    else Hub command (status, join, leave, etc.)
        CMD-->>AO: Fire OnSetStatus / OnJoinChannel / etc.
        AO->>EHC: UpdateStatusAsync() / JoinChannelAsync() / etc.
        EHC->>Server: SignalR Invoke
    else Local command (theme, help, quit)
        CMD-->>AO: Fire OnThemeChanged / etc.
        AO->>UI: Apply locally (no server call)
    end

    AO->>UI: AddSystemMessage(result)
```

**Available commands:**

| Command | Type | Handler |
|---------|------|---------|
| `/status <status> [message]` | Hub | `UpdateStatusAsync` |
| `/nick <name>` | API | `UpdateProfileAsync` |
| `/color <hex>` | API | `UpdateProfileAsync` |
| `/join <channel>` | Hub | `JoinChannelAsync` |
| `/leave` | Hub | `LeaveChannelAsync` |
| `/topic <text>` | API | `UpdateChannelTopicAsync` |
| `/kick <user>` | API | `POST /api/moderation/kick/{user}` |
| `/ban <user>` | API | `POST /api/moderation/ban/{user}` |
| `/unban <user>` | API | `POST /api/moderation/unban/{user}` |
| `/mute <user> [mins]` | API | `POST /api/moderation/mute/{user}` |
| `/unmute <user>` | API | `POST /api/moderation/unmute/{user}` |
| `/role <user> <role>` | API | `PUT /api/moderation/role/{user}` |
| `/nuke` | API | `DELETE /api/channels/{channel}/messages` |
| `/send <file>` | API | `POST /api/channels/{channel}/upload` |
| `/profile [user]` | Local | Show profile dialog |
| `/avatar` | API | `POST /api/users/avatar` |
| `/theme <name>` | Local | `ThemeManager.SetTheme()` |
| `/servers` | API | `GET /api/serverdir/servers` |
| `/users` | Local | Show userlist |
| `/help` | Local | Show help text |
| `/quit` | Local | Exit application |
