use super::*;

use std::{fs, ops::Deref};
use std::path::Path;

use rocksdb::{compaction_filter_factory, ColumnFamilyDescriptor, Options, WriteBatch, DB};
use serde::{Deserialize, Serialize};

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

    fn query_dist_id_with_xriso(
        &self,
        xriso_to_distribution_id_table_name: &str,
        xriso: u32,
    ) -> std::result::Result<u32, Box<dyn std::error::Error>>;
    fn save_xriso_to_dist_id<'a>(
        &self,
        xriso_to_distribution_id_table_name: &str,
        xriso_to_dist_id: Box<dyn Fn(u32) -> u32 + 'a>,
        n: u32,
        id_size: u32
    ) -> std::result::Result<(), Box<dyn std::error::Error>>;

    fn query_dist_with_id<D: serde::de::DeserializeOwned>(
        &self,
        id_to_distribution_table_name: &str,
        dist_id: u32,
    ) -> std::result::Result<D, Box<dyn std::error::Error>>;
    fn save_id_to_dist<D: serde::Serialize>(
        &self,
        id_to_distribution_table_name: &str,
        id_to_distribution: &Vec<D>,
    ) -> std::result::Result<(), 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<DB, 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 mut opts = Options::default();
        // 设置数据库如果不存在则创建
        opts.create_if_missing(true);
        // 设置数据库如果存在则打开
        // let cfs = DB::list_cf(&opts, path).unwrap(); // 列出所有的column family
        // 打开或创建数据库
        Ok(DB::open(&opts, path)?) // 打开数据库和column family
    }

    fn query_dist_id_with_xriso(
        &self,
        xriso_to_distribution_id_table_name: &str,
        xriso: u32,
    ) -> std::result::Result<u32, Box<dyn std::error::Error>> {
        let db = self.db.borrow();
        let cf = db
            .cf_handle(xriso_to_distribution_id_table_name)
            .ok_or(format!("打不开cf:{}", xriso_to_distribution_id_table_name))?;
        let dist_id_bytes = db
            .get_cf(cf, xriso.to_be_bytes())?
            .ok_or(format!("key:{}的值为None", xriso))?; //;//
        //println!("dist_id:{:?}", dist_id_bytes);
        assert!(dist_id_bytes.len() == 4);
        Ok(u32::from_be_bytes(dist_id_bytes.try_into().unwrap()))
    }

    fn save_xriso_to_dist_id<'a>(
        &self,
        xriso_to_distribution_id_table_name: &str,
        xriso_to_dist_id: Box<dyn Fn(u32) -> u32 + 'a>,
        n: u32,
        id_size: u32
    ) -> std::result::Result<(), Box<dyn std::error::Error>> {
        let mut tempkvs = (0..n).map(|nriso| xriso_to_dist_id(nriso)).collect_vec();

        let mut db = self.db.borrow_mut();

        if db.cf_handle(xriso_to_distribution_id_table_name).is_some() {
            db.drop_cf(xriso_to_distribution_id_table_name)?;
        }
        db.create_cf(xriso_to_distribution_id_table_name, &Options::default())?;

        let kvcf = db.cf_handle(xriso_to_distribution_id_table_name).unwrap();

        let mut batch = WriteBatch::default();
        for (nriso, value) in tempkvs.into_iter().enumerate() {
            let nriso = nriso as u32;
            batch.put_cf(kvcf, nriso.to_be_bytes(), value.to_be_bytes());
        }
        batch.put_cf(kvcf, n.to_be_bytes(), id_size.to_be_bytes());

        Ok(db.write(batch)?)
    }

    fn query_dist_with_id<D: serde::de::DeserializeOwned>(
        &self,
        id_to_distribution_table_name: &str,
        dist_id: u32,
    ) -> std::result::Result<D, Box<dyn std::error::Error>> {
        let db = self.db.borrow();
        let cf = db
            .cf_handle(id_to_distribution_table_name)
            .ok_or(format!("打不开cf:{}", id_to_distribution_table_name))?;
        let dist_bytes = db
            .get_cf(cf, dist_id.to_be_bytes())?
            .ok_or(format!("key:{}的值为None", dist_id))?;
        let dist: D = bincode::deserialize(&dist_bytes)?;
        Ok(dist)
    }

    fn save_id_to_dist<D: serde::Serialize>(
        &self,
        id_to_distribution_table_name: &str,
        id_to_distribution: &Vec<D>,
    ) -> std::result::Result<(), Box<dyn std::error::Error>> {
        let mut db = self.db.borrow_mut();

        if db.cf_handle(id_to_distribution_table_name).is_some() {
            db.drop_cf(id_to_distribution_table_name)?;
        }
        db.create_cf(id_to_distribution_table_name, &Options::default())?;

        let kvcf = db.cf_handle(id_to_distribution_table_name).unwrap();

        let mut batch = WriteBatch::default();
        for (dist_id, dist) in id_to_distribution.iter().enumerate() {
            let dist_id = dist_id as u32;
            let dist = bincode::serialize(&(*dist))?;
            batch.put_cf(kvcf, dist_id.to_be_bytes(), dist);
        }

        Ok(db.write(batch)?)
    }
}

// 为我生成两个泛型函数srlize<D>(d:D)、desrlize<D>(u:&[u8])要求：
// 1. d的类型可以是Vec也可能是Array两种容器，其中容器中包裹的类型可以被序列化成二进制字节
// 2. srlize函数使用bincode返回d的序列化字节
// 3. desrlize接受一个字节切片，通过bincode库将该字节序列返回成d:D
// 4. 生成一个测试程序将d:Vec<u32>通过srlize和desrlize再返回成d；将d:[f64; 5]通过srlize和desrlize再返回成d