//! [Path] and [PathBuf] utilities

use std::ffi::OsString;
use std::path::{Path, PathBuf};

#[cfg(unix)]
use std::os::unix::ffi::OsStringExt as _;
#[cfg(windows)]
compile_error!("PathBytes is not supported on Windows");

use sea_orm::sea_query::{ArrayType, Nullable, ValueType, ValueTypeErr};
use sea_orm::{ColIdx, ColumnType, DbErr, QueryResult, TryFromU64, TryGetError, TryGetable, Value};

type SeaOrmRepr = Vec<u8>;

/// Wrapper around [PathBuf] to store paths as bytes in SeaORM
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
#[repr(transparent)]
pub struct PathBytes(pub PathBuf);

impl From<PathBuf> for PathBytes {
	fn from(value: PathBuf) -> Self {
		Self(value)
	}
}

impl From<PathBytes> for PathBuf {
	fn from(value: PathBytes) -> Self {
		value.0
	}
}

impl AsRef<Path> for PathBytes {
	fn as_ref(&self) -> &Path {
		&self.0
	}
}

impl ValueType for PathBytes {
	fn try_from(v: Value) -> Result<Self, ValueTypeErr> {
		<SeaOrmRepr as ValueType>::try_from(v)
			.map(OsString::from_vec)
			.map(PathBuf::from)
			.map(Self)
	}

	fn type_name() -> String {
		core::any::type_name::<Self>().to_string()
	}

	fn array_type() -> ArrayType {
		SeaOrmRepr::array_type()
	}

	fn column_type() -> ColumnType {
		SeaOrmRepr::column_type()
	}
}

impl From<PathBytes> for Value {
	fn from(value: PathBytes) -> Self {
		value.0.into_os_string().into_vec().into()
	}
}

impl TryGetable for PathBytes {
	fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
		SeaOrmRepr::try_get_by(res, index)
			.map(OsString::from_vec)
			.map(PathBuf::from)
			.map(Self)
	}
}

impl TryFromU64 for PathBytes {
	fn try_from_u64(n: u64) -> Result<Self, DbErr> {
		SeaOrmRepr::try_from_u64(n)
			.map(OsString::from_vec)
			.map(PathBuf::from)
			.map(Self)
	}
}

impl Nullable for PathBytes {
	fn null() -> Value {
		SeaOrmRepr::null()
	}
}

#[cfg(test)]
mod tests {
	use sea_orm::{
		ActiveModelBehavior, DeriveEntityModel, DerivePrimaryKey, DeriveRelation, EntityTrait,
		EnumIter, PrimaryKeyTrait,
	};

	use super::PathBytes;

	#[allow(dead_code)]
	#[derive(Debug, Clone, PartialEq, Eq, DeriveEntityModel)]
	#[sea_orm(table_name = "paths")]
	pub struct Model {
		#[sea_orm(primary_key, auto_increment = false)]
		id: u8,
		#[sea_orm(primary_key, auto_increment = false)]
		path: PathBytes,
		nullable: Option<PathBytes>,
	}

	impl ActiveModelBehavior for ActiveModel {}

	#[allow(dead_code)]
	#[derive(Debug, EnumIter, DeriveRelation)]
	pub enum Relation {}
}
