// What a server refuses, in the protocol's own vocabulary: every case is one of the errors the
// specification names, so a client sees the same numeric code whichever binding answered.

///|
/// A refused operation, named as the specification names it. The string is the server's own note
/// on what the caller did, empty when the name says everything.
pub(all) suberror Refused {
  TaskNotFound(String)
  TaskNotCancelable(String)
  PushNotificationNotSupported(String)
  UnsupportedOperation(String)
  ContentTypeNotSupported(String)
  InvalidAgentResponse(String)
  ExtendedAgentCardNotConfigured(String)
  ExtensionSupportRequired(String)
  VersionNotSupported(String)
  InvalidRequest(String)
  InvalidParams(String)
}

///|
/// The JSON-RPC error code of the refusal, as section 5.4 of the specification maps it.
pub fn Refused::code(self : Refused) -> Int32 {
  match self {
    TaskNotFound(_) => -32001
    TaskNotCancelable(_) => -32002
    PushNotificationNotSupported(_) => -32003
    UnsupportedOperation(_) => -32004
    ContentTypeNotSupported(_) => -32005
    InvalidAgentResponse(_) => -32006
    ExtendedAgentCardNotConfigured(_) => -32007
    ExtensionSupportRequired(_) => -32008
    VersionNotSupported(_) => -32009
    InvalidRequest(_) => @moonjrpc.invalid_request
    InvalidParams(_) => @moonjrpc.invalid_params
  }
}

///|
/// The machine-readable reason, which goes into the error details.
pub fn Refused::reason(self : Refused) -> String {
  match self {
    TaskNotFound(_) => "TASK_NOT_FOUND"
    TaskNotCancelable(_) => "TASK_NOT_CANCELABLE"
    PushNotificationNotSupported(_) => "PUSH_NOTIFICATION_NOT_SUPPORTED"
    UnsupportedOperation(_) => "UNSUPPORTED_OPERATION"
    ContentTypeNotSupported(_) => "CONTENT_TYPE_NOT_SUPPORTED"
    InvalidAgentResponse(_) => "INVALID_AGENT_RESPONSE"
    ExtendedAgentCardNotConfigured(_) => "EXTENDED_AGENT_CARD_NOT_CONFIGURED"
    ExtensionSupportRequired(_) => "EXTENSION_SUPPORT_REQUIRED"
    VersionNotSupported(_) => "VERSION_NOT_SUPPORTED"
    InvalidRequest(_) => "INVALID_REQUEST"
    InvalidParams(_) => "INVALID_PARAMS"
  }
}

///|
/// The one-line message a client shows a human, worded as the specification's error table words it.
pub fn Refused::mention(self : Refused) -> String {
  match self {
    TaskNotFound(_) => "Task not found"
    TaskNotCancelable(_) => "Task cannot be canceled"
    PushNotificationNotSupported(_) => "Push notifications are not supported"
    UnsupportedOperation(_) => "This operation is not supported"
    ContentTypeNotSupported(_) => "Content type is not supported"
    InvalidAgentResponse(_) => "The agent response is invalid"
    ExtendedAgentCardNotConfigured(_) => "No extended agent card is configured"
    ExtensionSupportRequired(_) => "A required extension was not declared"
    VersionNotSupported(_) => "This protocol version is not supported"
    InvalidRequest(_) => "Invalid request"
    InvalidParams(_) => "Invalid parameters"
  }
}

///|
/// What the caller did that the server is complaining about, when the refusal says.
pub fn Refused::detail(self : Refused) -> String? {
  let detail = match self {
    TaskNotFound(detail)
    | TaskNotCancelable(detail)
    | PushNotificationNotSupported(detail)
    | UnsupportedOperation(detail)
    | ContentTypeNotSupported(detail)
    | InvalidAgentResponse(detail)
    | ExtendedAgentCardNotConfigured(detail)
    | ExtensionSupportRequired(detail)
    | VersionNotSupported(detail)
    | InvalidRequest(detail)
    | InvalidParams(detail) => detail
  }
  if detail == "" {
    None
  } else {
    Some(detail)
  }
}

///|
/// The `google.rpc.ErrorInfo` object that travels in the error's `data`, as section 9.5 asks.
fn error_info(reason : String, detail : String?) -> Json {
  let metadata = object()
  match detail {
    Some(text) => metadata["detail"] = Json::string(text)
    None => ()
  }
  Json::object({
    "@type": Json::string("type.googleapis.com/google.rpc.ErrorInfo"),
    "reason": Json::string(reason),
    "domain": Json::string("a2a-protocol.org"),
    "metadata": Json::object(metadata),
  })
}

///|
/// The JSON-RPC failure that carries this refusal to the caller.
pub fn Refused::failure(self : Refused) -> @moonjrpc.Failure {
  {
    code: self.code(),
    message: self.mention(),
    data: Some(Json::array([error_info(self.reason(), self.detail())])),
  }
}

///|
/// The refusal as the error a JSON-RPC handler raises, so its code and details reach the caller.
pub fn Refused::as_failed(self : Refused) -> @moonjrpc.Failed {
  @moonjrpc.Failed(self.failure())
}

///|
/// Read a refusal off the wire, by the code the server gave it. A code this library does not know
/// becomes `InvalidAgentResponse`: the caller cannot recover from it either way, and naming it
/// after what it is beats inventing a case for it.
pub fn Refused::from_failure(failure : @moonjrpc.Failure) -> Refused {
  match failure.code {
    -32001 => TaskNotFound("")
    -32002 => TaskNotCancelable("")
    -32003 => PushNotificationNotSupported("")
    -32004 => UnsupportedOperation("")
    -32005 => ContentTypeNotSupported("")
    -32006 => InvalidAgentResponse("")
    -32007 => ExtendedAgentCardNotConfigured("")
    -32008 => ExtensionSupportRequired("")
    -32009 => VersionNotSupported("")
    -32600 => InvalidRequest("")
    -32602 => InvalidParams("")
    _ => InvalidAgentResponse(failure.message)
  }
}