//! A single fact attached to a raw_contact: a phone, email, photo, etc.
//!
//! Field semantics depend on `mimetype`. Phase 1 stores them as a raw
//! `data1..data14` text array plus an optional BLOB for `data15`; the
//! mimetype-aware typed view (Phone/Email/Name/…) is built in `dbus` and
//! `carddav` on top of `DataRow`. See `docs/schema.md` for the per-mimetype
//! column mapping.

/// Number of `dataN` text columns (`data1` .. `data14`).
pub const TEXT_FIELDS: usize = 14;

#[ derive( Debug, Clone, PartialEq, Eq ) ]
pub struct DataRow
{
	pub id: i64,
	pub raw_contact_id: i64,
	pub mimetype: String,

	/// `data1` .. `data14`, indexed `[0]` .. `[13]`.
	pub fields: [ Option<String>; TEXT_FIELDS ],

	/// `data15` — used for `…/photo` blobs; otherwise `None`.
	pub blob: Option<Vec<u8>>,

	pub is_primary: bool,
	pub is_super_primary: bool,
	pub data_version: i64,

	/// `sync1` .. `sync4` — reserved for future sync engines, unused in v1.
	pub sync: [ Option<String>; 4 ],

	pub created_at: i64,
	pub updated_at: i64,
}

#[ derive( Debug, Clone ) ]
pub struct NewDataRow
{
	pub raw_contact_id: i64,
	pub mimetype: String,
	pub fields: [ Option<String>; TEXT_FIELDS ],
	pub blob: Option<Vec<u8>>,
	pub is_primary: bool,
	pub is_super_primary: bool,
}

impl NewDataRow
{
	pub fn new( raw_contact_id: i64, mimetype: impl Into<String> ) -> Self
	{
		Self
		{
			raw_contact_id,
			mimetype: mimetype.into(),
			fields: Default::default(),
			blob: None,
			is_primary: false,
			is_super_primary: false,
		}
	}

	/// Set `dataN` (1-based to match the schema).
	pub fn with_data( mut self, n: usize, value: impl Into<String> ) -> Self
	{
		assert!( ( 1..=TEXT_FIELDS ).contains( &n ), "data index must be 1..=14" );
		self.fields[ n - 1 ] = Some( value.into() );
		self
	}

	pub fn with_blob( mut self, bytes: Vec<u8> ) -> Self
	{
		self.blob = Some( bytes );
		self
	}
}

/// Partial update for an existing data row.
///
/// Each `fields[i]` slot is interpreted three ways:
/// - `None`               → leave column untouched.
/// - `Some( None )`       → set column to NULL.
/// - `Some( Some( v ) )`  → set column to `v`.
///
/// `blob` follows the same convention.
#[ derive( Debug, Clone, Default ) ]
pub struct DataPatch
{
	pub fields: [ Option<Option<String>>; TEXT_FIELDS ],
	pub blob: Option<Option<Vec<u8>>>,
	pub is_primary: Option<bool>,
	pub is_super_primary: Option<bool>,
}
