syntax = "proto3";

option java_multiple_files = true;
option java_package = "com.dexorder.proto";

// User container event system for delivering notifications to users
// via active sessions or external channels (Telegram, email, push).
//
// Two ZMQ patterns:
// - XPUB/SUB (port 5570): Fast path for informational events to active sessions
// - DEALER/ROUTER (port 5571): Guaranteed delivery for critical events with ack
//
// See doc/protocol.md and doc/user_container_events.md for details.

// =============================================================================
// User Event (Container → Gateway)
// Message Type ID: 0x20
// =============================================================================

message UserEvent {
  // User ID this event belongs to
  string user_id = 1;

  // Unique event ID for deduplication and ack tracking (UUID)
  string event_id = 2;

  // Timestamp when event was generated (Unix milliseconds)
  int64 timestamp = 3;

  // Type of event
  EventType event_type = 4;

  // Event payload (JSON or nested protobuf, depending on event_type)
  bytes payload = 5;

  // Delivery specification (priority and channel preferences)
  DeliverySpec delivery = 6;
}

enum EventType {
  // Trading events
  ORDER_PLACED = 0;
  ORDER_FILLED = 1;
  ORDER_CANCELLED = 2;
  ORDER_REJECTED = 3;
  ORDER_EXPIRED = 4;

  // Alert events
  ALERT_TRIGGERED = 10;
  ALERT_CREATED = 11;
  ALERT_DELETED = 12;

  // Position events
  POSITION_OPENED = 20;
  POSITION_CLOSED = 21;
  POSITION_UPDATED = 22;
  POSITION_LIQUIDATED = 23;

  // Workspace/chart events
  WORKSPACE_CHANGED = 30;
  CHART_ANNOTATION_ADDED = 31;
  CHART_ANNOTATION_REMOVED = 32;
  INDICATOR_UPDATED = 33;

  // Strategy events
  STRATEGY_STARTED = 40;
  STRATEGY_STOPPED = 41;
  STRATEGY_LOG = 42;
  STRATEGY_ERROR = 43;
  BACKTEST_COMPLETED = 44;

  // System events
  CONTAINER_STARTING = 50;
  CONTAINER_READY = 51;
  CONTAINER_SHUTTING_DOWN = 52;
  EVENT_ERROR = 53;
}

// =============================================================================
// Delivery Specification
// =============================================================================

message DeliverySpec {
  // Priority determines routing behavior
  Priority priority = 1;

  // Ordered list of channel preferences (try first, then second, etc.)
  repeated ChannelPreference channels = 2;
}

enum Priority {
  // Drop if no active session (fire-and-forget via XPUB)
  // Use for: indicator updates, chart syncs, strategy logs when watching
  INFORMATIONAL = 0;

  // Best effort delivery - queue briefly, deliver when possible
  // Uses XPUB if subscribed, otherwise DEALER
  // Use for: alerts, position updates
  NORMAL = 1;

  // Must deliver - retry until acked, escalate channels
  // Always uses DEALER for guaranteed delivery
  // Use for: order fills, liquidations, critical errors
  CRITICAL = 2;
}

message ChannelPreference {
  // Channel to deliver to
  ChannelType channel = 1;

  // If true, skip this channel if user is not connected to it
  // If false, deliver even if user is not actively connected
  // (e.g., send Telegram message even if user isn't in Telegram chat)
  bool only_if_active = 2;
}

enum ChannelType {
  // Whatever channel the user currently has open (WebSocket, Telegram session)
  ACTIVE_SESSION = 0;

  // Specific channels
  WEB = 1;           // WebSocket to web UI
  TELEGRAM = 2;      // Telegram bot message
  EMAIL = 3;         // Email notification
  PUSH = 4;          // Mobile push notification (iOS/Android)
  DISCORD = 5;       // Discord webhook (future)
  SLACK = 6;         // Slack webhook (future)
}

// =============================================================================
// Event Acknowledgment (Gateway → Container)
// Message Type ID: 0x21
// =============================================================================

