use std::collections::HashMap;
use std::{env, fs, io};
use std::io::{Error, ErrorKind, Write};
use br_crypto::encoding::code_to_utf8;
use br_crypto::hash::u8_to_md5;
use chrono::{DateTime, Local, TimeZone, Utc};
use json::{object, JsonValue};
use log::error;
use std::process::exit;

/// 解析邮件
#[derive(Debug)]
pub struct AnalyzeEmails {
    pub debug: bool,
    pub header: HashMap<String, String>,
    pub mime_version: String,
    boundary: String,
    pub md5: String,
    pub size: usize,
    /// 时间戳
    pub timestamp: i64,
    /// 本地时间
    pub datetime: String,
    /// 主题
    pub subject: String,
    /// 发件人
    pub from: HashMap<String, String>,
    /// 收件人
    pub to: HashMap<String, String>,
    /// 抄送人
    pub cc: HashMap<String, String>,
    /// 用于指定收件人回复邮件时应该使用的电子邮件地址
    pub replyto: HashMap<String, String>,
    /// 内容类型
    pub content_type: String,
    /// 编码规则
    pub content_transfer_encoding: ContentTransferEncoding,
    /// 实际发件人
    pub sender: String,
    pub body_text: String,
    pub body_html: String,
    pub files: JsonValue,
    pub charset: String,
}

impl AnalyzeEmails {
    pub fn new(data: Vec<u8>, debug: bool) -> io::Result<AnalyzeEmails> {
        let md5 = u8_to_md5(data.clone()).leak().to_string();
        let size = data.len();
        let subsequence = "\r\n\r\n".as_bytes();
        let (header, body) = match data.windows(subsequence.len()).position(|window| window == subsequence) {
            None => return Err(Error::new(ErrorKind::Other, "协议格式错误")),
            Some(e) => (data[..e].to_vec(), data[e + 4..].to_vec())
        };
        let mut that = Self {
            debug: debug,
            header: Default::default(),
            mime_version: "".to_string(),
            boundary: "".to_string(),
            md5,
            size,
            timestamp: 0,
            subject: "".to_string(),
            from: Default::default(),
            to: Default::default(),
            cc: Default::default(),
            replyto: Default::default(),
            datetime: "".to_string(),
            content_type: "".to_string(),
            content_transfer_encoding: ContentTransferEncoding::Bit7,
            sender: "".to_string(),
            body_text: "".to_string(),
            body_html: "".to_string(),
            files: object! {},
            charset: "utf-8".to_string(),
        };
        that.header(header)?;
        that.body(body)?;
        Ok(that)
    }

