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

use log::info;
use regex::Regex;
use ropey::RopeSlice;
use tower_lsp::lsp_types::{GotoDefinitionResponse, LocationLink, Position, Range, Url};

use crate::{core::{self, hdlparam::{self, FastHdlparam}}, server::LspServer, utils::{get_word_range_at_position, resolve_path, to_escape_path}};

/// 跳转到 include 的文件
pub fn goto_include_definition(uri: &Url, line: &RopeSlice, pos: Position) -> Option<GotoDefinitionResponse> {
    let line_text = line.as_str().unwrap_or("");
    if line_text.trim().starts_with("`include") {
        let character = pos.character as usize;
        let first_quote_idx = line_text.find("\"");
        let last_quote_idx = line_text.rfind("\"");
        
        if first_quote_idx.is_none() || last_quote_idx.is_none() {
            return None;
        }

        let first_quote_idx = first_quote_idx.unwrap();
        let last_quote_idx = last_quote_idx.unwrap();
        if character >= first_quote_idx && character <= last_quote_idx {
            let mut path_string = &line_text[(first_quote_idx + 1) .. last_quote_idx];
            if path_string.starts_with("./") || path_string.starts_with(".\\") {
                path_string = &path_string[2 ..];
            }

            // 路径转换
            let path = match PathBuf::from_str(uri.path()) {
                Ok(path) => path,
                Err(error) => {
                    info!("error happen in <goto_include_definition>: {:?}", error);
                    return None;
                }
            };
            let escape_path = to_escape_path(&path);
            let escape_path = escape_path.to_str().unwrap_or("");
            if escape_path.len() == 0 {
                return None;
            }

            if let Some(abs_path) = resolve_path(escape_path, path_string) {
                let target_uri = match Url::from_file_path(abs_path.as_path()) {
                    Ok(uri) => uri,
                    Err(_) => return None
                };

                let origin_selection_range = Range::new(
                    Position { line: pos.line, character: first_quote_idx as u32 },
                    Position { line: pos.line, character: (last_quote_idx + 1) as u32 }
                );

                let target_position = Position { line: 0, character: 0 };
                let target_range = Range::new(target_position, target_position);

                let link = vec![LocationLink {
                    target_uri,
                    origin_selection_range: Some(origin_selection_range),
                    target_range,
                    target_selection_range: target_range
                }];
                let links = GotoDefinitionResponse::Link(link);
                return Some(links);
            }

        }
    }

    None
}


/// 跳转到宏定义
pub fn goto_macro_definition(server: &LspServer, line: &RopeSlice, pos: Position) -> Option<GotoDefinitionResponse> {
    let macro_text_regex = Regex::new(r"[`_0-9a-zA-Z]").unwrap();
    if let Some((macro_text, range)) = get_word_range_at_position(line, pos, macro_text_regex) {
        if macro_text.starts_with("`") {
            if let Some((macro_define, define_path)) = server.find_macros(&macro_text) {
                let define_path = PathBuf::from_str(&define_path).unwrap();
                let target_uri = match Url::from_file_path(define_path) {
                    Ok(uri) => uri,
                    Err(_) => return None
                };
                
                let target_range = macro_define.range.to_lsp_range();
                let link = vec![LocationLink {
                    target_uri,
                    origin_selection_range: Some(range),
                    target_range: target_range,
                    target_selection_range: target_range
                }];
                let links = GotoDefinitionResponse::Link(link);
                return Some(links);
            }
        }
    }

    None
}


fn goto_instantiation<'a>(
    server: &LspServer,
    fast: &'a FastHdlparam,
    token_name: &str,
    pos: &Position,
    range: &Range
) -> Option<GotoDefinitionResponse> {
    for module in &fast.content {
        for instance in &module.instances {
            if let Some(param_range) = &instance.instparams {
                // let in_scope = compare_pos(&range.start, pos) != 1 && compare_pos(pos, &range.end) != 1;
                // info!("pos: {pos:?}, param_range: {range:?}, in_scope: {in_scope:?}");
                if param_range.contains(pos) {
                    let module = match server.srcs.hdl_param.find_module_by_name(&instance.inst_type) {
                        Some(module) => module,
                        None => return None
                    };
                    for param in &module.params {
                        if token_name == param.name {
                            let def_path = server.srcs.hdl_param.find_module_definition_path(&module.name).unwrap();
                            let target_uri = Url::from_file_path(def_path).unwrap();
                            let target_range = param.range.clone();

                            let file_type = server.srcs.hdl_param.find_file_type_by_module_name(&instance.inst_type);
                            let target_range = match file_type.as_str() {
                                "common" => {
                                    target_range.to_lsp_range()
                                }
                                "ip" => {
                                    let mut target_range = target_range.clone();
                                    target_range.affine(-1, -1).to_lsp_range()
                                }
                                "primitives" => {
                                    target_range.to_lsp_range()
                                }
                                _ => {
                                    target_range.to_lsp_range()
                                }
                            };

                            let link = vec![LocationLink {
                                target_uri,
                                origin_selection_range: Some(range.clone()),
                                target_range: target_range,
                                target_selection_range: target_range
                            }];
                            let links = GotoDefinitionResponse::Link(link);
                            return Some(links);
                        }
                    }
                    return None;
                }       
            }

            if let Some(port_range) = &instance.instports {
                // let in_scope = compare_pos(&range.start, pos) != 1 && compare_pos(pos, &range.end) != 1;
                // info!("pos: {pos:?}, port_range: {range:?}, in_scope: {in_scope:?}");
                if port_range.contains(pos) {
                    let module = match server.srcs.hdl_param.find_module_by_name(&instance.inst_type) {
                        Some(module) => module,
                        None => return None
                    };
                    for port in &module.ports {
                        if token_name == port.name {
                            let def_path = server.srcs.hdl_param.find_module_definition_path(&module.name).unwrap();
                            let target_uri = Url::from_file_path(def_path).unwrap();
                            let target_range = port.range.clone();

                            let file_type = server.srcs.hdl_param.find_file_type_by_module_name(&instance.inst_type);
                            let target_range = match file_type.as_str() {
                                "common" => {
                                    target_range.to_lsp_range()
                                }
                                "ip" => {
                                    let mut target_range = target_range.clone();
                                    target_range.affine(-1, -1).to_lsp_range()
                                }
                                "primitives" => {
                                    target_range.to_lsp_range()
                                }
                                _ => {
                                    target_range.to_lsp_range()
                                }
                            };
                            
                            let link = vec![LocationLink {
                                target_uri,
                                origin_selection_range: Some(range.clone()),
                                target_range: target_range,
                                target_selection_range: target_range
                            }];
                            let links = GotoDefinitionResponse::Link(link);
                            return Some(links);
                        }
                    }
                    return None;
                }
            }
        }
    }
    
    None
}

