use crate::{completion::feature::{get_dot_completion, include_path_completion}, core, hover::feature::make_module_profile_code, server::LspServer, sources::LSPSupport, utils::{from_uri_to_escape_path_string, get_definition_token, get_language_id_by_uri, is_character_ordered_match}};
#[allow(unused)]
use log::info;
use tower_lsp::lsp_types::*;

use super::feature::{vlog_directives_completion, vlog_directives_completion_without_prefix};


pub fn completion(server: &LspServer, params: &CompletionParams) -> Option<CompletionResponse> {
    let doc = &params.text_document_position;
    let uri = &params.text_document_position.text_document.uri;
    let pos = doc.position;
    let language_id = get_language_id_by_uri(uri);

    let path_string = from_uri_to_escape_path_string(uri).unwrap();

    // 等待解析完成
    server.srcs.wait_parse_ready(&path_string, false);
    let source = server.srcs.get_source(&path_string)?;
    let source = source.read().ok()?;

    // 获取当前这行的结果和当前光标所在的 token
    let line_text = source.text.line(doc.position.line as usize);
    let token = get_definition_token(&line_text, doc.position);

    let response = match &params.context {
        Some(context) => match context.trigger_kind {
            // 特殊字符触发
            CompletionTriggerKind::TRIGGER_CHARACTER => {

                let trigger_character = context.trigger_character.clone().unwrap();
                match trigger_character.as_str() {
                    "." => {
                        // 用户按下 . 如果是在例化 scope 中，则补全对应的 port
                        get_dot_completion(server, &line_text, uri, &pos, &language_id)
                    },
                    "$" => {
                        // 用户按下 $ 补全系统函数
                        Some(CompletionList {
                            is_incomplete: false,
                            items: server.vlog_sys_tasks_completion_items.clone(),
                        })
                    },
                    "`" => {
                        // 用户按下 ` , 补全系统宏定义和用户自己的宏定义
                        vlog_directives_completion(&token, server)
                    },
                    "/" => {
                        // 用户按下 / ，如果在 "" 内触发，路径的自动补全
                        include_path_completion(&doc.text_document.uri, &line_text, pos)
                    },
                    "\"" => {
                        // 用户按下 " ，如果开头有 include，则自动补全 
                        include_path_completion(&doc.text_document.uri, &line_text, pos)
                    }
                    _ => None,
                }
            }
            
            // 对于上一次自动补全结果 is_incomplete: true 的操作，还会额外触发一次自动补全，
            // 它的逻辑在这个分支里面
            CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS => None,

            // 常规触发
            CompletionTriggerKind::INVOKED => {
                // 1. 先根据 AST 获取上下文补全项
                // 去除如下几种情况：module textmacro
                let mut completion_items = server.srcs.get_completions(
                    &token,
                    source.text.pos_to_byte(&doc.position),
                    &doc.text_document.uri,
                )?;

                // info!("current completion token: {}", token);
                
                // 2. 根据 token 加入关键词
                // TODO: 考虑使用前缀树进行优化
                completion_items.items.extend::<Vec<CompletionItem>>(
                    server.vlog_keyword_completion_items
                        .iter()
                        .filter(|x| is_character_ordered_match(&token, &x.label))
                        .cloned()
                        .collect(),
                );

                // 3. 根据 token 加入系统函数
                // TODO: 考虑使用前缀树进行优化
                completion_items.items.extend::<Vec<CompletionItem>>(
                    server.vlog_sys_tasks_completion_items
                        .iter()
                        .filter(|x| is_character_ordered_match(&token, &x.label))
                        .cloned()
                        .collect(),
                );
                
                // 4. 加入例化自动补全的
                completion_items.items.extend::<Vec<CompletionItem>>(
                    make_module_completions(server, &token, &language_id)
                );

                // 5. 加入宏的自动补全
                completion_items.items.extend::<Vec<CompletionItem>>(
                    vlog_directives_completion_without_prefix(&token, server)
                );

                // 不知道为什么会有重复，去重就完事
                completion_items.items.dedup_by_key(|i| i.label.clone());

                // info!("invoked return completion_items {:?}", completion_items);
                Some(completion_items)
            }
            _ => None,
        },
        None => {
            return None;
        }
    };

    Some(CompletionResponse::List(response?))
}

fn make_primitives_instantiation_code(text: &str) -> String {
    let mut instantiations = text.lines()
    .filter(|line| !line.trim().is_empty() && !line.trim().starts_with("//"))
    .collect::<Vec<&str>>();
    // remove fake module and endmodule
    instantiations.remove(0);
    instantiations.pop();

    instantiations.iter().map(|line| {
        let trimmed = line.trim_start();
        if trimmed.contains('.') {
            format!("\t{}", trimmed)
        } else {
            trimmed.to_string()
        }
    }).collect::<Vec<String>>().join("\n")
}

fn make_primitives_module_profile_code(text: &str) -> String {
    let mut lines: Vec<&str> = text.split_inclusive('\n').collect();

    if lines.len() > 1 {
        lines.remove(0);
        lines.pop();
    }

    lines.join("")
}


