use std::{path::PathBuf, str::FromStr};

#[allow(unused)]
use log::info;
use ropey::{Rope, RopeSlice};
use tower_lsp::lsp_types::*;
use crate::{hover::feature::make_vhdl_module_profile_code, utils::{from_uri_to_escape_path_string, to_escape_path}};
#[allow(unused)]
use crate::{server::LspServer, sources::LSPSupport, utils::get_language_id_by_uri};



/// Called when the client requests a completion.
/// This function looks in the source code to find suitable options and then returns them
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 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()?;

    let line_text = source.text.line(doc.position.line as usize);
    let token = get_completion_token(
        &source.text,
        line_text.clone(),
        doc.position,
    );

    let project = server.srcs.vhdl_project.read().ok()?;

    #[allow(unused)]
    let global_project = project.as_ref().unwrap();
    
    let path = match PathBuf::from_str(uri.path()) {
        Ok(path) => path,
        Err(error) => {
            info!("error happen in vhdl <hover>: {:?}", error);
            return None;
        }
    };
    let escape_path = to_escape_path(&path);

    #[allow(unused)]
    let project_file = escape_path.as_path();
    
    // let Some(source) = global_project.project.get_source(project_file) else {
    //     return None
    // };
    // let cursor = from_lsp_pos(params.text_document_position.position);
    // let vhdl_project_completion_items = global_project.project
    //     .list_completion_options(&source, cursor)
    //     .into_iter()
    //     .map(|item| vhdl_ls::VHDLServer::completion_item_to_tower_lsp_item(item))
    //     .collect::<Vec<CompletionItem>>();

    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() {
                    // 按下 . 时需要触发的补全效果
                    "." => {
                        info!("trigger vhdl dot completion");
                        let mut completion_items = server.srcs.get_completions(
                            &token,
                            source.text.pos_to_byte(&doc.position),
                            &doc.text_document.uri,
                        )?;
                        
                        // completion_items.items.extend(vhdl_project_completion_items);

                        completion_items.items.dedup_by_key(|i| i.label.to_string());
                        Some(completion_items)
                    },
                    _ => None,
                }
            }
            CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS => None,
            
            // 一般情况下根据字符触发的补全项目
            CompletionTriggerKind::INVOKED => {
                // 1. 先根据 AST 获取上下文补全项
                let mut completion_items = server.srcs.get_completions(
                    &token,
                    source.text.pos_to_byte(&doc.position),
                    &doc.text_document.uri,
                )?;
                
                // 2. 根据 token 再加入关键词
                completion_items.items.extend::<Vec<CompletionItem>>(
                    server.vhdl_keyword_completiom_items
                        .iter()
                        .filter(|x| x.label.starts_with(&token))
                        .cloned()
                        .collect(),
                );

                // 3. 加入例化自动补全的
                completion_items.items.extend::<Vec<CompletionItem>>(
                    make_module_completions(server, &token, &language_id)
                );

                // completion_items.items.extend(vhdl_project_completion_items);

                // 去重
                completion_items.items.dedup_by_key(|i| i.label.to_string());
                Some(completion_items)
            }
            _ => None,
        },
        None => None
    };
    // eprintln!("comp response: {}", now.elapsed().as_millis());
    Some(CompletionResponse::List(response?))
}

fn prev_char(text: &Rope, pos: &Position) -> char {
    let char_idx = text.pos_to_char(pos);
    if char_idx > 0 {
        for i in (0..char_idx).rev() {
            let res = text.char(i);
            if !res.is_whitespace() {
                return res;
            }
        }
        ' '
    } else {
        ' '
    }
}

fn get_completion_token(text: &Rope, line: RopeSlice, pos: Position) -> String {
    let mut token = String::new();
    let mut line_iter = line.chars();
    for _ in 0..(line.utf16_cu_to_char(pos.character as usize)) {
        line_iter.next();
    }
    let mut c = line_iter.prev();
    //TODO: make this a regex
    while c.is_some()
        && (c.unwrap().is_alphanumeric()
            || c.unwrap() == '_'
            || c.unwrap() == '.'
            || c.unwrap() == '['
            || c.unwrap() == ']')
    {
        token.push(c.unwrap());
        c = line_iter.prev();
    }
    let mut result: String = token.chars().rev().collect();
    if result.contains('[') {
        let l_bracket_offset = result.find('[').unwrap_or(result.len());
        result.replace_range(l_bracket_offset.., "");
    }
    if &result == "." {
        // probably a instantiation, the token should be what we're instatiating
        let mut char_iter = text.chars();
        let mut token = String::new();
        for _ in 0..text.pos_to_char(&pos) {
            char_iter.next();
        }
        let mut c = char_iter.prev();

        // go to the last semicolon
        while c.is_some() && (c.unwrap() != ';') {
            c = char_iter.prev();
        }
        // go the the start of the next symbol
        while c.is_some() && !(c.unwrap().is_alphanumeric() || c.unwrap() == '_') {
            c = char_iter.next();
        }
        // then extract the next symbol
        while c.is_some() && (c.unwrap().is_alphanumeric() || c.unwrap() == '_') {
            token.push(c.unwrap());
            c = char_iter.next();
        }
        token
    } else {
        result
    }
}

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();

    // 遍历 hdlparam 中所有的 modules，匹配符合 prefix 前缀的（不区分大小写）
    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();

            let (insert_text, module_profile, define_info) = if file_type == "primitives" {
                // TODO: 支持原语
                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_vhdl_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 item = CompletionItem {
                label: module.name.to_string(),
                detail: Some(detail),
                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
}

/// 实现 vhdl 例化补全的代码片段
fn make_instantiation_code(module: &crate::core::hdlparam::Module) -> String {
    // TODO: 显性和隐性例化
    let mut snippet_codes = Vec::<String>::new();
    let mut placeholder_id: u32 = 1;
    
    snippet_codes.push(format!("u_{} : {}\n", module.name, module.name));

    // 2001 style，先计算出 generic 和 port，然后加入总体例化样板中
    let params_length = module.params.len();
    let ports_length = module.ports.len();

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

        let max_param_name = module.params.iter().map(|param| param.name.len()).max().unwrap_or(0);
        let mut i: usize = 0;
        for generic in &module.params {
            let n_padding = " ".repeat(max_param_name - generic.name.len() + 1);
            let placeholder_init = format!("${{{}:{}}}", placeholder_id, generic.init);
            snippet_codes.push(format!("\t{}{} => {}", generic.name, n_padding, placeholder_init));
            placeholder_id += 1;
            if i < params_length - 1 {
                snippet_codes.push(",\n".to_string());
            }
            i += 1;
        }
        snippet_codes.push(")\n".to_string());
    }

    if ports_length > 0 {
        snippet_codes.push("port map(\n\t-- ports\n".to_string());

        let max_port_name = module.ports.iter().map(|port| port.name.len()).max().unwrap_or(0);
        let mut i: usize = 0;

        for port in &module.ports {
            let n_padding = " ".repeat(max_port_name - port.name.len() + 1);
            let placeholder_name = format!("${{{}:{}}}", placeholder_id, port.name);
            snippet_codes.push(format!("\t{}{} => {}", port.name, n_padding, placeholder_name));
            placeholder_id += 1;
            if i < ports_length - 1 {
                snippet_codes.push(",\n".to_string());
            }
            i += 1;
        }
        snippet_codes.push("\n);\n".to_string());
    }

    snippet_codes.join("")
}