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

use log::info;
use regex::Regex;
use ropey::Rope;
use tower_lsp::lsp_types::*;
use crate::{core::hdlparam::{Instance, Module}, hover::{BracketMatchResult, BracketMatcher}, server::LspServer};
use crate::core::scope_tree::common::*;

use super::{from_lsp_pos, from_uri_to_escape_path_string, get_definition_token, get_language_id_by_uri, to_escape_path};

pub fn hover(server: &LspServer, params: &HoverParams) -> Option<Hover> {
    let uri = &params.text_document_position_params.text_document.uri;
    let pos: Position = params.text_document_position_params.position;

    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(pos.line as usize);

    let project = server.srcs.vhdl_project.read().ok()?;
    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);
    let source = global_project.project.get_source(&escape_path)?;

    let ent = global_project.project.find_declaration(&source, from_lsp_pos(pos))?;
    let value = global_project.project.format_declaration(ent)?;

    Some(Hover {
        contents: HoverContents::Markup(MarkupContent {
            kind: MarkupKind::Markdown,
            value: format!("```vhdl\n{value}\n```"),
        }),
        range: None,
    })

    // // match `include
    // if let Some(hover) = hover_include(doc, &line_text, pos, &language_id) {
    //     return Some(hover);
    // }

    // // match macro
    // if let Some(hover) = hover_macro(server, &line_text, pos, &language_id) {
    //     return Some(hover);
    // }
    
    // let scope_tree = server.srcs.scope_tree.read().ok()?;                
    // let global_scope = scope_tree.as_ref().unwrap();

    // let symbol_definition: GenericDec = global_scope
    //     .get_definition(&token, file.text.pos_to_byte(&pos), doc)?;

    // // match positional port param
    // if let Some(hover) = hover_position_port_param(server, &line_text, doc, pos, &language_id) {
    //     return Some(hover);
    // }

    // // match module name
    // if let Some(hover) = hover_module_declaration(server, &token, &symbol_definition, &language_id) {
    //     return Some(hover);
    // }

    // match 正常 symbol
    // if let Some(hover) = hover_common_symbol(server, &token, &symbol_definition, &file, doc, pos, &language_id) {
    //     return Some(hover);
    // }

    // // match digit 5'b00110
    // if let Some(hover) = hover_format_digit(&line_text, pos, &language_id) {
    //     return Some(hover);
    // }

    // None
}

fn make_hover_with_comment(doc: &Rope, line: usize, language_id: &str, exclude_code: bool) -> Option<Hover> {
    if line == 0 {
        let language_string = LanguageString {
            language: language_id.to_string(),
            value: doc.line(line).to_string()
        };
        let markdown = MarkedString::LanguageString(language_string);
        
        return Some(Hover {
            contents: HoverContents::Scalar(markdown),
            range: None,
        });
    }
    let mut hover: Vec<String> = Vec::new();
    let mut multiline: bool = false;
    let mut valid: bool = true;
    let mut current: String = doc.line(line).to_string();

    let ltrim: String = " ".repeat(current.len() - current.trim_start().len());
    let mut line_idx = line;

    // 寻找周围的注释
    while valid {
        hover.push(current.clone());
        line_idx -= 1;
        valid = false;
        if line_idx > 0 {
            current = doc.line(line_idx).to_string();
            let currentl = current.clone().trim_start().to_owned();
            let currentr = current.clone().trim_end().to_owned();
            if currentl.starts_with("/*") && currentr.ends_with("*/") {
                valid = true;
            } else if currentr.ends_with("*/") {
                multiline = true;
                valid = true;
            } else if currentl.starts_with("/*") {
                multiline = false;
                valid = true;
            } else {
                valid = currentl.starts_with("--") || multiline;
            }
        }
    }
    hover.reverse();

    let multi_space_regex = Regex::new(r"\s+").unwrap();
    let line_handler = |line: &str| -> String {
        let line = multi_space_regex.replace_all(line.trim(), " ");
        let mut line = line.into_owned();

        if line.starts_with("/*") {
            line = line.strip_prefix("/*").unwrap().trim().to_string();
        } else if line.starts_with("--") {
            line = line.strip_prefix("--").unwrap().trim().to_string();
        }

        if line.ends_with("*/") {
            line = line.strip_suffix("*/").unwrap().trim().to_string();
        }

        return format!("{}\n", line);
    };

    let line_comment_extractor = |line: &str| -> Option<(String, String)> {
        let line = line.trim();
        if line.contains("--") {
            let comment_start = line.split_once("--");
            match comment_start {
                Some((code, comment)) => {
                    let code = code.trim().to_string();
                    let comment = comment.trim().to_string();
                    Some((code, comment))
                },
                None => None
            }
        } else if line.contains("/*") {
            let comment_start = line.split_once("/*");
            match comment_start {
                Some((code, comment)) => {
                    let code = code.trim().to_string();
                    let comment = comment.trim().strip_suffix("*/").unwrap_or("").to_string();
                    Some((code, comment))
                },
                None => None
            }
        } else {
            return None;
        }
    };

    // 所有的 markdown
    let mut comment_markdowns = Vec::<MarkedString>::new();

    for (line_no, line_text) in hover.iter().enumerate() {
        let line_hover = if let Some(stripped) = line_text.strip_prefix(&ltrim) {
            line_handler(stripped)
        } else {
            line_handler(&line_text)
        };

        if line_no == hover.len() - 1 {
            // 最后一个为定义所在的一行
            if let Some((code, comment)) = line_comment_extractor(&line_text) {
                comment_markdowns.push(MarkedString::LanguageString(LanguageString {
                    language: language_id.to_string(),
                    value: code
                }));
                comment_markdowns.insert(0, MarkedString::String(comment.trim().to_string()));
            } else {
                // 这行只有代码，没有注释
                comment_markdowns.push(MarkedString::LanguageString(LanguageString {
                    language: language_id.to_string(),
                    value: line_hover
                }));
            }
        } else {
            // 否则，都是上方的注释
            comment_markdowns.push(MarkedString::String(line_hover.trim().to_string()));
        }
    }

    // 合并其中的 markdown
    let mut merge_markdowns = Vec::<MarkedString>::new();
    let mut string_buffer = String::new();
    for (_, md) in comment_markdowns.iter().enumerate() {
        match md {
            MarkedString::String(markdown_string) => {
                string_buffer.push_str(format!("{}\n", markdown_string.trim()).as_str());
            }
            MarkedString::LanguageString(code) => {
                if !string_buffer.is_empty() {
                    merge_markdowns.push(MarkedString::String(string_buffer.to_string()));
                    string_buffer.clear();   
                }

                if !exclude_code {
                    merge_markdowns.push(MarkedString::LanguageString(code.to_owned()));
                }
            }
        }
    }

    if comment_markdowns.len() > 0 {
        return Some(Hover {
            contents: HoverContents::Array(merge_markdowns),
            range: None
        });
    }

    None
}



