use crate::{
    ActiveModelBehavior, ActiveModelTrait, ColumnTrait, Delete, DeleteMany, DeleteOne,
    FromQueryResult, Identity, Insert, InsertMany, ModelTrait, PrimaryKeyArity, PrimaryKeyToColumn,
    PrimaryKeyTrait, QueryFilter, Related, RelationBuilder, RelationTrait, RelationType, Select,
    Update, UpdateMany, UpdateOne, ValidatedDeleteOne,
};
use sea_query::{Iden, IntoIden, IntoTableRef, IntoValueTuple, TableRef};
use std::fmt::Debug;
pub use strum::IntoEnumIterator as Iterable;

/// An identifier (table or column name) that can be borrowed as a `&'static str`.
pub trait IdenStatic: Iden + Copy + Debug + 'static {
    /// The static SQL identifier this value represents.
    fn as_str(&self) -> &'static str;
}

/// Names the SQL table backing an [`Entity`](EntityTrait), plus the optional
/// schema and comment metadata.
///
/// One of the building blocks of [`EntityTrait`]; usually generated via
/// `#[derive(DeriveEntity)]` or `#[derive(DeriveEntityModel)]`.
pub trait EntityName: IdenStatic + Default {
    /// Schema name this table lives in (PostgreSQL / SQL Server). Returns
    /// `None` when the table lives in the connection's default schema.
    fn schema_name(&self) -> Option<&str> {
        None
    }

    /// SQL comment to attach to the table when it is created.
    fn comment(&self) -> Option<&str> {
        None
    }

    /// SQL name of the table.
    fn table_name(&self) -> &'static str;

    /// [`TableRef`] for this entity, qualified with [`schema_name`](Self::schema_name)
    /// if one is set.
    fn table_ref(&self) -> TableRef {
        match self.schema_name() {
            Some(schema) => (schema.to_owned(), self.into_iden()).into_table_ref(),
            None => self.into_table_ref(),
        }
    }
}

/// The contract every SeaORM entity implements — a static description of a
/// table plus the CRUD entry points used to query it.
///
/// Normally generated by `#[derive(DeriveEntityModel)]` or `#[sea_orm::model]`
/// from a [`Model`](ModelTrait) struct.
///
/// Each implementor exposes:
///
/// - **`Model`** / **`ActiveModel`** — the row struct and its editable
///   counterpart (see [`ModelTrait`], [`ActiveModelTrait`]).
/// - **`Column`** — strongly-typed enum of the table's columns
///   (see [`ColumnTrait`]).
/// - **`PrimaryKey`** — enum identifying the primary key column(s)
///   (see [`PrimaryKeyTrait`]).
/// - **`Relation`** — enum of relations to other entities
///   (see [`RelationTrait`]).
///
/// And entry points for queries: [`find`](Self::find), [`find_by_id`](Self::find_by_id),
/// [`insert`](Self::insert), [`insert_many`](Self::insert_many),
/// [`update`](Self::update), [`update_many`](Self::update_many),
/// [`delete`](Self::delete), [`delete_many`](Self::delete_many),
/// [`delete_by_id`](Self::delete_by_id).
pub trait EntityTrait: EntityName {
    #[allow(missing_docs)]
    type Model: ModelTrait<Entity = Self> + FromQueryResult;

    #[allow(missing_docs)]
    type ModelEx: ModelTrait<Entity = Self>;

    #[allow(missing_docs)]
    type ActiveModel: ActiveModelBehavior<Entity = Self>;

    /// The "extended" ActiveModel produced by `#[derive(DeriveActiveModelEx)]`
    /// (or by `#[sea_orm::model]` in the 2.0 dense format) — same as
    /// `ActiveModel`, but with extra fields for `HasOne` / `HasMany`
    /// relations so the entire object graph can be saved in one call.
    ///
    /// **Note:** the ActiveModelEx API is still evolving — its shape may
    /// change in minor releases (e.g. 2.0 → 2.1), though never in patch
    /// releases.
    type ActiveModelEx: ActiveModelTrait<Entity = Self>;

    #[allow(missing_docs)]
    type Column: ColumnTrait;

    #[allow(missing_docs)]
    type Relation: RelationTrait;

    #[allow(missing_docs)]
    type PrimaryKey: PrimaryKeyTrait + PrimaryKeyToColumn<Column = Self::Column>;

    /// Start building a `belongs_to` relation: this table holds the foreign
    /// key pointing at `related`. Call `.from(...).to(...)` on the returned
    /// builder to specify the column mapping.
    fn belongs_to<R>(related: R) -> RelationBuilder<Self, R>
    where
        R: EntityTrait,
    {
        RelationBuilder::new(RelationType::HasOne, Self::default(), related, false)
    }

    /// Start building a `has_one` relation: `R` holds a foreign key pointing
    /// back at this entity. Requires `R: Related<Self>` so the column
    /// mapping can be inferred from the reverse `belongs_to`.
    fn has_one<R>(_: R) -> RelationBuilder<Self, R>
    where
        R: EntityTrait + Related<Self>,
    {
        RelationBuilder::from_rel(RelationType::HasOne, R::to().rev(), true)
    }