    fn header(&mut self, data: Vec<u8>) -> io::Result<()> {
        let data = unsafe { String::from_utf8_unchecked(data) };
        let data = data.replace("\r\n\t", "").replace("\r\n ", " ").leak();
        for item in data.lines() {
            let (key, value) = match item.find(": ") {
                None => {
                    continue;
                }
                Some(e) => (&item[..e], &item[e + 2..])
            };
            let name = key.to_lowercase().leak();
            if value.is_empty() {
                continue;
            }
            match key.to_lowercase().as_str() {
                "mime-version" => self.mime_version = value.to_string(),
                "from" => {
                    self.from = self.from(value);
                }
                "sender" => {
                    self.sender = value.to_string();
                }
                "to" => {
                    self.to = self.email_encoded(value);
                }
                "cc" => {
                    self.cc = self.email_encoded(value);
                }
                "reply-to" => {
                    self.replyto = self.email_encoded(value);
                }
                "subject" => {
                    self.subject = self.subject(value.to_string());
                }
                "content-type" => {
                    let types = value.split(";").collect::<Vec<&str>>();
                    self.content_type = types[0].to_string();
                    match self.content_type.to_lowercase().as_str() {
                        "multipart/mixed" | "multipart/alternative" | "multipart/related" | "multipart/report"=> {
                            match value.find("boundary=") {
                                None => {}
                                Some(e) => {
                                    let boundary = &value[e..];
                                    self.boundary = boundary.trim_start_matches("boundary=\"").trim_end_matches("\"").to_string();
                                }
                            }
                        }
                        _ => {}
                    }
                    if types.len() > 1 {
                        for item in types.iter() {
                            if item.contains("charset=") {
                                self.charset = item.trim_start_matches("charset=").trim_start_matches("\"").trim_end_matches("\"").to_string();
                            }
                        }
                    }
                }
                "content-transfer-encoding" => {
                    self.content_transfer_encoding = ContentTransferEncoding::from(value);
                }
                "date" => {
                    let index = value.len() - 13;
                    let t = &value[index - 1..index];
                    let value = if t == " " {
                        format!("{}0{}", &value[..index], &value[index..]).leak()
                    } else {
                        value
                    };
                    // 解析字符串为 DateTime<Utc>
                    let datetime = match DateTime::parse_from_rfc2822(value) {
                        Ok(e) => e.with_timezone(&Utc),
                        Err(e) => {
                            return Err(Error::new(ErrorKind::Other, format!("时间解析失败: {e} [{value}]")))
                        }
                    };
                    self.timestamp = datetime.timestamp();
                    // 使用 Utc 将时间戳转换为 UTC 时间
                    let datetime_utc: DateTime<Utc> = Utc.timestamp_opt(self.timestamp, 0).unwrap();
                    // 将 UTC 时间转换为本地时间
                    let datetime_local: DateTime<Local> = datetime_utc.with_timezone(&Local);
                    self.datetime = datetime_local.format("%Y-%m-%d %H:%M:%S").to_string().leak().to_string();
                }
                _ => {
                    self.header.insert(name.trim().to_string(), value.to_string());
                }
            }
        }
        Ok(())
    }
    fn body(&mut self, data: Vec<u8>) -> io::Result<()> {
        match self.content_type.to_lowercase().as_str() {
            "text/html" => {
                let data = self.content_transfer_encoding.decode(data)?;
                let res = code_to_utf8(self.charset.as_str(), data.clone());
                self.body_html = res.leak().to_string();
            }
            "text/plain" => {
                let data = self.content_transfer_encoding.decode(data)?;
                let res = code_to_utf8(self.charset.as_str(), data.clone());
                self.body_text = res.leak().to_string();
            }
            "multipart/mixed" | "multipart/alternative" | "multipart/related" | "multipart/report" => {
                let data = self.content_transfer_encoding.decode(data.clone())?;
                let parts = unsafe { String::from_utf8_unchecked(data) };
                let parts = parts.trim().trim_end_matches(format!("--{}--", self.boundary).as_str()).to_string();
                let parts = parts.leak().split(format!("--{}", self.boundary).as_str())
                    .map(|x| x.trim())
                    .filter(|&x| !x.is_empty()).collect::<Vec<&'static str>>();
                for part in parts {
                    if part.contains("This is a multi-part message in MIME format."){
                        continue
                    }
                    self.parts(part.to_string())?;
                }
            }
            _ => {
                error!("未知body类型: {}", self.content_type);
                if self.debug {
                    exit(0);
                }
            }
        }
        Ok(())
    }
    fn parts(&mut self, data: String) -> io::Result<()> {
        let (&ref header, body) = match data.find("\r\n\r\n") {
            None => {println!("{}", data); return Err(Error::new(ErrorKind::Other, "解析附件头失败"))},
            Some(e) => (&data[..e].replace("\r\n\t", " ").leak().lines(), &data[e + 4..])
        };

        let mut content_disposition = "";
        let mut filename = "".to_string();
        let mut content_type = "";
        let mut boundary = "";
        let mut content_transfer_encoding = ContentTransferEncoding::None;
        for item in header.clone() {
            let (key, value) = match item.find(": ") {
                None => {
                    continue;
                }
                Some(e) => (&item[..e], &item[e + 2..])
            };
            let name = key.to_lowercase().leak();
            match name.trim() {
                "content-disposition" => {
                    match value.find("filename=") {
                        None => {}
                        Some(e) => {
                            let res = &value[e..];
                            let disposition = &value[..e];
                            filename = res.trim_start_matches("filename=\"").trim_end_matches("\"").to_string();
                            content_disposition = disposition.trim().trim_end_matches(";").trim();
                        }
                    }
                }
                "content-transfer-encoding" => {
                    content_transfer_encoding = ContentTransferEncoding::from(value);
                }
                "content-type" => {
                    let types = value.trim().split(";").collect::<Vec<&'static str>>();
                    content_type = types[0];
                    let name = types.iter().filter(|&x| x.trim().starts_with("name=")).map(|&x| x.trim().to_string()).collect::<Vec<String>>();
                    if name.len() > 0 {
                        let name = name[0].to_string().leak().trim_start_matches("name=\"").trim_end_matches("\"");

                        filename = self.encoded(name);
                    }

                    if content_type == "multipart/mixed" {
                        let types = value.split(";").collect::<Vec<&str>>();
                        boundary = types[1].trim().trim_start_matches("boundary=\"").trim_end_matches("\"");
                    }
                }
                "content-id"=>{
                    
                }
                _ => {
                    error!("未知header类型: {}", name);
                    if self.debug {
                        exit(0);
                    }
                }
            }
        }

        match content_type {
            "text/plain" => {
                if filename.is_empty() {
                    let res = content_transfer_encoding.decode(body.as_bytes().to_vec())?;
                    self.body_text = unsafe { String::from_utf8_unchecked(res) }.leak().to_string();
                } else {
                    self.set_files(content_transfer_encoding, body, filename.as_str(), content_type)?;
                }
            }
            "text/html" => {
                let res = content_transfer_encoding.decode(body.as_bytes().to_vec())?;
                self.body_html = unsafe { String::from_utf8_unchecked(res) }.leak().to_string();
            }
            "multipart/mixed" => {
                let data = self.content_transfer_encoding.decode(body.as_bytes().to_vec())?;
                let parts = unsafe { String::from_utf8_unchecked(data) };

                let parts = parts.trim().trim_end_matches(format!("--{}--", boundary).as_str()).to_string();
                let parts = parts.leak().split(format!("--{}", boundary).as_str())
                    .map(|x| x.trim())
                    .filter(|&x| !x.is_empty()).collect::<Vec<&'static str>>();
                for part in parts {
                    self.parts(part.to_string())?;
                }
            }
            "application/octet-stream" | "application/zip" | "application/pdf" => {
                self.set_files(content_transfer_encoding, body, filename.as_str(), content_type)?;
            }
            _ => {
                // error!("未知parts content_type类型: {}", content_type);
                // if self.debug {
                //     exit(0);
                // }
            }
        }
        Ok(())
    }
    pub fn from(&mut self, value: &str) -> HashMap<String, String> {
        let mut r = value.split("<").filter(|x| !x.trim().is_empty()).map(|x| x.trim()).collect::<Vec<&str>>();
        if r[0].starts_with("\"") && r[0].ends_with("\"") {
            r[0] = r[0].trim_start_matches("\"").trim_end_matches("\"").trim();
        }
        let mut emails = HashMap::new();
        if r.len() == 1 {
            let name = r[0].trim_end_matches(">").to_string();
            emails.insert(name.clone(), name);
        } else {
            let name = self.encoded(r[0].trim());
            let email = r[1].trim_end_matches(">").to_string();
            emails.insert(email, name);
        }
        emails
    }
    fn subject(&mut self, value: String) -> String {
        let value = value.replace("?==?", "?=\r\n\t=?");
        if !value.starts_with("=?") && !value.ends_with("?=") {
            return value.leak().to_string();
        }
        let list = value.leak().split("\r\n\t").collect::<Vec<&str>>();
        if list.len() > 1 {
            let mut charset = "utf-8";
            let mut strs = "";
            let mut code = "B";
            for item in list.iter() {
                let line = item.split('?').collect::<Vec<&str>>();
                charset = line[1].to_lowercase().to_string().leak();
                code = line[2];
                strs = format!("{strs}{}", line[3]).leak();
            }
            let text = format!("=?{charset}?{code}?{strs}?=");
            self.encoded_line(text.as_str())
        } else {
            self.encoded_line(list[0])
        }
    }

