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::production::Production;  
use common::models::used::Used;  
  
use crate::traits::database::{Database, DatabaseSub};  
use crate::traits::query::QuerySub;  
use crate::traits::standard::{Standard, StandardSub};  
  
impl Standard for Production {  
    fn table_name() -> String {  
        "production".to_string()  
    }  
}  
  
impl StandardSub for Production {  
    fn parent_table() -> String {  
        "produced_entry_id".to_string()  
    }  
}  
  
impl QuerySub<Production> for Production {  
    fn from_sqlite_row(row: SqliteRow, _parent_id: i64, pool: &Data<Pool<Sqlite>>) -> Production {  
        let production_id: i64 = row.get(0);  
  
        let uses = Used::get_by_parent_id(production_id, pool);  
  
        Production {  
            id: production_id,  
            date_created: row.get(1),  
            date_produced: row.get(2),  
            mhd: row.get(3),  
            uses  
        }  
    }  
  
    fn from_sqlite_row_small(row: SqliteRow, _parent_id: i64, _pool: &Data<Pool<Sqlite>>) -> Production {  
        Production {  
            id: row.get(0),  
            date_created: row.get(1),  
            date_produced: row.get(2),  
            mhd: row.get(3),  
            uses: vec![]  
        }  
    }  
}  
  
impl DatabaseSub<Production> for Production {  
  
}  
  
impl Database<Production> for Production {  
    fn insert(&self, pool: &Data<Pool<Sqlite>>) -> i64 {  
        // TODO: First check if there is enough from ingredient to produce  
  
        let statement = format!(  
            "INSERT INTO {} (date_created) VALUES ($1)",  
            Production::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);  
  
        let mut conn = block_on(pool.acquire()).unwrap();  
        let temp = block_on(  
            sqlx::query_with(  
                statement.as_str(),  
                args)  
                .execute(&mut *conn)  
        );  
  
        println!("Produced result:\n {:#?}", temp);  
  
        // TODO: Check if problematic with more users when more productions are created in a very very short timespan.  
        let production_id = temp.unwrap().last_insert_rowid();  
  
        for entry in &self.uses {  
            let mut entry = entry.clone();  
  
            entry.production_id = Some(production_id);  
            entry.order_pos_id = None;  
  
            entry.insert(pool);  
        }  
  
        production_id  
    }  
  
    fn update(&self, pool: &Data<Pool<Sqlite>>) {  
        let production = Production::get_by_id(self.id, pool).first().unwrap().clone();  
  
        let mut date_produced: Option<DateTime<chrono::Utc>> = self.date_produced;  
        let mut mhd: Option<DateTime<chrono::Utc>> = self.mhd;  
  
        if production.date_produced.is_some() {  
            date_produced = production.date_produced;  
        }  
  
        if production.mhd.is_some() {  
            mhd = production.mhd;  
        }  
  
        let statement = format!(  
            "UPDATE {} SET date_produced = $2, mhd = $3 WHERE id = $1",  
            Production::table_name(),  
        );  
  
        let mut args = SqliteArguments::default();  
        args.add(&self.id);  
        args.add(date_produced);  
        args.add(mhd);  
  
        println!("Production");  
  
        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>>) {  
        println!("Not allowed to delete!")  
    }  
  
    fn get_all(pool: &Data<Pool<Sqlite>>) -> Vec<Production> {  
        let mut conn = block_on(pool.acquire()).unwrap();  
        let res = block_on(  
            sqlx::query(format!("SELECT * FROM {}", Production::table_name()).as_str())  
                .fetch_all(&mut *conn)  
        );  
        conn.detach();  
  
        let mut productions: Vec<Production> = Vec::new();  
  
        match res {  
            Ok(records) => {  
                for record in records {  
                    productions.push(  
                        // TODO: Load parent_id  
                        //Production::from_sqlite_row(record, -1, pool)                        Production::from_sqlite_row_small(record, -1, pool)  
                    );  
                }  
            }  
            Err(err) => {  
                println!("Error while loading all production entries:");  
                println!("{:#?}", err);  
            }  
        }  
  
        productions  
    }  
  
    fn get_by_id(id: i64, pool: &Data<Pool<Sqlite>>) -> Vec<Production> {  
        let statement = format!(  
            "SELECT * FROM {} WHERE id = $1",  
            Production::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 productions: Vec<Production> = Vec::new();  
  
        match res {  
            Ok(records) => {  
                for record in records {  
                    productions.push(  
                        // TODO: Load parent_id  
                        Production::from_sqlite_row(record, -1, pool)  
                        //Production::from_sqlite_row_small(record, -1, pool)  
                    );  
                }  
            }  
            Err(err) => {  
                println!("Error while loading production with id: {}:", id);  
                println!("{:#?}", err);  
            }  
        }  
  
        productions  
    }  
  
    fn get_by_search(search_term: String, pool: &Data<Pool<Sqlite>>) -> Vec<Production> {  
        let statement = format!(  
            "SELECT * FROM {} WHERE description like \"%{}%\"",  
            Production::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 productions: Vec<Production> = Vec::new();  
  
        match res {  
            Ok(records) => {  
                for record in records {  
                    productions.push(  
                        // TODO: Load parent_id  
                        //Production::from_sqlite_row(record, -1, pool)                        Production::from_sqlite_row_small(record, -1, pool)  
                    );  
                }  
            }  
            Err(err) => {  
                println!("Error while loading variants with search term: {}:", search_term);  
                println!("{:#?}", err);  
            }  
        }  
  
        productions  
    }  
}