fn make_instantiation_code(module: &crate::core::hdlparam::Module) -> String {
    // makeVlogParamAssignments 参数信息列表
    let mut snippet_codes = Vec::<String>::new();
    let mut placeholder_id: u32 = 1;

    snippet_codes.push(format!("{} ", module.name));

    let params_length = module.params.len();
    let ports_length = module.ports.len();

    if params_length > 0 {
        snippet_codes.push("#(\n".to_string());

        let max_name_length = module.params.iter().map(|param| param.name.len()).max().unwrap_or(0);
        let max_init_length = module.params.iter().map(|param| param.init.len()).max().unwrap_or(0);
        let mut i: usize = 0;
        for param in &module.params {
            // 写入 .NAME  ( INIT  ),
            let padding_name = format!("{}{}", param.name, " ".repeat(max_name_length - param.name.len() + 1));
            snippet_codes.push(format!("\t.{}\t(", padding_name));
            snippet_codes.push(format!("${{{}:{}}}", placeholder_id, param.init));
            placeholder_id += 1;
            snippet_codes.push(format!("{} )", " ".repeat(max_init_length - param.init.len() + 1)));

            if i < params_length - 1 {
                snippet_codes.push(",\n".to_string());
            }
            i += 1;
        }

        snippet_codes.push(")\n".to_string());
    }

    snippet_codes.push(format!("u_{}", module.name));

    if ports_length > 0 {
        let max_name_length = module.ports.iter().map(|port| port.name.len()).max().unwrap_or(0);
        snippet_codes.push("(\n".to_string());
        let mut i: usize = 0;
        for port in &module.ports {
            let padding_name = format!("{}{}", port.name, " ".repeat(max_name_length - port.name.len() + 1));
            snippet_codes.push(format!("\t.{}\t(", padding_name));
            snippet_codes.push(format!("${{{}:{}}}", placeholder_id, port.name));
            placeholder_id += 1;
            snippet_codes.push(format!("{} )", " ".repeat(max_name_length - port.name.len() + 1)));

            if i < ports_length - 1 {
                snippet_codes.push(",".to_string());
            }
            snippet_codes.push("\n".to_string());

            i += 1;
        }

        snippet_codes.push(");\n".to_string());
    }

    snippet_codes.join("")
}

/// 自动补全例化模块
fn make_module_completions(
    server: &LspServer,
    token: &str,
    language_id: &str
) -> Vec<CompletionItem> {
    let mut module_completioms = Vec::<CompletionItem>::new();
    let hdl_param = server.srcs.hdl_param.clone();

    let prefix = token.to_string().to_lowercase();
    let module_name_to_path = hdl_param.module_name_to_path.read().unwrap();

    // 获取和自动补全相关的配置
    let auto_add_output_declaration = server.srcs.get_lsp_configuration_bool_value("digital-ide.function.lsp.completion.vlog.auto-add-output-declaration").unwrap_or(true);    

    // 遍历 hdlparam 中所有的 modules
    for module_name in module_name_to_path.keys() {
        if !module_name.to_string().to_lowercase().starts_with(&prefix) {
            continue;
        }
        if let Some((module, file_type, def_path)) = hdl_param.find_module_context_by_name(&module_name) {
            let mut insert_text = Vec::<String>::new();
            if auto_add_output_declaration {
                if let Some(declaration_string) = make_output_declaration(&module) {
                    insert_text.push(declaration_string);
                }
            }

            let (insert_text, module_profile, define_info) = if file_type == "primitives" {
                let primitive_map = server.srcs.primitive_text.name_to_text.read().unwrap();
                if let Some(text) = primitive_map.get(&module.name) {
                    insert_text.push(make_primitives_instantiation_code(text));
                    (
                        insert_text.join("\n"),
                        make_primitives_module_profile_code(text),
                        format!("[Definition] Primitive module: {}", module.name)
                    )
                } else {
                    continue;
                }
            } else {
                insert_text.push(make_instantiation_code(&module));
                let path_uri = Url::from_file_path(def_path).unwrap().to_string();
                let def_row = module.range.start.line;
                let def_col = module.range.start.character;

                (
                    insert_text.join("\n"),
                    make_module_profile_code(&module),
                    format!("Go to [Definition]({path_uri}#L{def_row}:{def_col})")
                )
            };

            let module_profile = MarkupContent {
                kind: MarkupKind::Markdown,
                value: format!("```{}\n{}\n```\n{}", language_id, module_profile, define_info)
            };

            let detail = format!("module instantiation ({})", file_type);
            let label_details = CompletionItemLabelDetails {
                description: Some("module instantiation".to_string()),
                ..Default::default()
            };
            let item = CompletionItem {
                label: module.name.to_string(),
                detail: Some(detail),
                label_details: Some(label_details),
                documentation: Some(Documentation::MarkupContent(module_profile)),
                kind: Some(CompletionItemKind::CLASS),
                insert_text: Some(insert_text),
                // 给模块例化自动补全附上最高权重
                sort_text: Some("0".to_string()),
                insert_text_format: Some(InsertTextFormat::SNIPPET),
                insert_text_mode: Some(InsertTextMode::ADJUST_INDENTATION),
                ..CompletionItem::default()
            };
            module_completioms.push(item);  
        }
    }

    

    module_completioms
}

fn make_output_declaration(
    module: &core::hdlparam::Module
) -> Option<String> {
    let mut output_declaration = Vec::<String>::new();
    for port in &module.ports {
        if port.dir_type == "output" || port.dir_type == "out" {
            let mut declaration = Vec::<String>::new();
            if port.net_type == "reg" || port.net_type == "wire" {
                declaration.push(port.net_type.to_string());
            } else {
                declaration.push("wire".to_string());
            }
            if port.width != "1" {
                declaration.push(port.width.to_string());
            }
            declaration.push(port.name.to_string());
            output_declaration.push(format!("{};", declaration.join(" ")));
        }
    }

    if output_declaration.len() > 0 {
        let output_declaration = format!("// output declaration of module {}\n{}\n", module.name, output_declaration.join("\n"));
        Some(output_declaration)
    } else {
        None
    }
}