    fn encoded(&mut self, value: &str) -> String {
        let mut t = value.trim_start_matches("\"").trim_end_matches("\"");
        t = t.trim_start_matches("\"").trim_end_matches("\"");
        if t.starts_with("=?") && t.ends_with("?=") {
            self.encoded_line(t)
        } else {
            t.to_string().leak().to_string()
        }
    }
    /// 段落解码
    fn encoded_line(&mut self, value: &str) -> String {
        let line = value.split('?').collect::<Vec<&str>>();
        let charset = line[1].to_lowercase().to_string().leak();
        let code = line[2];
        let data = line[3].to_string().leak();
        let strs = match code {
            "B" => br_crypto::base64::decode_u8(data),
            "Q" => br_crypto::qp::decode(data).unwrap_or(vec![]),
            _ => data.as_bytes().to_vec()
        };
        let text = code_to_utf8(charset, strs).leak();
        let t: String = text.chars().filter(|&x| x != '\u{200b}').collect();
        t.leak().to_string()
    }
    pub fn email_encoded(&mut self, value: &str) -> HashMap<String, String> {
        let list = value.split(",").map(|x| x.trim()).collect::<Vec<&str>>();
        let mut emails = HashMap::new();
        for item in list {
            let mut r = item.split(" <").collect::<Vec<&str>>();
            if r[0].starts_with("\"") && r[0].ends_with("\"") {
                r[0] = r[0].trim_start_matches("\"").trim_end_matches("\"");
            }
            if r.len() == 1 {
                let name = r[0].trim_start_matches("<").trim_end_matches(">").to_string();
                emails.insert(name.clone(), name);
            } else {
                let name = self.encoded(r[0].trim());
                let email = r[1].trim_end_matches(">").to_string();
                emails.insert(email, name);
            }
        }
        emails
    }
    fn set_files(&mut self, mut content_transfer_encoding: ContentTransferEncoding, body: &str, filename: &str, content_type: &str) -> io::Result<()> {
        let mut data = "";
        match content_transfer_encoding {
            ContentTransferEncoding::Base64 => {
                let mut text = "".to_string();
                for line in body.lines() {
                    text += line;
                }
                data = text.leak();
            }
            _ => {}
        }


        let body = content_transfer_encoding.decode(data.as_bytes().to_vec())?;
        let md5 = u8_to_md5(body.clone());
        let size = body.len();
        // 获取系统临时目录
        let mut temp_dir = env::temp_dir();
        // 构造临时文件的完整路径
        temp_dir.push(filename);
        // 打开（创建）临时文件
        let mut temp_file = match fs::File::create(&temp_dir) {
            Ok(e) => e,
            Err(e) => return Err(Error::new(ErrorKind::Other, e))
        };
        match temp_file.write(body.as_slice()) {
            Ok(_) => {
                self.files[md5.as_str()] = object! {
                            name:filename,
                            md5:md5.clone(),
                            size:size,
                            "content-type":content_type,
                            file:temp_dir.to_str()
                        };
            }
            Err(_) => {}
        };
        Ok(())
    }
}

