use crate::database;

use super::*;

use lmdb::{Database, Environment, RwTransaction, Transaction, Cursor, EnvironmentFlags};
use std::fs;
use std::path::Path;

pub trait DbConnect{
    fn clear_and_init_env(database_path:&str) -> std::result::Result<Environment, Box<dyn std::error::Error>>;

    fn save_nriso_to_win_dist_id<'a>(&self, nriso_to_win_dist_id: Box<dyn Fn(u32) -> u32 + 'a>, n : u32, street_sentinel : i32) -> std::result::Result<(), Box<dyn std::error::Error>>;
    fn save_nriso_to_ptl_dist_id<'a>(&self, nriso_to_ptl_dist_id: Box<dyn Fn(u32) -> u32 + 'a>, n : u32, street_sentinel : i32) -> std::result::Result<(), Box<dyn std::error::Error>>;
    
    fn save_priso_to_win_trc_dist_id<'a>(&self, priso_to_trc_dist_id: Box<dyn Fn(u32) -> u32 + 'a>, n: u32, street_sentinel : i32, recall_from_sentinel : i32) -> std::result::Result<(), Box<dyn std::error::Error>>;
    fn save_priso_to_ptl_trc_dist_id<'a>(&self, priso_to_trc_dist_id: Box<dyn Fn(u32) -> u32 + 'a>, n: u32, street_sentinel : i32, recall_from_sentinel : i32) -> std::result::Result<(), Box<dyn std::error::Error>>;

    fn query_win_dist_id_with_nriso(&self, nriso : u32, street_sentinel : i32) -> std::result::Result<u32, Box<dyn std::error::Error>>;
}

