use std::io::{Error, ErrorKind, Read, Write};
use std::net::TcpStream;
use openssl::ssl::{SslConnector, SslMethod};

pub enum Server {
    StreamTls(openssl::ssl::SslStream<TcpStream>),
    Stream(TcpStream),
}

impl Server {
    pub fn new(host: String, port: u16) -> Result<Server, Error> {
        let ssl = if port == 995 {
            true
        } else {
            false
        };

        // 创建一个 OpenSSL SSL 连接器
        let connector = SslConnector::builder(SslMethod::tls())?.build();

        let res = match TcpStream::connect(format!("{}:{}", host, port)) {
            Ok(stream) => {
                if ssl {
                    // let root_store = RootCertStore::from_iter(
                    //     webpki_roots::TLS_SERVER_ROOTS
                    //         .iter()
                    //         .cloned(),
                    // );
                    //
                    // let config = rustls::ClientConfig::builder()
                    //     .with_root_certificates(root_store)
                    //     .with_no_client_auth();
                    //
                    // let server_name = host.try_into().unwrap();
                    // let connection = match ClientConnection::new(Arc::new(config), server_name) {
                    //     Ok(e) => e,
                    //     Err(e) => return Err(Error::new(ErrorKind::Other, format!("连接加载失败: {}", e.to_string())))
                    // };
                    // Server::StreamTls(StreamOwned::new(connection, stream))

                    let ssl_stream = connector.connect(host.as_str(), stream).unwrap();

                    Server::StreamTls(ssl_stream)
                } else {
                    Server::Stream(stream)
                }
            }
            Err(e) => return Err(Error::new(ErrorKind::Other, format!("连接加载失败: {}", e.to_string())))
        };
        Ok(res)
    }
    pub fn login(&mut self, name: String, pass: String) -> Result<(), Error> {
        self.read()?;
        self.write(format!("USER {}\r\n", name).as_bytes(), false)?;
        self.write(format!("PASS {}\r\n", pass).as_bytes(), false)?;
        Ok(())
    }

    fn write(&mut self, data: &[u8], long: bool) -> Result<String, Error> {
        let res = match self {
            Server::StreamTls(e) => e.write(data),
            Server::Stream(e) => e.write(data),
        };
        match res {
            Ok(_) => {
                match long {
                    true => {
                        Ok(self.read_multi()?)
                    }
                    false => {
                        let res = self.read()?;

                        if res.starts_with("+OK") {
                            let e = res.trim().trim_start_matches("+OK").trim();
                            return Ok(e.trim().to_string());
                        }
                        if res.starts_with("-Err") {
                            let e = res.trim().trim_start_matches("-Err").trim();
                            return Err(Error::new(ErrorKind::Other, e));
                        }
                        Err(Error::new(ErrorKind::Other, format!("写入失败: {}", res)))
                    }
                }
            }
            Err(e) => Err(Error::new(ErrorKind::Other, format!("写入失败: {}", e.to_string())))
        }
    }

    fn read(&mut self) -> Result<String, Error> {
        let res = match self {
            Server::StreamTls(e) => read(e)?,
            Server::Stream(e) => read(e)?
        };
        Ok(res)
    }

    fn read_multi(&mut self) -> Result<String, Error> {
        let res = match self {
            Server::StreamTls(e) => read_multi(e)?,
            Server::Stream(e) => read_multi(e)?
        };
        Ok(res)
    }

    pub fn total(&mut self) -> Result<usize, Error> {
        let res = self.write(format!("STAT\r\n").as_bytes(), false)?;
        let res = res.split(" ").collect::<Vec<&str>>();
        Ok(res[0].parse::<usize>().unwrap())
    }

    pub fn list(&mut self) -> Result<Vec<String>, Error> {
        let list = self.write(format!("LIST\r\n").as_bytes(), true)?;
        let lines = list.leak().lines();
        let mut ids = vec![];
        for line in lines {
            let li = line.trim().split(" ").collect::<Vec<&'static str>>();
            ids.push(li[0].to_string())
        }
        Ok(ids)
    }
    /// 获取详细信息
    pub fn info(&mut self, id: &str) -> Result<Vec<u8>, Error> {
        let info = self.write(format!("RETR {}\r\n", id).as_bytes(), true)?;
        Ok(info.as_bytes().to_vec())
    }

    /// 删除邮件
    pub fn dele(&mut self, id: &str) -> Result<bool, Error> {
        match self.write(format!("DELE {}\r\n", id).as_bytes(), false) {
            Ok(_) => Ok(true),
            Err(_) => Ok(false),
        }
    }
    pub fn logout(&mut self) -> Result<bool, Error> {
        match self.write(format!("QUIT\r\n").as_bytes(), false) {
            Ok(_) => Ok(true),
            Err(_) => Ok(false),
        }
    }
}
fn read_multi(reader: &mut impl Read) -> Result<String, Error> {
    let mut data = vec![];
    loop {
        let mut buffer = [0u8; 1024 * 1024];
        match reader.read(&mut buffer) {
            Ok(e) => data.extend(buffer[..e].to_vec()),
            Err(e) => return Err(Error::new(ErrorKind::Other, format!("读取失败: {}", e.to_string())))
        }
        if data.ends_with(&[13, 10, ".".as_bytes()[0], 13, 10]) {
            break;
        }
    }
    let str = unsafe { String::from_utf8_unchecked(data) };
    if !str.clone().starts_with("+OK") {
        return Err(Error::new(ErrorKind::Other, format!("读取失败: {}", str.clone())));
    }
    let data = match str.clone().find("\r\n") {
        None => return Err(Error::new(ErrorKind::Other, format!("读取失败: {}", str.clone()))),
        Some(index) => &str[index + 2..str.len() - 3]
    };
    Ok(data.to_string())
}

fn read(reader: &mut impl Read) -> Result<String, Error> {
    let mut data = vec![];
    loop {
        let mut buffer = [0u8; 1024];
        match reader.read(&mut buffer) {
            Ok(e) => {
                data.extend(buffer[..e].to_vec());
            }
            Err(e) => return Err(Error::new(ErrorKind::Other, format!("读取失败: {}", e.to_string())))
        }
        if data.ends_with(&[13, 10]) {
            break;
        }
    }
    let str = unsafe { String::from_utf8_unchecked(data) };
    Ok(str.trim().to_string())
}
