use chrono::prelude::Utc;
use diesel::pg::PgConnection;
use diesel::result::Error;
use diesel::Connection;
use diesel::ExpressionMethods;
use diesel::QueryDsl;
use diesel::RunQueryDsl;
use std::env;
use std::vec::Vec;

fn establish_connection() -> PgConnection {
    let database_url = env::var("DATABASE_URL").expect("DATABASE_URL not setted");

    PgConnection::establish(&database_url).expect(&format!("Error connecting to {}", database_url))
}

pub mod posts {
    use crate::controllers::*;
    use crate::models::{NewPost, Post};

    pub const MAX_POSTS_PER_PAGE: u64 = 10;

    pub fn get_posts(page: Option<u64>) -> (Vec<Post>, i64) {
        use crate::schema::posts::dsl::*;

        let connection = establish_connection();
        let visible_posts = posts.order(created_at.desc());
        let visible_posts = match page {
            Some(number_page) => visible_posts
                .filter(published.eq(true))
                .limit(MAX_POSTS_PER_PAGE as i64)
                .offset((MAX_POSTS_PER_PAGE * (number_page - 1)) as i64)
                .load::<Post>(&connection)
                .expect("Error loading posts"),
            None => visible_posts
                .load::<Post>(&connection)
                .expect("Error loading posts"),
        };

        let number_of_posts: i64 = posts
            .filter(published.eq(true))
            .count()
            .get_result(&connection)
            .expect("Error counting the posts");

        (visible_posts, number_of_posts)
    }

    pub fn get_post(id_number: i32) -> Result<Post, Error> {
        use crate::schema::posts::dsl::*;

        let connection = establish_connection();
        let post = posts.find(id_number).first(&connection);

        post
    }

    pub fn add_post(new_post: &NewPost) -> Result<Post, Error> {
        use crate::schema::posts;

        let connection = establish_connection();
        diesel::insert_into(posts::table)
            .values(new_post)
            .get_result(&connection)
    }

    pub fn edit_post(updated_id: i32, new_post: &NewPost) -> Result<Post, Error> {
        use crate::schema::posts::dsl::*;
        let connection = establish_connection();

        let date = Utc::now().naive_utc();

        diesel::update(posts.filter(id.eq(updated_id)))
            .set((
                title.eq(&new_post.title),
                content.eq(&new_post.content),
                published.eq(&new_post.published),
                updated_at.eq(&date),
            ))
            .get_result(&connection)
    }
}