///|
/// SSE Event State enumeration
pub enum EventState {
  Connecting
  Open
  Closed
  Error
} derive(Debug)

///|
pub impl Show for EventState with fn to_string(self) {
  match self {
    EventState::Connecting => "Connecting"
    EventState::Open => "Open"
    EventState::Closed => "Closed"
    EventState::Error => "Error"
  }
}

///|
pub extend EventState with Show::{to_string, output}

///|
pub extend EventState with @debug.Debug::{to_repr}

///|
/// SSE Message structure
pub struct SseMessage {
  event_name : String
  data : String
  origin : String
  id : String?
}

///|
/// Connection configuration for SSE
pub struct ConnectionConfig {
  url : String
  with_credentials : Bool
  reconnect_interval : Int
  max_retries : Int
}

///|
/// Default connection configuration
pub fn default_connection_config(url : String) -> ConnectionConfig {
  { url, with_credentials: false, reconnect_interval: 3000, max_retries: 10, }
}

///|
/// Event handler type for SSE events
pub type EventHandler = (SseMessage) -> Unit

///|
/// Connection state handler type
pub type ConnectionStateHandler = (EventState) -> Unit

///|
/// EventSource ready state constants
pub const CONNECTING : Int = 0

///|
pub const OPEN : Int = 1

///|
pub const CLOSED : Int = 2

///|
/// Convert ready state to EventState
pub fn ready_state_to_event_state(state : Int) -> EventState {
  match state {
    0 => EventState::Connecting
    1 => EventState::Open
    2 => EventState::Closed
    _ => EventState::Error
  }
}

///|
/// Server-side SSE event structure
pub(all) struct SSEEvent {
  data : String
  event : String?
  id : String?
} derive(Debug, Eq)

///|
fn option_string_to_string(value : String?) -> String {
  match value {
    Some(value) => "Some(" + value + ")"
    None => "None"
  }
}

///|
pub impl Show for SSEEvent with fn to_string(self) {
  "{data: " +
  self.data +
  ", event: " +
  option_string_to_string(self.event) +
  ", id: " +
  option_string_to_string(self.id) +
  "}"
}

///|
pub extend SSEEvent with Show::{to_string, output}

///|
pub extend SSEEvent with Eq::{not_equal, equal}

///|
pub extend SSEEvent with @debug.Debug::{to_repr}

///|
pub fn SSEEvent::new(data : String, event : String, id : String?) -> SSEEvent {
  { data, event: Some(event), id, }
}

///|
pub fn SSEEvent::data_only(data : String) -> SSEEvent {
  { data, event: None, id: None, }
}

///|
pub fn format_event(event : SSEEvent) -> String {
  let mut result = ""
  match event.id {
    Some(id) => result = result + "id: " + id + "\n"
    None => ()
  }
  match event.event {
    Some(e) => result = result + "event: " + e + "\n"
    None => ()
  }
  let data_lines = event.data.split("\n")
  for line_view in data_lines {
    let line = line_view.to_owned()
    result = result + "data: " + line + "\n"
  }
  result + "\n"
}