use sea_orm::sea_query::{IntoColumnRef, OnConflict};
use sea_orm::{ActiveModelTrait, EntityTrait, Insert, Iterable};

/// This trait add a method on [Insert] to allow for upsert behavior
pub trait UpsertTrait: private::Sealed {
	/// Set ON CONFLICT on primary key to update all other columns
	fn on_conflict_upsert(self) -> Self;
}

fn primary_key_iter<A: ActiveModelTrait>()
-> impl Iterator<Item = <A::Entity as EntityTrait>::PrimaryKey> {
	<A::Entity as EntityTrait>::PrimaryKey::iter()
}

fn column_iter<A: ActiveModelTrait>() -> impl Iterator<Item = <A::Entity as EntityTrait>::Column> {
	<<A as ActiveModelTrait>::Entity as EntityTrait>::Column::iter()
}

impl<A: ActiveModelTrait> UpsertTrait for Insert<A> {
	fn on_conflict_upsert(self) -> Self {
		self.on_conflict(
			OnConflict::columns(primary_key_iter::<A>())
				.update_columns(column_iter::<A>().filter(|col| {
					!primary_key_iter::<A>().any(|pk| pk.into_column_ref() == col.into_column_ref())
				}))
				.to_owned(),
		)
	}
}

mod private {
	pub trait Sealed {}
	impl<A: ::sea_orm::ActiveModelTrait> Sealed for ::sea_orm::Insert<A> {}
}