fn hover_common_symbol(
    #[allow(unused)]
    server: &LspServer,
    #[allow(unused)]
    token: &String,
    symbol_definition: &GenericDec,
    file: &RwLockReadGuard<'_, crate::sources::Source>,
    doc: &Url,
    #[allow(unused)]
    pos: Position,
    language_id: &String
) -> Option<Hover> {
    // 根据 symbol 的类别进行额外的判断
    match symbol_definition.def_type {
        DefinitionType::ModuleInstantiation => {
            let hdlparam = server.srcs.hdl_param.clone();
            let pathbuf = PathBuf::from_str(doc.path()).unwrap();
            let pathbuf = to_escape_path(&pathbuf);
            let path_string = pathbuf.to_str().unwrap().replace("\\", "/");

            let find_name_condition = |_: &Module, instance: &Instance| {
                symbol_definition.ident == instance.name
            };


            if let Some(instance) = hdlparam.walk_instantiation(&path_string, find_name_condition) {
                info!("instance {:?}", instance);
                let def_line = file.text.byte_to_line(symbol_definition.byte_idx());
                let mut markdown_comment = match make_hover_with_comment(&file.text, def_line, &language_id, true) {
                    Some(hover) => {
                        match hover.contents {
                            HoverContents::Array(array) => array,
                            _ => Vec::<MarkedString>::new()
                        }
                    },
                    None => Vec::<MarkedString>::new()
                };
                
                // 扫描到右括号
                let mut current_line = def_line;
                let mut buffer = String::new();
                let len_lines = file.text.len_lines();
                let mut matcher = BracketMatcher::new();

                loop {
                    if current_line >= len_lines {
                        break;
                    }
                    let line_text = file.text.line(current_line).to_string();
                    buffer.push_str(&line_text);
                    match matcher.consume_string(&line_text) {
                        BracketMatchResult::Invalid | BracketMatchResult::Complete => break,
                        BracketMatchResult::Valid => {}
                    }

                    current_line += 1;
                }
                markdown_comment.push(MarkedString::LanguageString(LanguageString {
                    language: language_id.to_string(),
                    value: buffer
                }));
                
                return Some(Hover { contents: HoverContents::Array(markdown_comment), range: None });
            }
        }

        _ => {}
    };

    let def_line = file.text.byte_to_line(symbol_definition.byte_idx());
    make_hover_with_comment(&file.text, def_line, &language_id, false)
}

