use std::collections::HashMap;
use std::fs;
use json::{JsonValue, object};
use lazy_static::lazy_static;
use crate::imap::Imap;
use crate::pop3::Pop3;
use crate::smtp::Smtp;

#[cfg(any(feature = "default", feature = "pop"))]
mod pop3;
#[cfg(any(feature = "default", feature = "imaps"))]
mod imap;
#[cfg(any(feature = "default", feature = "smtp"))]
mod smtp;
pub mod analysis;

use std::sync::Mutex;

lazy_static! {
      pub static ref MAIL_DATA : Mutex<HashMap<String,Mail>> = Mutex::new(HashMap::new());
      pub static ref MAIL_CONN : Mutex<HashMap<String,Connection>> = Mutex::new(HashMap::new());
}
#[derive(Clone)]
pub enum Mail {
    #[cfg(any(feature = "pop"))]
    Pop3(Pop3),
    #[cfg(any(feature = "imaps"))]
    Imap(Imap),
    #[cfg(any(feature = "smtp"))]
    Smtp(Smtp),
    None,
}

impl Mail {
    pub fn load(path: &str) -> Self {
        let config = match fs::read_to_string(path)
        {
            Ok(e) => {
                match json::parse(&*e.clone()) {
                    Ok(e) => e,
                    Err(_) => {
                        let mut def = Config::default();
                        def.json()
                    }
                }
            }
            Err(_) => {
                let mut def = Config::default();
                def.json()
            }
        };
        Mail::new(config)
    }
    pub fn new(conf: JsonValue) -> Self {
        let config = Config::from(conf);
        for (key, value) in config.connections.clone() {
            MAIL_CONN.lock().unwrap().insert(key.clone(), value.clone());
        }
        if MAIL_DATA.lock().unwrap().get(&*config.default.clone()).is_none() {
            let mail = Mail::get_mode(config.connections.get(&*config.default.clone()).unwrap().clone());
            MAIL_DATA.lock().unwrap().insert(config.default.clone(), mail.clone());
        }
        MAIL_DATA.lock().unwrap().get(&*config.default.clone()).unwrap().clone()
    }
    fn get_mode(mut connection: Connection) -> Self {
        match connection.protocol.str() {
            #[cfg(any(feature = "pop"))]
            "pop3" => {
                Mail::Pop3(Pop3 {
                    connection: connection.clone()
                })
            }
            #[cfg(any(feature = "imaps"))]
            "imap" => {
                Mail::Imap(Imap {
                    connection,
                })
            }
            #[cfg(any(feature = "smtp"))]
            "smtp" => {
                Mail::Smtp(Smtp {
                    connection: connection.clone(),
                    from_name: connection.name.to_string(),
                    from_mail: connection.mail.to_string(),
                    to_name: "".to_string(),
                    to_mail: "".to_string(),
                    cc_name: "".to_string(),
                    cc_mail: "".to_string(),
                    body_text: "".to_string(),
                    body_html: "".to_string(),
                    subject: "".to_string(),
                    files: HashMap::new(),
                })
            }
            _ => Mail::None
        }
    }

    pub fn connection(&mut self, name: &str) -> Self {
        if MAIL_DATA.lock().unwrap().get(&*name.clone()).is_none() {
            let connection = MAIL_CONN.lock().unwrap().get(name.clone()).unwrap().clone();
            let mail = Mail::get_mode(connection.clone());
            MAIL_DATA.lock().unwrap().insert(name.to_string().clone(), mail.clone());
        }
        MAIL_DATA.lock().unwrap().get(&*name.clone()).unwrap().clone()
    }
    pub fn add_connection(&mut self, name: &str, connection: JsonValue) -> &mut Mail {
        let conn = Connection::from(connection);
        MAIL_CONN.lock().unwrap().insert(name.to_string(), conn).unwrap();
        self
    }
}

#[derive(Clone)]
pub struct Config {
    pub default: String,
    pub connections: HashMap<String, Connection>,
}

impl Config {
    pub fn default() -> Config {
        let mut connections = HashMap::new();
        connections.insert("myname".to_string(), Connection::default());
        Self {
            default: "myname".to_string(),
            connections,
        }
    }
    pub fn json(&mut self) -> JsonValue {
        let mut data = object! {};
        data["default"] = self.default.clone().into();
        let mut connections = object! {};
        for (name, connection) in self.connections.iter_mut() {
            connections[name.clone()] = connection.json().clone();
        }
        data["connections"] = connections;
        data
    }
    pub fn from(data: JsonValue) -> Config {
        let default = data["default"].to_string();
        let mut connections = HashMap::new();
        for (key, value) in data["connections"].entries() {
            let connection = Connection::from(value.clone()).clone();
            connections.insert(key.to_string(), connection.clone());
        }
        Self {
            default,
            connections,
        }
    }
}

