//! Bridge between the neutral [`crate::model::data_fields::FieldValue`] and
//! zvariant's [`OwnedValue`].
//!
//! This is the only place in the codebase that knows about both
//! representations; the rest of the dbus layer works in terms of `FieldValue`
//! (which the data_fields codec already type-checks against the mimetype's
//! keymap).

use std::collections::HashMap;

use thiserror::Error;
use zbus::zvariant::{ Array, OwnedValue, Value };

use crate::model::data_fields::FieldValue;

#[ derive( Debug, Error ) ]
pub enum FieldDecodeError
{
	#[ error( "unsupported D-Bus value type: {signature}" ) ]
	Unsupported
	{
		signature: String,
	},

	#[ error( "byte array contained a non-byte element" ) ]
	BadByteArray,
}

/// Encode a `FieldValue` as an `OwnedValue`. Infallible in practice — every
/// `FieldValue` variant has a direct zvariant representation.
pub fn field_value_to_owned( v: FieldValue ) -> Result<OwnedValue, zbus::zvariant::Error>
{
	let val = match v
	{
		FieldValue::Text( s )  => Value::from( s ),
		FieldValue::Int32( i ) => Value::from( i ),
		FieldValue::Bytes( b ) => Value::Array( Array::from( b ) ),
	};
	val.try_to_owned()
}

/// Decode an `OwnedValue` into a `FieldValue`. Strict on types: only `s`,
/// `i`, and `ay` are accepted. Other types return `Unsupported` so a client
/// sending the wrong type gets a clean `org.liberux.Contacts1.Error.InvalidArgument`.
pub fn owned_to_field_value( v: &OwnedValue ) -> Result<FieldValue, FieldDecodeError>
{
	let inner: &Value<'_> = v;
	match inner
	{
		Value::Str( s ) => Ok( FieldValue::Text( s.to_string() ) ),
		Value::I32( i ) => Ok( FieldValue::Int32( *i ) ),
		Value::Array( arr ) =>
		{
			let elt = arr.element_signature();
			if elt.as_str() != "y"
			{
				return Err( FieldDecodeError::Unsupported
				{
					signature: format!( "a{}", elt.as_str() ),
				} );
			}
			let mut bytes = Vec::with_capacity( arr.len() );
			for entry in arr.iter()
			{
				match entry
				{
					Value::U8( b ) => bytes.push( *b ),
					_              => return Err( FieldDecodeError::BadByteArray ),
				}
			}
			Ok( FieldValue::Bytes( bytes ) )
		}
		other => Err( FieldDecodeError::Unsupported
		{
			signature: other.value_signature().as_str().to_owned(),
		} ),
	}
}

/// Bulk encode a `HashMap<String, FieldValue>` into the wire dict
/// `HashMap<String, OwnedValue>` used by `a{sv}` arguments.
pub fn field_map_to_dict(
	map: HashMap<String, FieldValue>,
) -> Result<HashMap<String, OwnedValue>, zbus::zvariant::Error>
{
	let mut out = HashMap::with_capacity( map.len() );
	for ( k, v ) in map
	{
		out.insert( k, field_value_to_owned( v )? );
	}
	Ok( out )
}

/// Bulk decode the wire dict into a `HashMap<String, FieldValue>` ready for
/// the data_fields codec to validate against a mimetype keymap.
pub fn dict_to_field_map(
	dict: HashMap<String, OwnedValue>,
) -> Result<HashMap<String, FieldValue>, FieldDecodeError>
{
	let mut out = HashMap::with_capacity( dict.len() );
	for ( k, v ) in dict
	{
		out.insert( k, owned_to_field_value( &v )? );
	}
	Ok( out )
}

impl From<FieldDecodeError> for crate::dbus::error::Error
{
	fn from( e: FieldDecodeError ) -> Self
	{
		crate::dbus::error::Error::InvalidArgument( e.to_string() )
	}
}

impl From<zbus::zvariant::Error> for crate::dbus::error::Error
{
	fn from( e: zbus::zvariant::Error ) -> Self
	{
		crate::dbus::error::Error::Internal( format!( "zvariant: {e}" ) )
	}
}