    /// Start building a `has_many` relation: `R` holds a foreign key
    /// pointing back at this entity. Requires `R: Related<Self>` so the
    /// column mapping can be inferred from the reverse `belongs_to`.
    fn has_many<R>(_: R) -> RelationBuilder<Self, R>
    where
        R: EntityTrait + Related<Self>,
    {
        RelationBuilder::from_rel(RelationType::HasMany, R::to().rev(), true)
    }

    /// Like [`has_many`](Self::has_many), but takes an explicit reverse
    /// [`RelationTrait`] instead of relying on `Related<Self>` — useful when
    /// the related entity does not (or cannot) implement `Related<Self>`.
    fn has_many_via<R, T>(_: R, rel: T) -> RelationBuilder<Self, R>
    where
        R: EntityTrait,
        T: RelationTrait,
    {
        RelationBuilder::from_rel(RelationType::HasMany, rel.def().rev(), true)
    }

    /// Construct select statement to find one / all models
    ///
    /// - To select columns, join tables and group by expressions, see [`QuerySelect`](crate::query::QuerySelect)
    /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
    /// - To apply order by expressions, see [`QueryOrder`](crate::query::QueryOrder)
    ///
    /// # Example
    ///
    /// ```
    /// # use sea_orm::{error::*, tests_cfg::*, *};
    /// #
    /// # #[cfg(feature = "mock")]
    /// # pub fn main() -> Result<(), DbErr> {
    /// #
    /// # let db = MockDatabase::new(DbBackend::Postgres)
    /// #     .append_query_results([
    /// #         vec![
    /// #             cake::Model {
    /// #                 id: 1,
    /// #                 name: "New York Cheese".to_owned(),
    /// #             },
    /// #         ],
    /// #         vec![
    /// #             cake::Model {
    /// #                 id: 1,
    /// #                 name: "New York Cheese".to_owned(),
    /// #             },
    /// #             cake::Model {
    /// #                 id: 2,
    /// #                 name: "Chocolate Forest".to_owned(),
    /// #             },
    /// #         ],
    /// #     ])
    /// #     .into_connection();
    /// #
    /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
    ///
    /// assert_eq!(
    ///     cake::Entity::find().one(&db)?,
    ///     Some(cake::Model {
    ///         id: 1,
    ///         name: "New York Cheese".to_owned(),
    ///     })
    /// );
    ///
    /// assert_eq!(
    ///     cake::Entity::find().all(&db)?,
    ///     [
    ///         cake::Model {
    ///             id: 1,
    ///             name: "New York Cheese".to_owned(),
    ///         },
    ///         cake::Model {
    ///             id: 2,
    ///             name: "Chocolate Forest".to_owned(),
    ///         },
    ///     ]
    /// );
    ///
    /// assert_eq!(
    ///     db.into_transaction_log(),
    ///     [
    ///         Transaction::from_sql_and_values(
    ///             DbBackend::Postgres,
    ///             r#"SELECT "cake"."id", "cake"."name" FROM "cake" LIMIT $1"#,
    ///             [1u64.into()]
    ///         ),
    ///         Transaction::from_sql_and_values(
    ///             DbBackend::Postgres,
    ///             r#"SELECT "cake"."id", "cake"."name" FROM "cake""#,
    ///             []
    ///         ),
    ///     ]
    /// );
    /// #
    /// # Ok(())
    /// # }
    /// ```
    fn find() -> Select<Self> {
        Select::new()
    }

    /// Same as `find_related`, but using the other Entity's relation definition.
    /// Not stable.
    #[doc(hidden)]
    fn find_related_rev<R>() -> Select<R>
    where
        R: EntityTrait,
        R: Related<Self>,
    {
        use crate::{JoinType, QuerySelect};
        Select::<R>::new().join_join(JoinType::InnerJoin, R::to(), R::via())
    }