impl Mode for Mail {
    /// 获取最大ID
    fn mail_id_max(&mut self) -> Result<u32, String> {
        match self {
            #[cfg(any(feature = "pop"))]
            Mail::Pop3(e) => e.mail_id_max(),
            #[cfg(any(feature = "imaps"))]
            Mail::Imap(e) => e.mail_id_max(),
            #[cfg(any(feature = "smtp"))]
            Mail::Smtp(_) => Err("无此功能".to_string()),
            Mail::None => Err("请求无效".to_string())
        }
    }
    fn mail_id_list(&mut self) -> Result<Vec<String>, String> {
        match self {
            #[cfg(any(feature = "pop"))]
            Mail::Pop3(e) => e.mail_id_list(),
            #[cfg(any(feature = "imaps"))]
            Mail::Imap(e) => e.mail_id_list(),
            #[cfg(any(feature = "smtp"))]
            Mail::Smtp(_) => Err("无此功能".to_string()),
            Mail::None => Err("请求无效".to_string())
        }
    }
    fn mail_id_get(&mut self, mail_id: u32) -> Result<Vec<u8>, String> {
        match self {
            #[cfg(any(feature = "pop"))]
            Mail::Pop3(e) => e.mail_id_get(mail_id),
            #[cfg(any(feature = "imaps"))]
            Mail::Imap(e) => e.mail_id_get(mail_id),
            #[cfg(any(feature = "smtp"))]
            Mail::Smtp(_) => Err("无此功能".to_string()),
            Mail::None => Err("请求无效".to_string())
        }
    }

    fn mail_msg_size(&mut self, mail_id: u32) -> Result<u32, String> {
        match self {
            #[cfg(any(feature = "pop"))]
            Mail::Pop3(e) => e.mail_msg_size(mail_id),
            #[cfg(any(feature = "imaps"))]
            Mail::Imap(e) => e.mail_msg_size(mail_id),
            #[cfg(any(feature = "smtp"))]
            Mail::Smtp(_) => Err("无此功能".to_string()),
            Mail::None => Err("请求无效".to_string())
        }
    }

    fn send(&mut self) -> Result<Vec<u8>, String> {
        match self {
            #[cfg(any(feature = "pop"))]
            Mail::Pop3(_) => Err("请求无效".to_string()),
            #[cfg(any(feature = "imaps"))]
            Mail::Imap(_) => Err("请求无效".to_string()),
            #[cfg(any(feature = "smtp"))]
            Mail::Smtp(e) => e.send(),
            Mail::None => Err("请求无效".to_string())
        }
    }

    fn set_from(&mut self, name: &str, mail: &str) -> &mut Self {
        match self {
            #[cfg(any(feature = "pop"))]
            Mail::Pop3(_) => {}
            #[cfg(any(feature = "imaps"))]
            Mail::Imap(_) => {}
            Mail::None => {}
            #[cfg(any(feature = "smtp"))]
            Mail::Smtp(e) => {
                e.set_from(name, mail);
            }
        }
        self
    }

    fn set_to(&mut self, name: &str, mail: &str) -> &mut Self {
        match self {
            #[cfg(any(feature = "pop"))]
            Mail::Pop3(_) => {}
            #[cfg(any(feature = "imaps"))]
            Mail::Imap(_) => {}
            Mail::None => {}
            #[cfg(any(feature = "smtp"))]
            Mail::Smtp(e) => {
                e.set_to(name, mail);
            }
        }
        self
    }
    fn set_cc(&mut self, name: &str, mail: &str) -> &mut Self {
        match self {
            #[cfg(any(feature = "pop"))]
            Mail::Pop3(_) => {}
            #[cfg(any(feature = "imaps"))]
            Mail::Imap(_) => {}
            Mail::None => {}
            #[cfg(any(feature = "smtp"))]
            Mail::Smtp(e) => {
                e.set_cc(name, mail);
            }
        }
        self
    }

    fn set_subject(&mut self, subject: &str) -> &mut Self {
        match self {
            #[cfg(any(feature = "pop"))]
            Mail::Pop3(_) => {}
            #[cfg(any(feature = "imaps"))]
            Mail::Imap(_) => {}
            Mail::None => {}
            #[cfg(any(feature = "smtp"))]
            Mail::Smtp(e) =>
                {
                    e.set_subject(subject);
                }
        }
        self
    }

