use core::panic;
use pest::Parser;
use proc_macro::TokenStream;
use std::str::FromStr;

use super::scan_path_utils::attr_split_to_map;

#[derive(Debug, Clone)]
pub struct FunContent {
    pub fun_name: String,
    pub fun_param: String,
    pub fun_head_end_group: String,
    pub fun_for_content: String,
}

#[derive(Parser)]
#[grammar = "./pestf/aop_celler_input.pest"]
pub struct AopCellerParser;

pub fn read_this_parset(unparsed_file: String) -> Option<FunContent> {
    let file = AopCellerParser::parse(Rule::file, unparsed_file.as_str())
        .expect("unsuccessful parse")
        .next()
        .unwrap();
    for line in file.into_inner() {
        match line.as_rule() {
            Rule::scan_macro_fun_content => {
                let mut inner_rules = line.into_inner();
                let fun_name_group = inner_rules.next().unwrap().as_str().to_string();
                let fun_brackets_group = inner_rules.next().unwrap().as_str().to_string();
                let fun_head_end_group = inner_rules.next().unwrap().as_str().to_string();
                let fun_for_content = inner_rules.next().unwrap().as_str().to_string();
                return Some(FunContent {
                    fun_name: fun_name_group,
                    fun_param: fun_brackets_group,
                    fun_head_end_group: fun_head_end_group,
                    fun_for_content: fun_for_content,
                });
            }
            Rule::EOI => (),
            _ => (),
        }
    }
    return None;
}

pub fn impl_aop(_attr: &TokenStream, _input: &TokenStream) -> TokenStream {
    let attr_str = _attr.clone().to_string();
    let input_str = _input.clone().to_string();
    let param = attr_split_to_map(&attr_str);
    let mut first_bean = String::new();
    if let Some(r) = param.get("first_bean") {
        first_bean = r.clone();
    }
    let mut last_bean = String::new();
    if let Some(r) = param.get("last_bean") {
        last_bean = r.clone();
    }
    let fun_content_op = read_this_parset(input_str);

    match fun_content_op {
        Some(celler_fun) => {
            let mut fun_for_content = celler_fun.fun_for_content;
            fun_for_content = for_substring!(
                &fun_for_content,
                fun_for_content.find("{").unwrap() + 1,
                fun_for_content.rfind("}").unwrap()
            )
            .to_string();

            let source_code_str = format!(
                "pub fn aop_{}({}){}{{{}}}",
                celler_fun.fun_name,
                celler_fun.fun_param,
                celler_fun.fun_head_end_group,
                fun_for_content
            );
            let first_set_param_str =
                format!("let mut aop_param_vec = Vec::<Box<dyn Any>>::new();");
            let last_set_param_str = format!("let mut aop_last_param_vec = Vec::<Box<dyn Any>>::new();aop_last_param_vec.push(Box::new(aop_result.clone()));");
            let mut insert_param_str = String::new();
            let mut aop_set_param_str = String::new();
            for rely_str in &celler_fun.fun_param.split(",").collect::<Vec<&str>>() {
                if rely_str.is_empty() {
                    continue;
                }
                match rely_str.split_once(":") {
                    Some((key, _)) => {
                        aop_set_param_str += &(key.trim().to_string() + ",");
                        let insert_str =
                            format!("aop_param_vec.push(Box::new({}.clone()));", key.trim());
                        insert_param_str = insert_param_str + &insert_str;
                    }
                    None => {
                        continue;
                    }
                }
            }
            aop_set_param_str =
                for_substring!(aop_set_param_str, 0, aop_set_param_str.rfind(",").unwrap())
                    .to_string();

            let mut aop_first_str = String::new();
            if !first_bean.is_empty() {
                aop_first_str = format!(
                    "single_get_unwrap!({:?},{})(aop_param_vec);",
                    first_bean, "Box<(dyn Fn(Vec<Box<dyn Any>>) + Send + Sync )>"
                );
            }

            let run_source_code_str = format!(
                "let aop_result = aop_{}({});",
                celler_fun.fun_name, aop_set_param_str
            );
            let mut aop_last_str = String::new();

            if !last_bean.is_empty() {
                aop_last_str = format!(
                    "{} single_get_unwrap!({:?},{})(aop_last_param_vec);",
                    last_set_param_str,
                    last_bean,
                    "Box<(dyn Fn(Vec<Box<(dyn Any)>>) + Send + Sync )>"
                );
            }
            let content_concat_str = format!(
                "{}{}{}{}{}",
                first_set_param_str,
                insert_param_str,
                aop_first_str,
                run_source_code_str,
                aop_last_str
            );
            let code_str = format!(
                "pub fn {}({}){}{{{}  return aop_result; }} {}",
                celler_fun.fun_name,
                celler_fun.fun_param,
                celler_fun.fun_head_end_group,
                content_concat_str,
                source_code_str
            );
            println!("aop_code:\n{}", code_str);
            let result_token_stream = proc_macro2::TokenStream::from_str(&code_str).unwrap();
            return TokenStream::from(result_token_stream);
        }
        None => {
            panic!("parset this error");
        }
    }
}
