///|
/// A real SMTP transport over `moonbitlang/async/socket` (native only).
/// This file is compiled only for the native target; the js and wasm targets
/// use stubs that raise `Unsupported`.
pub struct SocketTransport {
  mut conn : @socket.Tcp?
}

///|
pub fn SocketTransport::new() -> SocketTransport {
  { conn: None }
}

///|
pub impl SmtpTransport for SocketTransport with fn connect(self, config) {
  let tcp = @socket.Tcp::connect_to_host(config.host, port=config.port) catch {
    e =>
      raise MailFailure::io(
        "socket connect to \{config.host}:\{config.port} failed: \{e}",
      )
  }
  self.conn = Some(tcp)
}

///|
pub impl SmtpTransport for SocketTransport with fn write_line(self, line) {
  match self.conn {
    Some(conn) =>
      conn.write(line + "\r\n") catch {
        e => raise MailFailure::io("socket write failed: \{e}")
      }
    None => raise MailFailure::transport("socket transport is not connected")
  }
}

///|
pub impl SmtpTransport for SocketTransport with fn read_line(self) {
  match self.conn {
    Some(conn) => {
      let line = conn.read_until("\n") catch {
        e => raise MailFailure::io("socket read failed: \{e}")
      }
      match line {
        Some(text) =>
          match text.strip_suffix("\r") {
            Some(view) => view.to_owned()
            None => text
          }
        None => raise MailFailure::io("connection closed by server")
      }
    }
    None => raise MailFailure::transport("socket transport is not connected")
  }
}

///|
pub impl SmtpTransport for SocketTransport with fn close(self) {
  match self.conn {
    Some(conn) => conn.close()
    None => ()
  }
  self.conn = None
}