    fn set_body(&mut self, text: &str, html: &str) -> &mut Self {
        match self {
            #[cfg(any(feature = "pop"))]
            Mail::Pop3(_) => {}
            #[cfg(any(feature = "imaps"))]
            Mail::Imap(_) => {}
            Mail::None => {}
            #[cfg(any(feature = "smtp"))]
            Mail::Smtp(e) => {
                e.set_body(text, html);
            }
        }
        self
    }

    fn set_file(&mut self, name: &str, data: Vec<u8>, content_type: &str) -> &mut Self {
        match self {
            #[cfg(any(feature = "pop"))]
            Mail::Pop3(_) => {}
            #[cfg(any(feature = "imaps"))]
            Mail::Imap(_) => {}
            Mail::None => {}
            #[cfg(any(feature = "smtp"))]
            Mail::Smtp(e) => {
                e.set_file(name, data, content_type);
            }
        }
        self
    }
}

pub trait Mode {
    /// 获取最大ID
    fn mail_id_max(&mut self) -> Result<u32, String>;
    /// 获取指定量ID列表
    fn mail_id_list(&mut self) -> Result<Vec<String>, String>;
    /// 获取指定邮件
    fn mail_id_get(&mut self, mail_id: u32) -> Result<Vec<u8>, String>;
    /// 获取邮件尺寸
    fn mail_msg_size(&mut self, mail_id: u32) -> Result<u32, String>;
    /// 发送邮件
    fn send(&mut self) -> Result<Vec<u8>, String>;

    /// 设置发件人信息
    fn set_from(&mut self, name: &str, mail: &str) -> &mut Self;

    /// 收件人
    /// * name 收件人名称
    /// * mail 收件箱
    fn set_to(&mut self, name: &str, mail: &str) -> &mut Self;
    /// 抄送人
    /// * name 收件人名称
    /// * mail 收件箱
    fn set_cc(&mut self, name: &str, mail: &str) -> &mut Self;
    /// 设置主题
    fn set_subject(&mut self, subject: &str) -> &mut Self;
    /// 设置内容
    fn set_body(&mut self, text: &str, html: &str) -> &mut Self;
    /// 设置文件
    fn set_file(&mut self, name: &str, data: Vec<u8>, content_type: &str) -> &mut Self;
}

#[derive(Clone)]
pub enum Protocol {
    Pop3,
    Imap,
    Smtp,
    None,
}

impl Protocol {
    pub fn str(&mut self) -> &'static str {
        match self {
            Protocol::Pop3 => "pop3",
            Protocol::Imap => "imap",
            Protocol::Smtp => "smtp",
            Protocol::None => ""
        }
    }
    pub fn from(name: &str) -> Self {
        let name = name.to_lowercase();
        match name.as_str() {
            "pop3" => Protocol::Pop3,
            "imap" => Protocol::Imap,
            "smtp" => Protocol::Smtp,
            _ => Protocol::None
        }
    }
}

#[derive(Clone)]
pub struct Connection {
    pub host: String,
    pub port: String,
    pub name: String,
    pub mail: String,
    pub pass: String,
    pub protocol: Protocol,
}

impl Connection {
    pub fn default() -> Connection {
        Self {
            host: "".to_string(),
            port: "".to_string(),
            name: "".to_string(),
            mail: "".to_string(),
            pass: "".to_string(),
            protocol: Protocol::Imap,
        }
    }

    pub fn new_smtp() -> Connection {
        Self {
            host: "".to_string(),
            port: "".to_string(),
            name: "".to_string(),
            mail: "".to_string(),
            pass: "".to_string(),
            protocol: Protocol::Smtp,
        }
    }

    pub fn json(&mut self) -> JsonValue {
        let mut data = object! {};
        data["host"] = self.host.clone().into();
        data["port"] = self.port.clone().into();
        data["name"] = self.name.clone().into();
        data["pass"] = self.pass.clone().into();
        data["mail"] = self.mail.clone().into();
        data["protocol"] = self.protocol.str().into();
        data
    }
    pub fn from(data: JsonValue) -> Self {
        let protocol = Protocol::from(data["protocol"].as_str().unwrap());
        Self {
            host: data["host"].to_string(),
            port: data["port"].to_string(),
            name: data["name"].to_string(),
            mail: data["mail"].to_string(),
            pass: data["pass"].to_string(),
            protocol,
        }
    }
}