    /// Find a model by primary key
    ///
    /// # Example
    ///
    /// ```
    /// # use sea_orm::{error::*, tests_cfg::*, *};
    /// #
    /// # #[cfg(feature = "mock")]
    /// # pub fn main() -> Result<(), DbErr> {
    /// #
    /// # let db = MockDatabase::new(DbBackend::Postgres)
    /// #     .append_query_results([
    /// #         [
    /// #             cake::Model {
    /// #                 id: 11,
    /// #                 name: "Sponge Cake".to_owned(),
    /// #             },
    /// #         ],
    /// #     ])
    /// #     .into_connection();
    /// #
    /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
    ///
    /// assert_eq!(
    ///     cake::Entity::find_by_id(11).all(&db)?,
    ///     [cake::Model {
    ///         id: 11,
    ///         name: "Sponge Cake".to_owned(),
    ///     }]
    /// );
    ///
    /// assert_eq!(
    ///     db.into_transaction_log(),
    ///     [Transaction::from_sql_and_values(
    ///         DbBackend::Postgres,
    ///         r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "cake"."id" = $1"#,
    ///         [11i32.into()]
    ///     )]
    /// );
    /// #
    /// # Ok(())
    /// # }
    /// ```
    /// Find by composite key
    /// ```
    /// # use sea_orm::{error::*, tests_cfg::*, *};
    /// #
    /// # #[cfg(feature = "mock")]
    /// # pub fn main() -> Result<(), DbErr> {
    /// #
    /// # let db = MockDatabase::new(DbBackend::Postgres)
    /// #     .append_query_results([
    /// #         [
    /// #             cake_filling::Model {
    /// #                 cake_id: 2,
    /// #                 filling_id: 3,
    /// #             },
    /// #         ],
    /// #     ])
    /// #     .into_connection();
    /// #
    /// use sea_orm::{entity::*, query::*, tests_cfg::cake_filling};
    ///
    /// assert_eq!(
    ///     cake_filling::Entity::find_by_id((2, 3)).all(&db)?,
    ///     [cake_filling::Model {
    ///         cake_id: 2,
    ///         filling_id: 3,
    ///     }]
    /// );
    ///
    /// assert_eq!(
    ///     db.into_transaction_log(),
    ///     [Transaction::from_sql_and_values(
    ///         DbBackend::Postgres,
    ///         [
    ///             r#"SELECT "cake_filling"."cake_id", "cake_filling"."filling_id" FROM "cake_filling""#,
    ///             r#"WHERE "cake_filling"."cake_id" = $1 AND "cake_filling"."filling_id" = $2"#,
    ///         ].join(" ").as_str(),
    ///         [2i32.into(), 3i32.into()]
    ///     )]);
    /// #
    /// # Ok(())
    /// # }
    /// ```
    fn find_by_id<T>(values: T) -> Select<Self>
    where
        T: Into<<Self::PrimaryKey as PrimaryKeyTrait>::ValueType>,
    {
        let mut select = Self::find();
        let mut keys = Self::PrimaryKey::iter();
        for v in values.into().into_value_tuple() {
            if let Some(key) = keys.next() {
                let col = key.into_column();
                select = select.filter(col.eq(v));
            } else {
                unreachable!("primary key arity mismatch");
            }
        }
        select
    }

    /// Get primary key as Identity
    fn primary_key_identity() -> Identity {
        let mut cols = Self::PrimaryKey::iter();
        macro_rules! next {
            () => {
                cols.next()
                    .expect("Already checked arity")
                    .into_column()
                    .as_column_ref()
                    .1
            };
        }
        match <<Self::PrimaryKey as PrimaryKeyTrait>::ValueType as PrimaryKeyArity>::ARITY {
            1 => {
                let s1 = next!();
                Identity::Unary(s1)
            }
            2 => {
                let s1 = next!();
                let s2 = next!();
                Identity::Binary(s1, s2)
            }
            3 => {
                let s1 = next!();
                let s2 = next!();
                let s3 = next!();
                Identity::Ternary(s1, s2, s3)
            }
            len => {
                let mut vec = Vec::with_capacity(len);
                for _ in 0..len {
                    let s = next!();
                    vec.push(s);
                }
                Identity::Many(vec)
            }
        }
    }

    /// Insert a model into database
    ///
    /// # Example (Postgres)
    ///
    /// ```
    /// # use sea_orm::{error::*, tests_cfg::*, *};
    /// #
    /// # #[cfg(feature = "mock")]
    /// # pub fn main() -> Result<(), DbErr> {
    /// #
    /// # let db = MockDatabase::new(DbBackend::Postgres)
    /// #     .append_query_results([[maplit::btreemap! {
    /// #         "id" => Into::<Value>::into(15),
    /// #     }]])
    /// #     .into_connection();
    /// #
    /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
    ///
    /// let apple = cake::ActiveModel {
    ///     name: Set("Apple Pie".to_owned()),
    ///     ..Default::default()
    /// };
    ///
    /// let insert_result = cake::Entity::insert(apple).exec(&db)?;
    ///
    /// assert_eq!(dbg!(insert_result.last_insert_id), 15);
    ///
    /// assert_eq!(
    ///     db.into_transaction_log(),
    ///     [Transaction::from_sql_and_values(
    ///         DbBackend::Postgres,
    ///         r#"INSERT INTO "cake" ("name") VALUES ($1) RETURNING "id""#,
    ///         ["Apple Pie".into()]
    ///     )]
    /// );
    /// #
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// # Example (MySQL)
    ///
    /// ```
    /// # use sea_orm::{error::*, tests_cfg::*, *};
    /// #
    /// # #[cfg(feature = "mock")]
    /// # pub fn main() -> Result<(), DbErr> {
    /// #
    /// # let db = MockDatabase::new(DbBackend::MySql)
    /// #     .append_exec_results([
    /// #         MockExecResult {
    /// #             last_insert_id: 15,
    /// #             rows_affected: 1,
    /// #         },
    /// #     ])
    /// #     .into_connection();
    /// #
    /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
    ///
    /// let apple = cake::ActiveModel {
    ///     name: Set("Apple Pie".to_owned()),
    ///     ..Default::default()
    /// };
    ///
    /// let insert_result = cake::Entity::insert(apple).exec(&db)?;
    ///
    /// assert_eq!(insert_result.last_insert_id, 15);
    ///
    /// assert_eq!(
    ///     db.into_transaction_log(),
    ///     [Transaction::from_sql_and_values(
    ///         DbBackend::MySql,
    ///         r#"INSERT INTO `cake` (`name`) VALUES (?)"#,
    ///         ["Apple Pie".into()]
    ///     )]
    /// );
    /// #
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// To get back inserted Model
    ///
    /// ```
    /// # use sea_orm::{error::*, tests_cfg::*, *};
    /// #
    /// # #[cfg(feature = "mock")]
    /// # pub fn main() -> Result<(), DbErr> {
    /// #
    /// # let db = MockDatabase::new(DbBackend::Postgres)
    /// #     .append_query_results([
    /// #         [cake::Model {
    /// #             id: 1,
    /// #             name: "Apple Pie".to_owned(),
    /// #         }],
    /// #     ])
    /// #     .into_connection();
    /// #
    /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
    ///
    /// assert_eq!(
    ///     cake::Entity::insert(cake::ActiveModel {
    ///         id: NotSet,
    ///         name: Set("Apple Pie".to_owned()),
    ///     })
    ///     .exec_with_returning(&db)?,
    ///     cake::Model {
    ///         id: 1,
    ///         name: "Apple Pie".to_owned(),
    ///     }
    /// );
    ///
    /// assert_eq!(
    ///     db.into_transaction_log()[0].statements()[0].sql,
    ///     r#"INSERT INTO "cake" ("name") VALUES ($1) RETURNING "id", "name""#
    /// );
    /// #
    /// # Ok(())
    /// # }
    /// ```
    fn insert<A>(model: A) -> Insert<A>
    where
        A: ActiveModelTrait<Entity = Self>,
    {
        Insert::one(model)
    }