impl<T> DbConnect for Featurizer<T> 
    where T : Singleton + Deal
{

    fn clear_and_init_env(database_path: &str) -> std::result::Result<Environment, Box<dyn std::error::Error>>{
        let path = Path::new(database_path);
        if path.exists(){
            fs::remove_dir_all(path).map_err(|e| std::format!("{}+不能删除文件夹", e))?;
        }
        fs::create_dir_all(path).map_err(|e| std::format!("{}+{}", e, database_path))?;

        let n = T::NUM_STREET * (T::NUM_STREET-1) + T::NUM_STREET + (T::NUM_STREET-1);

        Ok(Environment::new()
        .set_max_dbs(n.try_into().unwrap())
        .set_map_size(1024*1024*1024)
        .set_flags(EnvironmentFlags::WRITE_MAP)
        .open(path).map_err(|e| std::format!("{}+不能建环境", e))?)
    }

    fn query_win_dist_id_with_nriso(&self, nriso : u32, street_sentinel : i32) -> std::result::Result<u32, Box<dyn std::error::Error>>{
        // let table_name = format!("{}_{}_nriso_to_winning_distribution_id", T::GAME_NAME, street_sentinel);
        // let query_sql = format!(
        //     "select win_dist_id from {} where nriso={}", table_name, nriso
        // );

        // let url = MYSQL_URL;
        // let mut conn = self.pool.get_conn().map_err(|e| format!("line:{}, err:{}\n 出错时的查询语句:{}\n",1, e, query_sql.clone()))?;
        // let selected: Vec<u32>  = conn.query(query_sql.clone()).map_err(|e| format!("line:{}, err:{}\n 出错时的查询语句:{}\n",2, e, query_sql.clone()))?;
        // return Ok(selected[0]);
        //////////////////////////////////////////
        //let pool;// = Pool::new(url)?;//.map_err(|e| format!("line:{}, err:{}\n 出错时的查询语句:{}\n",0, e, query_sql.clone()))?;
        //for i in 0..=2{
            /*
            if let Ok(pool) = Pool::new(url){
                let mut conn = pool.get_conn().map_err(|e| format!("line:{}, err:{}\n 出错时的查询语句:{}\n",1, e, query_sql.clone()))?;
    
                let selected: Vec<u32>  = conn.query(query_sql.clone()).map_err(|e| format!("line:{}, err:{}\n 出错时的查询语句:{}\n",2, e, query_sql.clone()))?;
                return Ok(selected[0]);
            }else{
                #[cfg(feature = "test_print")]
                println!("err:{} {}", i, query_sql.clone());
            } */
        //}
        
        //panic!("err");
        Ok(0)
    }

    fn save_nriso_to_win_dist_id<'a>(&self, nriso_to_win_dist_id: Box<dyn Fn(u32) -> u32 + 'a>, n : u32, street_sentinel : i32) -> std::result::Result<(), Box<dyn std::error::Error>>
    {

        let kvdb_name = std::format!("{}_{}_nriso_to_winning_distribution_id", T::GAME_NAME, street_sentinel);
        let mut txn = self.env.begin_rw_txn()?;

        if let Ok(kvdb) = self.env.open_db(Some(kvdb_name.as_str())){
            txn.clear_db(kvdb)?;
        };

        let kvdb = self.env.create_db(Some(kvdb_name.as_str()), lmdb::DatabaseFlags::INTEGER_KEY)?;

        for nriso in 0..n {
            let key = nriso.to_be_bytes();
            let value = nriso_to_win_dist_id(nriso).to_be_bytes();
            txn.put(kvdb, &key, &value, lmdb::WriteFlags::empty()).unwrap();
        }

        println!("什么情况");

        txn.commit()?;
        // txn.abort();
        Ok(())
    }


    fn save_nriso_to_ptl_dist_id<'a>(&self, nriso_to_ptl_dist_id: Box<dyn Fn(u32) -> u32 + 'a>, n : u32, street_sentinel : i32) -> std::result::Result<(), Box<dyn std::error::Error>> 
    {
        let kvdb_name = std::format!("{}_{}_nriso_to_potential_distribution_id", T::GAME_NAME, street_sentinel);
        let mut txn = self.env.begin_rw_txn()?;

        if let Ok(kvdb) = self.env.open_db(Some(kvdb_name.as_str())){
            txn.clear_db(kvdb)?;
        };

        let kvdb = self.env.create_db(Some(kvdb_name.as_str()), lmdb::DatabaseFlags::INTEGER_KEY)?;

        (0..n).map(|nriso| {
            let key = nriso.to_be_bytes();
            let value = nriso_to_ptl_dist_id(nriso).to_be_bytes();
            txn.put(kvdb, &key, &value, lmdb::WriteFlags::empty()).unwrap();
        });

        txn.commit()?;
        // txn.abort();
        Ok(())
    }

    fn save_priso_to_ptl_trc_dist_id<'a>(&self, priso_to_trc_dist_id: Box<dyn Fn(u32) -> u32 + 'a>, n: u32, street_sentinel : i32, recall_from_sentinel : i32) -> std::result::Result<(), Box<dyn std::error::Error>>
    {
        let kvdb_name = std::format!("{}_{}_priso_from_{}_potential_trace_distribution_id", T::GAME_NAME, street_sentinel, recall_from_sentinel);
        let mut txn = self.env.begin_rw_txn()?;

        if let Ok(kvdb) = self.env.open_db(Some(kvdb_name.as_str())){
            txn.clear_db(kvdb)?;
        };

        let kvdb = self.env.create_db(Some(kvdb_name.as_str()), lmdb::DatabaseFlags::INTEGER_KEY)?;

        (0..n).map(|priso| {
            let key = priso.to_be_bytes();
            let value = priso_to_trc_dist_id(priso).to_be_bytes();
            txn.put(kvdb, &key, &value, lmdb::WriteFlags::empty()).unwrap();
        });

        txn.commit()?;
        // txn.abort();
        Ok(())
    }

    fn save_priso_to_win_trc_dist_id<'a>(&self, priso_to_trc_dist_id: Box<dyn Fn(u32) -> u32 + 'a>, n: u32, street_sentinel : i32, recall_from_sentinel : i32) -> std::result::Result<(), Box<dyn std::error::Error>>
    {
        let kvdb_name = std::format!("{}_{}_priso_from_{}_winning_trace_distribution_id", T::GAME_NAME, street_sentinel, recall_from_sentinel);
        let mut txn = self.env.begin_rw_txn()?;

        if let Ok(kvdb) = self.env.open_db(Some(kvdb_name.as_str())){
            txn.clear_db(kvdb)?;
        };

        let kvdb = self.env.create_db(Some(kvdb_name.as_str()), lmdb::DatabaseFlags::INTEGER_KEY)?;

        (0..n).map(|priso| {
            let key = priso.to_be_bytes();
            let value = priso_to_trc_dist_id(priso).to_be_bytes();
            txn.put(kvdb, &key, &value, lmdb::WriteFlags::empty()).unwrap();
        });

        txn.commit()?;
        // txn.abort();
        Ok(())
    }
}