message EventAck {
  // Event ID being acknowledged
  string event_id = 1;

  // Delivery status
  AckStatus status = 2;

  // Error message if status is ERROR
  string error_message = 3;

  // Channel that successfully delivered (for logging/debugging)
  ChannelType delivered_via = 4;
}

enum AckStatus {
  // Successfully delivered to at least one channel
  DELIVERED = 0;

  // Accepted and queued for delivery (e.g., rate limited, will retry)
  QUEUED = 1;

  // Permanent failure - all channels failed
  ACK_ERROR = 2;
}

// =============================================================================
// Event Payloads
// These are JSON-encoded in the UserEvent.payload field.
// Defined here for documentation; actual encoding is JSON for flexibility.
// =============================================================================

// Payload for ORDER_PLACED, ORDER_FILLED, ORDER_CANCELLED, etc.
message OrderEventPayload {
  string order_id = 1;
  string symbol = 2;
  string side = 3;              // "buy" or "sell"
  string order_type = 4;        // "market", "limit", "stop_limit", etc.
  string quantity = 5;          // Decimal string
  string price = 6;             // Decimal string (for limit orders)
  string fill_price = 7;        // Decimal string (for fills)
  string fill_quantity = 8;     // Decimal string (for partial fills)
  string status = 9;            // "open", "filled", "cancelled", etc.
  string exchange = 10;
  int64 timestamp = 11;         // Unix milliseconds
  string strategy_id = 12;      // If order was placed by a strategy
  string error_message = 13;    // If rejected/failed
}

// Payload for ALERT_TRIGGERED
message AlertEventPayload {
  string alert_id = 1;
  string symbol = 2;
  string condition = 3;         // Human-readable condition (e.g., "BTC > 50000")
  string triggered_price = 4;   // Decimal string
  int64 timestamp = 5;
}

// Payload for POSITION_OPENED, POSITION_CLOSED, POSITION_UPDATED
message PositionEventPayload {
  string position_id = 1;
  string symbol = 2;
  string side = 3;              // "long" or "short"
  string size = 4;              // Decimal string
  string entry_price = 5;       // Decimal string
  string current_price = 6;     // Decimal string
  string unrealized_pnl = 7;    // Decimal string
  string realized_pnl = 8;      // Decimal string (for closed positions)
  string leverage = 9;          // Decimal string (for margin)
  string liquidation_price = 10;
  string exchange = 11;
  int64 timestamp = 12;
}

// Payload for WORKSPACE_CHANGED, CHART_ANNOTATION_*, INDICATOR_UPDATED
message WorkspaceEventPayload {
  string workspace_id = 1;
  string change_type = 2;       // "symbol_changed", "timeframe_changed", "annotation_added", etc.
  string symbol = 3;
  string timeframe = 4;

  // For annotations
  string annotation_id = 5;
  string annotation_type = 6;   // "trendline", "horizontal", "rectangle", "text", etc.
  string annotation_data = 7;   // JSON string with coordinates, style, etc.

  // For indicators
  string indicator_name = 8;
  string indicator_params = 9;  // JSON string with indicator parameters

  int64 timestamp = 10;
}

// Payload for STRATEGY_LOG, STRATEGY_ERROR
message StrategyEventPayload {
  string strategy_id = 1;
  string strategy_name = 2;
  string log_level = 3;         // "debug", "info", "warn", "error"
  string message = 4;
  string details = 5;           // JSON string with additional context
  int64 timestamp = 6;
}

// Payload for BACKTEST_COMPLETED
message BacktestEventPayload {
  string backtest_id = 1;
  string strategy_id = 2;
  string strategy_name = 3;
  string symbol = 4;
  string timeframe = 5;
  int64 start_time = 6;
  int64 end_time = 7;

  // Results summary
  int32 total_trades = 8;
  int32 winning_trades = 9;
  int32 losing_trades = 10;
  string total_pnl = 11;        // Decimal string
  string win_rate = 12;         // Decimal string (0-1)
  string sharpe_ratio = 13;     // Decimal string
  string max_drawdown = 14;     // Decimal string (0-1)

  string results_path = 15;     // Path to full results file
  int64 completed_at = 16;
}