    /// Insert many models into database
    ///
    /// # Example (Postgres)
    ///
    /// ```
    /// # use sea_orm::{error::*, tests_cfg::*, *};
    /// #
    /// # #[cfg(feature = "mock")]
    /// # pub fn main() -> Result<(), DbErr> {
    /// #
    /// # let db = MockDatabase::new(DbBackend::Postgres)
    /// #     .append_query_results([[maplit::btreemap! {
    /// #         "id" => Into::<Value>::into(28),
    /// #     }]])
    /// #     .into_connection();
    /// #
    /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
    ///
    /// let apple = cake::ActiveModel {
    ///     name: Set("Apple Pie".to_owned()),
    ///     ..Default::default()
    /// };
    /// let orange = cake::ActiveModel {
    ///     name: Set("Orange Scone".to_owned()),
    ///     ..Default::default()
    /// };
    ///
    /// let insert_result = cake::Entity::insert_many::<cake::ActiveModel, _>([]).exec(&db)?;
    ///
    /// assert_eq!(insert_result.last_insert_id, None);
    ///
    /// let insert_result = cake::Entity::insert_many([apple, orange]).exec(&db)?;
    ///
    /// assert_eq!(insert_result.last_insert_id, Some(28));
    ///
    /// assert_eq!(
    ///     db.into_transaction_log(),
    ///     [Transaction::from_sql_and_values(
    ///         DbBackend::Postgres,
    ///         r#"INSERT INTO "cake" ("name") VALUES ($1), ($2) RETURNING "id""#,
    ///         ["Apple Pie".into(), "Orange Scone".into()]
    ///     )]
    /// );
    /// #
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// # Example (MySQL)
    ///
    /// ```
    /// # use sea_orm::{error::*, tests_cfg::*, *};
    /// #
    /// # #[cfg(feature = "mock")]
    /// # pub fn main() -> Result<(), DbErr> {
    /// #
    /// # let db = MockDatabase::new(DbBackend::MySql)
    /// #     .append_exec_results([
    /// #         MockExecResult {
    /// #             last_insert_id: 28,
    /// #             rows_affected: 2,
    /// #         },
    /// #     ])
    /// #     .into_connection();
    /// #
    /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
    ///
    /// let apple = cake::ActiveModel {
    ///     name: Set("Apple Pie".to_owned()),
    ///     ..Default::default()
    /// };
    /// let orange = cake::ActiveModel {
    ///     name: Set("Orange Scone".to_owned()),
    ///     ..Default::default()
    /// };
    ///
    /// let insert_result = cake::Entity::insert_many([apple, orange]).exec(&db)?;
    ///
    /// assert_eq!(insert_result.last_insert_id, Some(28));
    ///
    /// assert_eq!(
    ///     db.into_transaction_log(),
    ///     [Transaction::from_sql_and_values(
    ///         DbBackend::MySql,
    ///         r#"INSERT INTO `cake` (`name`) VALUES (?), (?)"#,
    ///         ["Apple Pie".into(), "Orange Scone".into()]
    ///     )]
    /// );
    /// #
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// Before 1.1.3, if the active models have different column set, this method would panic.
    /// Now, it'd attempt to fill in the missing columns with null
    /// (which may or may not be correct, depending on whether the column is nullable):
    ///
    /// ```
    /// use sea_orm::{
    ///     DbBackend,
    ///     entity::*,
    ///     query::*,
    ///     tests_cfg::{cake, cake_filling},
    /// };
    ///
    /// assert_eq!(
    ///     cake::Entity::insert_many([
    ///         cake::ActiveModel {
    ///             id: NotSet,
    ///             name: Set("Apple Pie".to_owned()),
    ///         },
    ///         cake::ActiveModel {
    ///             id: NotSet,
    ///             name: Set("Orange Scone".to_owned()),
    ///         }
    ///     ])
    ///     .build(DbBackend::Postgres)
    ///     .to_string(),
    ///     r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie'), ('Orange Scone')"#,
    /// );
    ///
    /// assert_eq!(
    ///     cake_filling::Entity::insert_many([
    ///         cake_filling::ActiveModel {
    ///             cake_id: ActiveValue::set(2),
    ///             filling_id: ActiveValue::NotSet,
    ///         },
    ///         cake_filling::ActiveModel {
    ///             cake_id: ActiveValue::NotSet,
    ///             filling_id: ActiveValue::set(3),
    ///         }
    ///     ])
    ///     .build(DbBackend::Postgres)
    ///     .to_string(),
    ///     r#"INSERT INTO "cake_filling" ("cake_id", "filling_id") VALUES (2, NULL), (NULL, 3)"#,
    /// );
    /// ```
    ///
    /// To get back inserted Models
    ///
    /// ```
    /// # use sea_orm::{error::*, tests_cfg::*, *};
    /// #
    /// # #[cfg(feature = "mock")]
    /// # pub fn main() -> Result<(), DbErr> {
    /// #
    /// # let db = MockDatabase::new(DbBackend::Postgres)
    /// #     .append_query_results([
    /// #         [cake::Model {
    /// #             id: 1,
    /// #             name: "Apple Pie".to_owned(),
    /// #         }, cake::Model {
    /// #             id: 2,
    /// #             name: "Choco Pie".to_owned(),
    /// #         }],
    /// #     ])
    /// #     .into_connection();
    /// #
    /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
    ///
    /// assert_eq!(
    ///     cake::Entity::insert_many([
    ///         cake::ActiveModel {
    ///             id: NotSet,
    ///             name: Set("Apple Pie".to_owned()),
    ///         },
    ///         cake::ActiveModel {
    ///             id: NotSet,
    ///             name: Set("Choco Pie".to_owned()),
    ///         },
    ///     ])
    ///     .exec_with_returning(&db)?,
    ///     [
    ///         cake::Model {
    ///             id: 1,
    ///             name: "Apple Pie".to_owned(),
    ///         },
    ///         cake::Model {
    ///             id: 2,
    ///             name: "Choco Pie".to_owned(),
    ///         }
    ///     ]
    /// );
    ///
    /// assert_eq!(
    ///     db.into_transaction_log()[0].statements()[0].sql,
    ///     r#"INSERT INTO "cake" ("name") VALUES ($1), ($2) RETURNING "id", "name""#
    /// );
    /// #
    /// # Ok(())
    /// # }
    /// ```
    fn insert_many<A, I>(models: I) -> InsertMany<A>
    where
        A: ActiveModelTrait<Entity = Self>,
        I: IntoIterator<Item = A>,
    {
        InsertMany::many(models)
    }