impl Default for AnalyzeEmails {
    fn default() -> Self {
        Self {
            debug: false,
            header: Default::default(),
            mime_version: "".to_string(),
            boundary: "".to_string(),
            md5: "".to_string(),
            size: 0,
            timestamp: 0,
            datetime: "".to_string(),
            subject: "".to_string(),
            from: Default::default(),
            to: Default::default(),
            cc: Default::default(),
            replyto: Default::default(),
            content_type: "".to_string(),
            content_transfer_encoding: ContentTransferEncoding::QuotedPrintable,
            sender: "".to_string(),
            body_text: "".to_string(),
            body_html: "".to_string(),
            files: JsonValue::Null,
            charset: "".to_string(),
        }
    }
}

/// 编码规则
/// 选择 Content-Transfer-Encoding 的原则
///
/// 纯文本: 如果内容是纯文本且只包含 ASCII 字符，通常使用 7bit。
/// 非 ASCII 文本: 如果内容包含非 ASCII 字符，可以使用 quoted-printable 或 8bit，具体取决于内容和兼容性要求。
/// 二进制数据: 对于图像、视频、音频等二进制数据，通常使用 base64 编码。
#[derive(Debug)]
enum ContentTransferEncoding {
    /// 这种编码方式主要用于编码文本数据，它保持大部分文本的可读性，但会对非 ASCII 字符和特殊字符（如 =, ?, & 等）进行编码，以确保兼容性。
    /// 适用于包含大量特殊字符或非 ASCII 文本的邮件内容。
    QuotedPrintable,
    ///    将二进制数据编码为 ASCII 字符串，使用 64 个字符的字母表（A-Z, a-z, 0-9, +, /）表示二进制数据。每 3 个字节的二进制数据编码为 4 个字符，便于在邮件中传输。
    /// 常用于编码附件、图像、音频、视频等二进制数据。
    Base64,
    /// 表示内容是二进制数据，不能被转义或编码，必须保持原始的二进制格式进行传输。这种编码方式通常用于图像、音频等二进制文件。
    /// 这种编码要求邮件传输代理能够处理所有可能的字节值，几乎不做任何转换，因此也不是所有系统都支持。
    Binary,
    /// 表示内容包含 8 位字符，这意味着它可能包含非 ASCII 字符（如带有音标的字母）。尽管这样编码的邮件可以包含更多字符，但并非所有邮件传输代理都支持 8bit 传输。
    /// 适用于非 ASCII 的文本数据，但需要确保邮件传输链路支持 8bit 数据传输。
    Bit8,
    /// 表示内容是 ASCII 文本，仅包含 7 位字符（即标准 ASCII 字符集），每个字符的最高位是 0。这种编码方式是最常用的，因为它适合绝大多数邮件传输系统。
    /// 适用于纯文本邮件，不包含任何特殊字符或二进制数据。
    Bit7,
    None,
}

impl ContentTransferEncoding {
    fn from(value: &'static str) -> Self {
        match value {
            "7bit" => Self::Bit7,
            "8bit" => Self::Bit8,
            "binary" => Self::Binary,
            "base64" => Self::Base64,
            "quoted-printable" => Self::QuotedPrintable,
            _ => Self::None
        }
    }
    fn decode(&mut self, mut data: Vec<u8>) -> io::Result<Vec<u8>> {
        let res = match self {
            ContentTransferEncoding::QuotedPrintable => {
                br_crypto::qp::decode(data)?
            }
            ContentTransferEncoding::Base64 => {
                let str = unsafe { String::from_utf8_unchecked(data) };
                let mut text = "".to_string();
                for line in str.lines() {
                    text += line;
                }
                data = text.leak().as_bytes().to_vec();
                br_crypto::base64::decode_u8(data)
            }
            ContentTransferEncoding::Binary => data,
            ContentTransferEncoding::Bit8 => data,
            ContentTransferEncoding::Bit7 => data,
            ContentTransferEncoding::None => return Err(Error::new(ErrorKind::Other, "未知 ContentTransferEncoding"))
        };
        Ok(res)
    }
}