pub fn goto_position_port_param_definition(
    server: &LspServer,
    line: &RopeSlice,
    url: &Url,
    pos: Position
) -> Option<GotoDefinitionResponse> {
    let position_port_regex = Regex::new(r"[._0-9a-zA-Z]").unwrap();
    if let Some((name, range)) = get_word_range_at_position(line, pos, position_port_regex) {
        if name.starts_with(".") {
            let name = &name[1..];
            // 进入最近的 scope 寻找
            let fast_map = server.srcs.hdl_param.path_to_hdl_file.read().unwrap();
            let path = PathBuf::from_str(url.path()).unwrap();
            let path = to_escape_path(&path);
            let path_string = path.to_str().unwrap();
            if let Some(hdl_file) = fast_map.get(path_string) {
                // 先找到属于哪一个 module
                let fast = &hdl_file.fast;
                if let Some(definition) = goto_instantiation(server, fast, name, &pos, &range) {
                    return Some(definition);
                }
            }
        }
    }

    None
}

pub fn goto_module_declaration_definition(
    server: &LspServer,
    token_name: &str 
) -> Option<GotoDefinitionResponse> {
    let hdl_param = server.srcs.hdl_param.clone();
    info!("get into goto_module_declaration_definition");

    if let Some((module, file_type, def_path)) = hdl_param.find_module_context_by_name(token_name) {
        match file_type.as_str() {
            "common" => {
                goto_common_module_declaration_definition(
                    server,
                    token_name,
                    &module,
                    &def_path
                )
            },
            "ip" => {
                goto_ip_module_declaration_definition(
                    server,
                    token_name,
                    &module,
                    &def_path
                )
            },
            "primitives" => {
                goto_primitives_module_declaration_definition(
                    server,
                    token_name,
                    &module,
                    &def_path
                )
            },
            _ => None
        }
    } else {
        None
    }
}

fn goto_common_module_declaration_definition(
    #[allow(unused)]
    server: &LspServer,
    #[allow(unused)]
    token_name: &str,
    #[allow(unused)]
    module: &core::hdlparam::Module,
    #[allow(unused)]
    def_path: &str
) -> Option<GotoDefinitionResponse> {
    let target_uri = Url::from_file_path(PathBuf::from_str(&def_path).unwrap()).unwrap();
        
    let target_range = module.range.clone();
    let target_range = target_range.to_lsp_range();
    
    let link = vec![LocationLink {
        target_uri,
        origin_selection_range: None,
        target_range: target_range,
        target_selection_range: target_range
    }];
    let links = GotoDefinitionResponse::Link(link);
    Some(links)
}

fn goto_ip_module_declaration_definition(
    #[allow(unused)]
    server: &LspServer,
    #[allow(unused)]
    token_name: &str,
    #[allow(unused)]
    module: &core::hdlparam::Module,
    #[allow(unused)]
    def_path: &str
) -> Option<GotoDefinitionResponse> {
    let pathbuf = PathBuf::from_str(def_path).unwrap();
    if pathbuf.exists() {
        let target_uri = Url::from_file_path(PathBuf::from_str(&def_path).unwrap()).unwrap();
        
        let mut target_range = module.range.clone();
        let target_range = target_range.affine(-1, -1).to_lsp_range();
        
        let link = vec![LocationLink {
            target_uri,
            origin_selection_range: None,
            target_range: target_range,
            target_selection_range: target_range
        }];
        let links = GotoDefinitionResponse::Link(link);
        Some(links)
    } else {
        None
    }
}

fn goto_primitives_module_declaration_definition(
    #[allow(unused)]
    server: &LspServer,
    #[allow(unused)]
    token_name: &str,
    #[allow(unused)]
    module: &core::hdlparam::Module,
    #[allow(unused)]
    def_path: &str
) -> Option<GotoDefinitionResponse> {

    None
}