    /// Update a model in database
    ///
    /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
    ///
    /// # Example (Postgres)
    ///
    /// ```
    /// # use sea_orm::{error::*, tests_cfg::*, *};
    /// #
    /// # #[cfg(feature = "mock")]
    /// # pub fn main() -> Result<(), DbErr> {
    /// #
    /// # let db = MockDatabase::new(DbBackend::Postgres)
    /// #     .append_query_results([
    /// #         [fruit::Model {
    /// #             id: 1,
    /// #             name: "Orange".to_owned(),
    /// #             cake_id: None,
    /// #         }],
    /// #     ])
    /// #     .into_connection();
    /// #
    /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
    ///
    /// let orange = fruit::ActiveModel {
    ///     id: Set(1),
    ///     name: Set("Orange".to_owned()),
    ///     ..Default::default()
    /// };
    ///
    /// assert_eq!(
    ///     fruit::Entity::update(orange.clone())
    ///         .validate()?
    ///         .filter(fruit::Column::Name.contains("orange"))
    ///         .exec(&db)
    ///         ?,
    ///     fruit::Model {
    ///         id: 1,
    ///         name: "Orange".to_owned(),
    ///         cake_id: None,
    ///     }
    /// );
    ///
    /// assert_eq!(
    ///     db.into_transaction_log(),
    ///     [Transaction::from_sql_and_values(
    ///         DbBackend::Postgres,
    ///         r#"UPDATE "fruit" SET "name" = $1 WHERE "fruit"."id" = $2 AND "fruit"."name" LIKE $3 RETURNING "id", "name", "cake_id""#,
    ///         ["Orange".into(), 1i32.into(), "%orange%".into()]
    ///     )]);
    /// #
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// # Example (MySQL)
    ///
    /// ```
    /// # use sea_orm::{error::*, tests_cfg::*, *};
    /// #
    /// # #[cfg(feature = "mock")]
    /// # pub fn main() -> Result<(), DbErr> {
    /// #
    /// # let db = MockDatabase::new(DbBackend::MySql)
    /// #     .append_exec_results([
    /// #         MockExecResult {
    /// #             last_insert_id: 0,
    /// #             rows_affected: 1,
    /// #         },
    /// #     ])
    /// #     .append_query_results([
    /// #         [fruit::Model {
    /// #             id: 1,
    /// #             name: "Orange".to_owned(),
    /// #             cake_id: None,
    /// #         }],
    /// #     ])
    /// #     .into_connection();
    /// #
    /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
    ///
    /// let orange = fruit::ActiveModel {
    ///     id: Set(1),
    ///     name: Set("Orange".to_owned()),
    ///     ..Default::default()
    /// };
    ///
    /// assert_eq!(
    ///     fruit::Entity::update(orange.clone())
    ///         .validate()?
    ///         .filter(fruit::Column::Name.contains("orange"))
    ///         .exec(&db)
    ///         ?,
    ///     fruit::Model {
    ///         id: 1,
    ///         name: "Orange".to_owned(),
    ///         cake_id: None,
    ///     }
    /// );
    ///
    /// assert_eq!(
    ///     db.into_transaction_log(),
    ///     [
    ///         Transaction::from_sql_and_values(
    ///             DbBackend::MySql,
    ///             r#"UPDATE `fruit` SET `name` = ? WHERE `fruit`.`id` = ? AND `fruit`.`name` LIKE ?"#,
    ///             ["Orange".into(), 1i32.into(), "%orange%".into()]
    ///         ),
    ///         Transaction::from_sql_and_values(
    ///             DbBackend::MySql,
    ///             r#"SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit` WHERE `fruit`.`id` = ? LIMIT ?"#,
    ///             [1i32.into(), 1u64.into()]
    ///         )]);
    /// #
    /// # Ok(())
    /// # }
    /// ```
    fn update<A>(model: A) -> UpdateOne<A>
    where
        A: ActiveModelTrait<Entity = Self>,
    {
        Update::one(model)
    }

