Documentation
use std::net::{
	SocketAddr,
	SocketAddrV4,
	SocketAddrV6,
	AddrParseError
};

use liter::column::{
	Affinity,
	Column
};
use rusqlite::Result as SqlResult;
use rusqlite::types::{
	FromSql,
	FromSqlResult,
	FromSqlError,
	ToSql,
	ToSqlOutput,
	ValueRef
};
use serde::{Serialize, Deserialize};

const DUMMY_PREFIX: &str = "dummy_addr/";

#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum Address {
	V4UdpChaCha20(SocketAddrV4),
	V6UdpChaCha20(SocketAddrV6),
	Dummy(usize)
}

impl std::fmt::Display for Address {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		match &self {
			Self::V4UdpChaCha20(addr) =>
				write!(f, "IPv4-UDP-ChaCha20/{addr}"),
			Self::V6UdpChaCha20(addr) =>
				write!(f, "IPv6-UDP-ChaCha20/{addr}"),
			Self::Dummy(addr) => write!(f, "{DUMMY_PREFIX}{addr}")
		}
	}
}

impl std::str::FromStr for Address {
	type Err = AddrParseError;

	fn from_str(s: &str) -> Result<Self, Self::Err> {
		match SocketAddr::from_str(s) {
			Ok(SocketAddr::V4(addr)) => Ok(Self::V4UdpChaCha20(addr)),
			Ok(SocketAddr::V6(addr)) => Ok(Self::V6UdpChaCha20(addr)),
			Err(e) => Err(e)
		}
	}
}

/*	SQL */
impl Column for Address {
	const AFFINITY: Affinity = Affinity::Text;
}

impl FromSql for Address {
	fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
		let val_as_str = value.as_str()?;
		#[cfg(test)]
		if let Some(dummy_addr) =  val_as_str.strip_prefix(DUMMY_PREFIX) {
			return dummy_addr.parse()
				.map(Self::Dummy)
				.map_err(Into::into)
				.map_err(FromSqlError::Other)
		}

		val_as_str.parse()
			.map_err(Into::into)
			.map_err(FromSqlError::Other)
	}
}

impl ToSql for Address {
	fn to_sql(&self) -> SqlResult<ToSqlOutput<'_>> {
		let as_string = match &self {
			Self::V4UdpChaCha20(addr) => addr.to_string(),
			Self::V6UdpChaCha20(addr) => addr.to_string(),
			Self::Dummy(addr) => format!("{DUMMY_PREFIX}{addr}")
		};
		Ok(ToSqlOutput::Owned(as_string.into()))
	}
}

liter::types::impl_from_to_sql_2!(Address);