use ::entity::entities::book::Entity as Book;
use ::entity::entities::user::Entity as User;

//, book_author::Entity as Author, book_person::Entity as Person, book_place::Entity as Place, book_subject::Entity as Subject, book_time::Entity as Time, book_isbn::Entity as ISBN};
use ::entity::entities::{
    book, book_author, book_isbn, book_person, book_place, book_subject, book_time, user,
};
//use ::entity::entities::{prelude::*, *};
use sea_orm::*;
//use sea_query::Expr;

#[derive(Clone)]
pub struct BookAndMetaV2 {
    pub book: book::Model,
    pub authors: Vec<book_author::Model>,
    pub persons: Vec<book_person::Model>,
    pub places: Vec<book_place::Model>,
    pub subjects: Vec<book_subject::Model>,
    pub times: Vec<book_time::Model>,
    pub isbns: Vec<book_isbn::Model>,
}
//use ::entity::entities::prelude::Book;

pub struct Query;

impl Query {
    pub async fn find_book_by_id(db: &DbConn, id: i32) -> Result<Option<book::Model>, DbErr> {
        Book::find_by_id(id).one(db).await
    }

    pub async fn find_userid_by_name(
        db: &DbConn,
        name: String,
    ) -> Result<Option<user::Model>, DbErr> {
        User::find()
            .filter(user::Column::UserName.eq(name))
            .one(db)
            .await
    }

    pub async fn find_user_by_id(db: &DbConn, id: i32) -> Result<Option<user::Model>, DbErr> {
        User::find_by_id(id).one(db).await
    }

    pub async fn list_all_users(db: &DbConn) -> Result<Vec<user::Model>, DbErr> {
        User::find().all(db).await
    }
    /*  pub async fn find_bookplusmeta_by_id(db: &DbConn, id: i32) -> Result<Option<(book::Model, Vec<book_author::Model>, Vec<book_person::Model>)>, DbErr> {
           let book = Book::find_by_id(id).one(db).await?.unwrap();
           let authors = book.find_related(Author).all(db).await?;
           let persons = book.find_related(Person).all(db).await?;
           Ok(Some((book, authors, persons)))
       }
    */

    /// If ok, returns (post models, num pages).
    pub async fn find_books_in_page(
        db: &DbConn,
        page: u64,
        posts_per_page: u64,
        userid: i32,
        sort: String,
    ) -> Result<(Vec<book::Model>, u64), DbErr> {
        // Setup paginator
        if sort == "desc".to_string() {
            let paginator = Book::find()
                .filter(book::Column::UserId.eq(userid))
                .order_by_desc(book::Column::Id)
                .paginate(db, posts_per_page);
            let num_pages = paginator.num_pages().await?;
            return paginator.fetch_page(page - 1).await.map(|p| (p, num_pages));
        } else {
            let paginator = Book::find()
                .filter(book::Column::UserId.eq(userid))
                .order_by_asc(book::Column::Id)
                .paginate(db, posts_per_page);
            let num_pages = paginator.num_pages().await?;
            return paginator.fetch_page(page - 1).await.map(|p| (p, num_pages));
        }

        // Fetch paginated posts
    }

    pub async fn find_books_plus_meta_in_page(
        db: &DbConn,
        page: usize,
        posts_per_page: usize,
        userid: i32,
        sort: String,
    ) -> Result<(Vec<BookAndMetaV2>, u64), DbErr> {
        // Setup paginator
        let books = Self::find_books_in_page(
            db,
            page.try_into().unwrap(),
            posts_per_page.try_into().unwrap(),
            userid,
            sort,
        )
        .await?;
        let book_ids: Vec<i32> = books.0.clone().into_iter().map(|b| b.id).collect();
        println!("SIZE IS {} and {:?}", book_ids.len(), book_ids);
        let mut resbooks: Vec<BookAndMetaV2> = Vec::with_capacity(book_ids.len());
        for book in books.0.iter() {
            let bauthors: Vec<book_author::Model> =
                book.find_related(book_author::Entity).all(db).await?;
            let bpersons: Vec<book_person::Model> =
                book.find_related(book_person::Entity).all(db).await?;
            let bplaces: Vec<book_place::Model> =
                book.find_related(book_place::Entity).all(db).await?;
            let bsubjects: Vec<book_subject::Model> =
                book.find_related(book_subject::Entity).all(db).await?;
            let btimes: Vec<book_time::Model> =
                book.find_related(book_time::Entity).all(db).await?;
            let bisbns: Vec<book_isbn::Model> =
                book.find_related(book_isbn::Entity).all(db).await?;

            let bookandmeta = BookAndMetaV2 {
                book: (book.clone()),
                authors: bauthors,
                persons: bpersons,
                places: bplaces,
                subjects: bsubjects,
                times: btimes,
                isbns: bisbns,
            };
            resbooks.push(bookandmeta);
        }

        Ok((resbooks, books.1))
    }
}