    /// Update many models in database
    ///
    /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
    ///
    /// # Example
    ///
    /// ```
    /// # use sea_orm::{error::*, tests_cfg::*, *};
    /// #
    /// # #[cfg(feature = "mock")]
    /// # pub fn main() -> Result<(), DbErr> {
    /// #
    /// # let db = MockDatabase::new(DbBackend::Postgres)
    /// #     .append_exec_results([
    /// #         MockExecResult {
    /// #             last_insert_id: 0,
    /// #             rows_affected: 5,
    /// #         },
    /// #     ])
    /// #     .into_connection();
    /// #
    /// use sea_orm::{
    ///     entity::*,
    ///     query::*,
    ///     sea_query::{Expr, Value},
    ///     tests_cfg::fruit,
    /// };
    ///
    /// let update_result = fruit::Entity::update_many()
    ///     .col_expr(fruit::Column::CakeId, Expr::value(Value::Int(None)))
    ///     .filter(fruit::Column::Name.contains("Apple"))
    ///     .exec(&db)?;
    ///
    /// assert_eq!(update_result.rows_affected, 5);
    ///
    /// assert_eq!(
    ///     db.into_transaction_log(),
    ///     [Transaction::from_sql_and_values(
    ///         DbBackend::Postgres,
    ///         r#"UPDATE "fruit" SET "cake_id" = $1 WHERE "fruit"."name" LIKE $2"#,
    ///         [Value::Int(None), "%Apple%".into()]
    ///     )]
    /// );
    /// #
    /// # Ok(())
    /// # }
    /// ```
    fn update_many() -> UpdateMany<Self> {
        Update::many(Self::default())
    }

    /// Delete a model from database
    ///
    /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
    ///
    /// # Example
    ///
    /// ```
    /// # use sea_orm::{error::*, tests_cfg::*, *};
    /// #
    /// # #[cfg(feature = "mock")]
    /// # pub fn main() -> Result<(), DbErr> {
    /// #
    /// # let db = MockDatabase::new(DbBackend::Postgres)
    /// #     .append_exec_results([
    /// #         MockExecResult {
    /// #             last_insert_id: 0,
    /// #             rows_affected: 1,
    /// #         },
    /// #     ])
    /// #     .into_connection();
    /// #
    /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
    ///
    /// let orange = fruit::ActiveModel {
    ///     id: Set(3),
    ///     ..Default::default()
    /// };
    ///
    /// let delete_result = fruit::Entity::delete(orange).exec(&db)?;
    ///
    /// assert_eq!(delete_result.rows_affected, 1);
    ///
    /// assert_eq!(
    ///     db.into_transaction_log(),
    ///     [Transaction::from_sql_and_values(
    ///         DbBackend::Postgres,
    ///         r#"DELETE FROM "fruit" WHERE "fruit"."id" = $1"#,
    ///         [3i32.into()]
    ///     )]
    /// );
    /// #
    /// # Ok(())
    /// # }
    /// ```
    fn delete<A>(model: A) -> DeleteOne<Self>
    where
        A: ActiveModelTrait<Entity = Self>,
    {
        Delete::one(model)
    }

