// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
pub impl ToSockaddr for Sockaddr with ip_name(self : Sockaddr) -> Bytes raise Errno {
  let self = self.to_sockaddr()
  let bytes = Bytes::make(_IF_NAMESIZE, 0)
  let status = uv_ip_name(self, bytes)
  if status < 0 {
    raise Errno::of_int(status)
  }
  let buffer = @buffer.new()
  for i = 0; bytes[i] != 0; i = i + 1 {
    buffer.write_byte(bytes[i])
  }
  buffer.contents()
}

///|
#borrow(ip, addr)
extern "c" fn uv_ip4_addr(ip : Bytes, port : Int, addr : SockaddrIn) -> Int = "moonbit_uv_ip4_addr"

///|
/// Creates an IPv4 socket address structure from an IP address and port number.
///
/// Parameters:
///
/// * `ip` : The IPv4 address as a null-terminated byte string (e.g.,
/// "127.0.0.1").
/// * `port` : The port number for the socket address.
///
/// Returns a `SockaddrIn` structure representing the IPv4 socket address that
/// can be used with TCP operations like binding or connecting.
///
/// Example:
///
/// ```moonbit nocheck
/// let addr = @uv.ip4_addr("127.0.0.1", 8080)
/// println(addr.ip_name())
/// ```
pub fn ip4_addr(ip : Bytes, port : Int) -> SockaddrIn raise Errno {
  let addr = uv_sockaddr_in_make()
  let status = uv_ip4_addr(ip, port, addr)
  if status < 0 {
    raise Errno::of_int(status)
  }
  addr
}

///|
#borrow(addr, dst)
extern "c" fn uv_ip4_name(addr : SockaddrIn, dst : Bytes) -> Int = "moonbit_uv_ip4_name"

///|
pub impl ToSockaddr for SockaddrIn with ip_name(self : SockaddrIn) -> Bytes raise Errno {
  let bytes = Bytes::make(_IF_NAMESIZE, 0)
  let status = uv_ip4_name(self, bytes)
  if status < 0 {
    raise Errno::of_int(status)
  }
  let buffer = @buffer.new()
  for i = 0; bytes[i] != 0; i = i + 1 {
    buffer.write_byte(bytes[i])
  }
  buffer.contents()
}

///|
#borrow(ip, addr)
extern "c" fn uv_ip6_addr(ip : Bytes, port : Int, addr : SockaddrIn6) -> Int = "moonbit_uv_ip6_addr"

///|
pub fn ip6_addr(ip : Bytes, port : Int) -> SockaddrIn6 raise Errno {
  let addr = uv_sockaddr_in6_make()
  let status = uv_ip6_addr(ip, port, addr)
  if status < 0 {
    raise Errno::of_int(status)
  }
  addr
}

///|
#borrow(addr, dst)
extern "c" fn uv_ip6_name(addr : SockaddrIn6, dst : Bytes) -> Int = "moonbit_uv_ip6_name"

///|
pub impl ToSockaddr for SockaddrIn6 with ip_name(self : SockaddrIn6) -> Bytes raise Errno {
  let bytes = Bytes::make(_IF_NAMESIZE, 0)
  let status = uv_ip6_name(self, bytes)
  if status < 0 {
    raise Errno::of_int(status)
  }
  let buffer = @buffer.new()
  for i = 0; bytes[i] != 0; i = i + 1 {
    buffer.write_byte(bytes[i])
  }
  buffer.contents()
}

///|
pub(all) enum Protocol {
  Tcp
  Udp
}

///|
pub fn Protocol::tcp() -> Protocol {
  return Tcp
}

///|
pub fn Protocol::udp() -> Protocol {
  return Udp
}

///|
extern "c" fn uv_IPPROTO_UDP() -> Int = "moonbit_uv_IPPROTO_UDP"

///|
let _IPPROTO_UDP : Int = uv_IPPROTO_UDP()

///|
extern "c" fn uv_IPPROTO_TCP() -> Int = "moonbit_uv_IPPROTO_TCP"

///|
let _IPPROTO_TCP : Int = uv_IPPROTO_TCP()

///|
extern "c" fn uv_IPPROTO_IP() -> Int = "moonbit_uv_IPPROTO_IP"

///|
let _IPPROTO_IP : Int = uv_IPPROTO_IP()

///|
fn Protocol::of_int(self : Int) -> Protocol raise Errno {
  if self == _IPPROTO_TCP {
    return Tcp
  }
  if self == _IPPROTO_UDP {
    return Udp
  }
  raise EINVAL
}

///|
fn Protocol::to_int(self : Protocol) -> Int {
  match self {
    Tcp => _IPPROTO_TCP
    Udp => _IPPROTO_UDP
  }
}

///|
#borrow(src, dst)
extern "c" fn uv_ip_name(src : Sockaddr, dst : Bytes) -> Int = "moonbit_uv_ip_name"