  
use actix_web::web::Data;  
use chrono::{Datelike, DateTime, Timelike, Utc};  
use futures::executor::block_on;  
use sqlx::{Arguments, Pool, Row, Sqlite};  
use sqlx::sqlite::{SqliteArguments, SqliteRow};  
  
use common::models::delivery::Delivery;  
use common::models::delivery_for::DeliveryFor;  
use common::models::delivery_item::DeliveryItem;  
use crate::impls::delivery_items_entry::DeliveryItemEntry;  
  
use crate::traits::database::{Database, DatabaseSub};  
use crate::traits::query::Query;  
use crate::traits::standard::Standard;  
  
impl Standard for Delivery {  
    fn table_name() -> String {  
        "delivery".to_string()  
    }  
}  
  
impl Query<Delivery> for Delivery {  
    fn from_sqlite_row(row: SqliteRow, pool: &Data<Pool<Sqlite>>) -> Delivery {  
        let delivery_for: Vec<DeliveryFor> = DeliveryFor::get_by_parent_id(row.get(0), pool);  
        let delivery_items: Vec<DeliveryItem> = DeliveryItem::get_by_parent_id(row.get(0), pool);  
  
        Delivery {  
            id: row.get(0),  
            description: row.get(3),  
            creation_date: row.get(1),  
            received_all_date: row.get(2),  
            delivery_for,  
            delivery_items  
        }  
    }  
  
    fn from_sqlite_row_small(row: SqliteRow, _pool: &Data<Pool<Sqlite>>) -> Delivery {  
        Delivery {  
            id: row.get(0),  
            description: row.get(3),  
            creation_date: row.get(1),  
            received_all_date: row.get(2),  
            delivery_for: vec![],  
            delivery_items: vec![]  
        }  
    }  
}  
  
impl Database<Delivery> for Delivery {  
    fn insert(&self, pool: &Data<Pool<Sqlite>>) -> i64 {  
        let statement = format!(  
            "INSERT INTO {} (creation_date, description) VALUES ($1, $2)",  
            Delivery::table_name(),  
        );  
  
        let mut date_time: DateTime<Utc> = Utc::now();  
        date_time = date_time.with_day(21).unwrap();  
        date_time = date_time.with_month(09).unwrap();  
        //date_time = date_time.with_hour(10).unwrap();  
  
        let mut args = SqliteArguments::default();  
        args.add(date_time);  
        args.add(&self.description);  
  
        let mut conn = block_on(pool.acquire()).unwrap();  
        let temp = block_on(  
            sqlx::query_with(  
                statement.as_str(),  
                args)  
                .execute(&mut *conn)  
        );  
  
        println!("Result:\n {:#?}", temp);  
  
        temp.unwrap().last_insert_rowid()  
    }  
  
    fn update(&self, pool: &Data<Pool<Sqlite>>) {  
        let statement = format!(  
            "UPDATE {} SET description = $2, received_all_date = $3 WHERE id = $1",  
            Delivery::table_name(),  
        );  
  
        let mut delivered = true;  
  
        for delivery_item in &self.delivery_items {  
            if delivery_item.total_amount_needed > delivery_item.received_amount {  
                delivered = false;  
            }  
        }  
  
        let mut date_time: DateTime<Utc> = Utc::now();  
        date_time = date_time.with_day(21).unwrap();  
        date_time = date_time.with_month(09).unwrap();  
        //date_time = date_time.with_hour(10).unwrap();  
  
        let date_time: Option<DateTime<Utc>> = match delivered {  
            true => {Some(date_time)}  
            false => {None}  
        };  
  
        let mut args = SqliteArguments::default();  
        args.add(&self.id);  
        args.add(&self.description);  
        args.add(date_time);  
  
        println!("Updating Delivery");  
  
        let mut conn = block_on(pool.acquire()).unwrap();  
        let temp = block_on(  
            sqlx::query_with(  
                statement.as_str(),  
                args)  
                .execute(&mut *conn)  
        );  
  
        println!("Result:\n {:#?}", temp);  
    }  
  
