///|
/// Error type for invalid native server configuration values.
pub suberror NativeServeError {
  InvalidMaxConnections(Int)
  InvalidMaxRequestBodyBytes(Int)
  InvalidRequestBodyReadTimeoutMs(Int)
  InvalidWebSocketMaxMessageBytes(Int)
  InvalidWebSocketOutgoingQueueCapacity(Int)
  InvalidWebSocketReadTimeoutMs(Int)
} derive(Debug, ToJson)

///|
/// Policy for handling a full outbound WebSocket message queue.
pub(all) enum NativeWebSocketOverflowPolicy {
  DropOldest
  DropLatest
} derive(Debug, Eq)

///|
pub impl Show for NativeWebSocketOverflowPolicy with output(self, logger) {
  match self {
    DropOldest => logger.write_string("DropOldest")
    DropLatest => logger.write_string("DropLatest")
  }
}

///|
/// Configuration options for the native async HTTP server runtime.
pub struct NativeServeOptions {
  max_connections : Int?
  max_request_body_bytes : Int?
  request_body_read_timeout_ms : Int?
  websocket_outgoing_queue_capacity : Int?
  websocket_overflow_policy : NativeWebSocketOverflowPolicy?
  websocket_read_timeout_ms : Int?
  websocket_max_message_bytes : Int?

  fn new(
    max_connections? : Int,
    max_request_body_bytes? : Int,
    request_body_read_timeout_ms? : Int,
    websocket_outgoing_queue_capacity? : Int,
    websocket_overflow_policy? : NativeWebSocketOverflowPolicy,
    websocket_read_timeout_ms? : Int,
    websocket_max_message_bytes? : Int,
  ) -> NativeServeOptions
}

///|
/// `max_connections`, if present, bounds concurrent accepted connections.
/// `max_request_body_bytes`, if present, rejects oversized request bodies
/// with `413 Request Entity Too Large`.
/// `request_body_read_timeout_ms`, if present, rejects slow request bodies
/// with `408 Request Timeout`.
/// `websocket_max_message_bytes`, if present, closes websocket connections
/// with `1009 MessageTooBig` when an inbound message exceeds that many bytes.
/// `websocket_outgoing_queue_capacity`, if present, bounds buffered outbound
/// websocket messages per connection.
/// `websocket_overflow_policy`, if present, chooses whether a full outbound
/// websocket queue drops the oldest or latest message.
/// `websocket_read_timeout_ms`, if present, closes websocket connections
/// after that many milliseconds waiting for the next inbound message.
pub fn NativeServeOptions::new(
  max_connections? : Int,
  max_request_body_bytes? : Int,
  request_body_read_timeout_ms? : Int,
  websocket_outgoing_queue_capacity? : Int,
  websocket_overflow_policy? : NativeWebSocketOverflowPolicy,
  websocket_read_timeout_ms? : Int,
  websocket_max_message_bytes? : Int,
) -> NativeServeOptions {
  {
    max_connections,
    max_request_body_bytes,
    request_body_read_timeout_ms,
    websocket_outgoing_queue_capacity,
    websocket_overflow_policy,
    websocket_read_timeout_ms,
    websocket_max_message_bytes,
  }
}

///|
fn resolve_native_max_connections(
  options : NativeServeOptions,
) -> Int? raise Error {
  match options.max_connections {
    Some(max_connections) => {
      guard max_connections > 0 else {
        raise NativeServeError::InvalidMaxConnections(max_connections)
      }
      Some(max_connections)
    }
    None => None
  }
}

///|
fn resolve_native_max_request_body_bytes(
  options : NativeServeOptions,
) -> Int? raise Error {
  match options.max_request_body_bytes {
    Some(max_request_body_bytes) => {
      guard max_request_body_bytes >= 0 else {
        raise NativeServeError::InvalidMaxRequestBodyBytes(
          max_request_body_bytes,
        )
      }
      Some(max_request_body_bytes)
    }
    None => None
  }
}

///|
fn resolve_native_request_body_read_timeout_ms(
  options : NativeServeOptions,
) -> Int? raise Error {
  match options.request_body_read_timeout_ms {
    Some(request_body_read_timeout_ms) => {
      guard request_body_read_timeout_ms > 0 else {
        raise NativeServeError::InvalidRequestBodyReadTimeoutMs(
          request_body_read_timeout_ms,
        )
      }
      Some(request_body_read_timeout_ms)
    }
    None => None
  }
}

///|
fn resolve_native_websocket_max_message_bytes(
  options : NativeServeOptions,
) -> Int? raise Error {
  match options.websocket_max_message_bytes {
    Some(websocket_max_message_bytes) => {
      guard websocket_max_message_bytes >= 0 else {
        raise NativeServeError::InvalidWebSocketMaxMessageBytes(
          websocket_max_message_bytes,
        )
      }
      Some(websocket_max_message_bytes)
    }
    None => None
  }
}

///|
fn resolve_native_websocket_outgoing_queue_capacity(
  options : NativeServeOptions,
) -> Int? raise Error {
  match options.websocket_outgoing_queue_capacity {
    Some(websocket_outgoing_queue_capacity) => {
      guard websocket_outgoing_queue_capacity > 0 else {
        raise NativeServeError::InvalidWebSocketOutgoingQueueCapacity(
          websocket_outgoing_queue_capacity,
        )
      }
      Some(websocket_outgoing_queue_capacity)
    }
    None => None
  }
}

///|
fn resolve_native_websocket_overflow_policy(
  options : NativeServeOptions,
) -> NativeWebSocketOverflowPolicy {
  match options.websocket_overflow_policy {
    Some(policy) =>
      match policy {
        DropOldest => DropOldest
        DropLatest => DropLatest
      }
    None => DropOldest
  }
}

///|
fn resolve_native_websocket_read_timeout_ms(
  options : NativeServeOptions,
) -> Int? raise Error {
  match options.websocket_read_timeout_ms {
    Some(websocket_read_timeout_ms) => {
      guard websocket_read_timeout_ms > 0 else {
        raise NativeServeError::InvalidWebSocketReadTimeoutMs(
          websocket_read_timeout_ms,
        )
      }
      Some(websocket_read_timeout_ms)
    }
    None => None
  }
}