    /// Delete many models from database
    ///
    /// - To apply where conditions / filters, see [`QueryFilter`](crate::query::QueryFilter)
    ///
    /// # Example
    ///
    /// ```
    /// # use sea_orm::{error::*, tests_cfg::*, *};
    /// #
    /// # #[cfg(feature = "mock")]
    /// # pub fn main() -> Result<(), DbErr> {
    /// #
    /// # let db = MockDatabase::new(DbBackend::Postgres)
    /// #     .append_exec_results([
    /// #         MockExecResult {
    /// #             last_insert_id: 0,
    /// #             rows_affected: 5,
    /// #         },
    /// #     ])
    /// #     .append_query_results([
    /// #         [cake::Model {
    /// #             id: 15,
    /// #             name: "Apple Pie".to_owned(),
    /// #         }],
    /// #     ])
    /// #     .into_connection();
    /// #
    /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
    ///
    /// let delete_result = fruit::Entity::delete_many()
    ///     .filter(fruit::Column::Name.contains("Apple"))
    ///     .exec(&db)?;
    ///
    /// assert_eq!(delete_result.rows_affected, 5);
    ///
    /// assert_eq!(
    ///     db.into_transaction_log(),
    ///     [Transaction::from_sql_and_values(
    ///         DbBackend::Postgres,
    ///         r#"DELETE FROM "fruit" WHERE "fruit"."name" LIKE $1"#,
    ///         ["%Apple%".into()]
    ///     )]
    /// );
    /// #
    /// # Ok(())
    /// # }
    /// ```
    fn delete_many() -> DeleteMany<Self> {
        Delete::many(Self::default())
    }