    fn delete(&self, pool: &Data<Pool<Sqlite>>) {  
        let received_amounts: f64 = self.delivery_items.iter().map(|x| x.received_amount).sum();  
  
        if received_amounts == 0.0 {  
            let mut conn = block_on(pool.acquire()).unwrap();  
  
            // Delete delivery for  
            let temp = block_on(  
                sqlx::query(  
                    format!("DELETE FROM {} WHERE delivery_id = {}",  
                            DeliveryFor::table_name(),  
                            self.id  
                    ).as_str()  
                ).execute(&mut *conn)  
            );  
  
            println!("Result:\n {:#?}", temp);  
  
            // Delete delivery item entry  
            for delivery_item in &self.delivery_items {  
                let temp = block_on(  
                    sqlx::query(  
                        format!("DELETE FROM {} WHERE delivery_item_id = {}",  
                                DeliveryItemEntry::table_name(),  
                                delivery_item.id  
                        ).as_str()  
                    ).execute(&mut *conn)  
                );  
  
                println!("Result:\n {:#?}", temp);  
            }  
  
            // Delete delivery item  
            let temp = block_on(  
                sqlx::query(  
                    format!("DELETE FROM {} WHERE delivery_id = {}",  
                            DeliveryItem::table_name(),  
                            self.id  
                    ).as_str()  
                ).execute(&mut *conn)  
            );  
  
            println!("Result:\n {:#?}", temp);  
  
            // Delete delivery  
            let temp = block_on(  
                sqlx::query(  
                    format!("DELETE FROM {} WHERE id = {}",  
                            Delivery::table_name(),  
                            self.id  
                    ).as_str()  
                ).execute(&mut *conn)  
            );  
  
            println!("Result:\n {:#?}", temp);  
        } else {  
            println!("Can not delete delivery, already something delivered.");  
        }  
    }  
  
    fn get_all(pool: &Data<Pool<Sqlite>>) -> Vec<Delivery> {  
        let mut conn = block_on(pool.acquire()).unwrap();  
        let res = block_on(  
            sqlx::query(format!("SELECT * FROM {}", Delivery::table_name()).as_str())  
                .fetch_all(&mut *conn)  
        );  
        conn.detach();  
  
        let mut deliveries: Vec<Delivery> = Vec::new();  
  
        match res {  
            Ok(records) => {  
                for record in records {  
                    deliveries.push(  
                        Delivery::from_sqlite_row_small(record, pool)  
                    );  
                }  
            }  
            Err(err) => {  
                println!("Error while loading all deliveries:");  
                println!("{:#?}", err);  
            }  
        }  
  
        deliveries  
    }  
  
    fn get_by_id(id: i64, pool: &Data<Pool<Sqlite>>) -> Vec<Delivery> {  
        let statement = format!(  
            "SELECT * FROM {} WHERE id = $1",  
            Delivery::table_name(),  
        );  
  
        let mut args = SqliteArguments::default();  
        args.add(id);  
  
        let mut conn = block_on(pool.acquire()).unwrap();  
        let res = block_on(  
            sqlx::query_with(  
                statement.as_str(),  
                args)  
                .fetch_all(&mut *conn)  
        );  
        conn.detach();  
  
        let mut deliveries: Vec<Delivery> = Vec::new();  
  
        match res {  
            Ok(records) => {  
                for record in records {  
                    deliveries.push(  
                        Delivery::from_sqlite_row(record, pool)  
                    );  
                }  
            }  
            Err(err) => {  
                println!("Error while loading deliveries with id: {}:", id);  
                println!("{:#?}", err);  
            }  
        }  
  
        deliveries  
    }  
  
    fn get_by_search(search_term: String, pool: &Data<Pool<Sqlite>>) -> Vec<Delivery> {  
        let statement = format!(  
            "SELECT * FROM {} WHERE creation_date like \"%{}%\"",  
            Delivery::table_name(),  
            search_term,  
        );  
  
        let mut conn = block_on(pool.acquire()).unwrap();  
        let res = block_on(  
            sqlx::query(  
                statement.as_str())  
                .fetch_all(&mut *conn)  
        );  
        conn.detach();  
  
        let mut deliveries: Vec<Delivery> = Vec::new();  
  
        match res {  
            Ok(records) => {  
                for record in records {  
                    deliveries.push(  
                        Delivery::from_sqlite_row_small(record, pool)  
                    );  
                }  
            }  
            Err(err) => {  
                println!("Error while loading deliveries with search term: {}:", search_term);  
                println!("{:#?}", err);  
            }  
        }  
  
        deliveries  
    }  
}