///|
pub(all) enum ErrorCode {
  TaskNotFound
  TaskOffline
  InternalError
} derive(Debug, Eq)

///|
pub fn ErrorCode::to_wire(self : ErrorCode) -> String {
  match self {
    TaskNotFound => "server_not_found"
    TaskOffline => "server_offline"
    InternalError => "internal_error"
  }
}

///|
pub fn ErrorCode::from_wire(s : String) -> ErrorCode? {
  match s {
    "server_not_found" => Some(TaskNotFound)
    "server_offline" => Some(TaskOffline)
    "internal_error" => Some(InternalError)
    _ => None
  }
}

///|
pub(all) enum RegisterRejectReason {
  UuidAlreadyClaimedThisSession
  InvalidUuid
} derive(Eq, Debug)

///|
pub fn RegisterRejectReason::to_wire(self : RegisterRejectReason) -> String {
  match self {
    UuidAlreadyClaimedThisSession => "uuid_already_claimed_this_session"
    InvalidUuid => "invalid_uuid"
  }
}

///|
pub fn RegisterRejectReason::from_wire(s : String) -> RegisterRejectReason? {
  match s {
    "uuid_already_claimed_this_session" => Some(UuidAlreadyClaimedThisSession)
    "invalid_uuid" => Some(InvalidUuid)
    _ => None
  }
}

///|
pub(all) enum SysMessage {
  Register(task_uuid~ : @uuid.UUID, hostname~ : String, cwd~ : String)
  Registered(task_uuid~ : @uuid.UUID)
  RegisterRejected(reason~ : RegisterRejectReason)
  TaskExited(exit_code~ : Int)
  Connect(task_uuid~ : @uuid.UUID)
  Connected
  NewClient(client_id~ : String)
  ClientDisconnected(client_id~ : String)
  Error(ErrorCode)
  AuthError(message~ : String)
}

///|
/// Manual `Debug` impl because `@uuid.UUID` does not implement `Debug` and
/// the orphan rule prevents us from adding it from this package. We render
/// UUID fields as their canonical hex-string form via `Repr::string`, which
/// matches what `derive(Debug)` would produce if `UUID : Debug` were bridged
/// through its `Show` impl.
pub impl @debug.Debug for SysMessage with to_repr(self) {
  match self {
    Register(task_uuid~, hostname~, cwd~) =>
      @debug.Repr::ctor("Register", [
        (Some("task_uuid"), @debug.Repr::string(task_uuid.to_string())),
        (Some("hostname"), @debug.to_repr(hostname)),
        (Some("cwd"), @debug.to_repr(cwd)),
      ])
    Registered(task_uuid~) =>
      @debug.Repr::ctor("Registered", [
        (Some("task_uuid"), @debug.Repr::string(task_uuid.to_string())),
      ])
    RegisterRejected(reason~) =>
      @debug.Repr::ctor("RegisterRejected", [
        (Some("reason"), @debug.to_repr(reason)),
      ])
    TaskExited(exit_code~) =>
      @debug.Repr::ctor("TaskExited", [
        (Some("exit_code"), @debug.to_repr(exit_code)),
      ])
    Connect(task_uuid~) =>
      @debug.Repr::ctor("Connect", [
        (Some("task_uuid"), @debug.Repr::string(task_uuid.to_string())),
      ])
    Connected => @debug.Repr::ctor("Connected", [])
    NewClient(client_id~) =>
      @debug.Repr::ctor("NewClient", [
        (Some("client_id"), @debug.to_repr(client_id)),
      ])
    ClientDisconnected(client_id~) =>
      @debug.Repr::ctor("ClientDisconnected", [
        (Some("client_id"), @debug.to_repr(client_id)),
      ])
    Error(code) => @debug.Repr::ctor("Error", [(None, @debug.to_repr(code))])
    AuthError(message~) =>
      @debug.Repr::ctor("AuthError", [
        (Some("message"), @debug.to_repr(message)),
      ])
  }
}

///|
pub(all) enum TermMessage {
  Data(client_id~ : String, data~ : String)
  Exited(client_id~ : String)
} derive(Debug)

///|
pub(all) enum Message {
  Sys(SysMessage)
  Fs(FsMessage)
  Term(TermMessage)
  Codex(client_id~ : String, data~ : String)
} derive(Debug)

///|
pub(all) struct FileStat {
  file_type : Int
  size : Int64
  mtime : Int64
  ctime : Int64
} derive(Debug)

///|
pub(all) enum FsMessage {
  Stat(req_id~ : String, client_id~ : String, path~ : String)
  StatResult(req_id~ : String, client_id~ : String, stat~ : FileStat)
  Readdir(req_id~ : String, client_id~ : String, path~ : String)
  ReaddirResult(
    req_id~ : String,
    client_id~ : String,
    entries~ : Array[(String, Int)]
  )
  ReadFile(req_id~ : String, client_id~ : String, path~ : String)
  ReadFileResult(req_id~ : String, client_id~ : String, data~ : String)
  WriteFile(
    req_id~ : String,
    client_id~ : String,
    path~ : String,
    data~ : String,
    create~ : Bool,
    overwrite~ : Bool
  )
  Mkdir(req_id~ : String, client_id~ : String, path~ : String)
  FsDelete(
    req_id~ : String,
    client_id~ : String,
    path~ : String,
    recursive~ : Bool
  )
  FsRename(
    req_id~ : String,
    client_id~ : String,
    from~ : String,
    to~ : String,
    overwrite~ : Bool
  )
  FsOk(req_id~ : String, client_id~ : String)
  FsError(
    req_id~ : String,
    client_id~ : String,
    code~ : String,
    message~ : String
  )
} derive(Debug)