    /// Delete a model based on primary key
    ///
    /// # Example
    ///
    /// ```
    /// # use sea_orm::{error::*, tests_cfg::*, *};
    /// #
    /// # #[cfg(feature = "mock")]
    /// # pub fn main() -> Result<(), DbErr> {
    /// #
    /// # let db = MockDatabase::new(DbBackend::Postgres)
    /// #     .append_exec_results([
    /// #         MockExecResult {
    /// #             last_insert_id: 0,
    /// #             rows_affected: 1,
    /// #         },
    /// #     ])
    /// #     .into_connection();
    /// #
    /// use sea_orm::{entity::*, query::*, tests_cfg::fruit};
    ///
    /// let delete_result = fruit::Entity::delete_by_id(1).exec(&db)?;
    ///
    /// assert_eq!(delete_result.rows_affected, 1);
    ///
    /// assert_eq!(
    ///     db.into_transaction_log(),
    ///     [Transaction::from_sql_and_values(
    ///         DbBackend::Postgres,
    ///         r#"DELETE FROM "fruit" WHERE "fruit"."id" = $1"#,
    ///         [1i32.into()]
    ///     )]
    /// );
    /// #
    /// # Ok(())
    /// # }
    /// ```
    /// Delete by composite key
    /// ```
    /// # use sea_orm::{error::*, tests_cfg::*, *};
    /// #
    /// # #[cfg(feature = "mock")]
    /// # pub fn main() -> Result<(), DbErr> {
    ///
    /// # let db = MockDatabase::new(DbBackend::Postgres)
    /// #     .append_exec_results([
    /// #         MockExecResult {
    /// #             last_insert_id: 0,
    /// #             rows_affected: 1,
    /// #         },
    /// #     ])
    /// #     .into_connection();
    /// #
    /// use sea_orm::{entity::*, query::*, tests_cfg::cake_filling};
    ///
    /// let delete_result = cake_filling::Entity::delete_by_id((2, 3)).exec(&db)?;
    ///
    /// assert_eq!(delete_result.rows_affected, 1);
    ///
    /// assert_eq!(
    ///     db.into_transaction_log(),
    ///     [Transaction::from_sql_and_values(
    ///         DbBackend::Postgres,
    ///         r#"DELETE FROM "cake_filling" WHERE "cake_filling"."cake_id" = $1 AND "cake_filling"."filling_id" = $2"#,
    ///         [2i32.into(), 3i32.into()]
    ///     )]
    /// );
    /// #
    /// # Ok(())
    /// # }
    /// ```
    fn delete_by_id<T>(values: T) -> ValidatedDeleteOne<Self>
    where
        T: Into<<Self::PrimaryKey as PrimaryKeyTrait>::ValueType>,
    {
        let mut am = Self::ActiveModel::default();
        let mut keys = Self::PrimaryKey::iter();
        for v in values.into().into_value_tuple() {
            if let Some(key) = keys.next() {
                let col = key.into_column();
                am.set(col, v);
            } else {
                unreachable!("primary key arity mismatch");
            }
        }
        Delete::one(am).validate().expect("Must be valid")
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_delete_by_id_1() {
        use crate::tests_cfg::cake;
        use crate::{DbBackend, entity::*, query::*};
        assert_eq!(
            cake::Entity::delete_by_id(1)
                .build(DbBackend::Sqlite)
                .to_string(),
            r#"DELETE FROM "cake" WHERE "cake"."id" = 1"#,
        );
    }

    #[test]
    fn test_delete_by_id_2() {
        use crate::tests_cfg::cake_filling_price;
        use crate::{DbBackend, entity::*, query::*};
        assert_eq!(
            cake_filling_price::Entity::delete_by_id((1, 2))
                .build(DbBackend::Sqlite)
                .to_string(),
            r#"DELETE FROM "public"."cake_filling_price" WHERE "cake_filling_price"."cake_id" = 1 AND "cake_filling_price"."filling_id" = 2"#,
        );
    }

    #[test]
    #[cfg(feature = "macros")]
    fn entity_model_1() {
        use crate::entity::*;

        mod hello {
            use crate as sea_orm;
            use crate::entity::prelude::*;

            #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
            #[sea_orm(table_name = "hello")]
            pub struct Model {
                #[sea_orm(primary_key)]
                pub id: i32,
            }

            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
            pub enum Relation {}

            impl ActiveModelBehavior for ActiveModel {}
        }

        assert_eq!(hello::Entity.table_name(), "hello");
        assert_eq!(hello::Entity.schema_name(), None);
    }

    #[test]
    #[cfg(feature = "macros")]
    fn entity_model_2() {
        use crate::entity::*;

        mod hello {
            use crate as sea_orm;
            use crate::entity::prelude::*;

            #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
            #[sea_orm(table_name = "hello", schema_name = "world")]
            pub struct Model {
                #[sea_orm(primary_key)]
                pub id: i32,
            }

            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
            pub enum Relation {}

            impl ActiveModelBehavior for ActiveModel {}
        }

        assert_eq!(hello::Entity.table_name(), "hello");
        assert_eq!(hello::Entity.schema_name(), Some("world"));
    }

    #[test]
    #[cfg(feature = "macros")]
    fn entity_model_3() {
        use crate::{DbBackend, entity::*, query::*};
        use std::borrow::Cow;

        mod hello {
            use crate as sea_orm;
            use crate::entity::prelude::*;

            #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
            #[sea_orm(table_name = "hello", schema_name = "world")]
            pub struct Model {
                #[sea_orm(primary_key, auto_increment = false)]
                pub id: String,
            }

            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
            pub enum Relation {}

            impl ActiveModelBehavior for ActiveModel {}
        }

        fn delete_by_id<T>(value: T)
        where
            T: Into<<<hello::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::ValueType>,
        {
            assert_eq!(
                hello::Entity::delete_by_id(value)
                    .build(DbBackend::Sqlite)
                    .to_string(),
                r#"DELETE FROM "world"."hello" WHERE "hello"."id" = 'UUID'"#
            );
        }

        delete_by_id("UUID".to_string());
        delete_by_id("UUID");
        delete_by_id(Cow::from("UUID"));
    }

    #[test]
    fn test_find_by_id() {
        use crate::tests_cfg::{cake, cake_filling};
        use crate::{DbBackend, EntityTrait, MockDatabase};

        let db = MockDatabase::new(DbBackend::MySql).into_connection();

        cake::Entity::find_by_id(1).all(&db).ok();
        cake_filling::Entity::find_by_id((2, 3)).all(&db).ok();

        // below does not compile:

        // cake::Entity::find_by_id((1, 2)).all(&db).ok();
        // cake_filling::Entity::find_by_id(1).all(&db).ok();
        // cake_filling::Entity::find_by_id((1, 2, 3))
        //     .all(&db)
        //
        //     .ok();
    }

    #[test]
    fn test_triangle() {
        mod triangle {
            use crate as sea_orm;
            use sea_orm::entity::prelude::*;
            use serde::{Deserialize, Serialize};

            #[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
            #[sea_orm(table_name = "triangle")]
            pub struct Model {
                #[sea_orm(primary_key)]
                pub id: i32,
                pub p1: Point,
                pub p2: Point,
                pub p3: Point,
            }

            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
            pub enum Relation {}

            impl ActiveModelBehavior for ActiveModel {}

            #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, FromJsonQueryResult)]
            pub struct Point {
                pub x: f64,
                pub y: f64,
            }
        }
        use triangle::{Model as Triangle, Point};

        impl Triangle {
            fn area(&self) -> f64 {
                let a = self.p1.distance_to(&self.p2);
                let b = self.p2.distance_to(&self.p3);
                let c = self.p3.distance_to(&self.p1);
                let s = (a + b + c) / 2.0;
                (s * (s - a) * (s - b) * (s - c)).sqrt()
            }
        }

        impl Point {
            fn distance_to(&self, p: &Point) -> f64 {
                let dx = self.x - p.x;
                let dy = self.y - p.y;
                (dx * dx + dy * dy).sqrt()
            }
        }

        assert!(
            (Triangle {
                id: 1,
                p1: Point { x: 0., y: 0. },
                p2: Point { x: 2., y: 0. },
                p3: Point { x: 0., y: 2. },
            }
            .area()
                - 2.)
                .abs()
                < 0.00000001
        );
    }
}
