use std::borrow::Cow;
use std::str::FromStr;
use std::{fs, future};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use log::info;
use ropey::Rope;
use serde::{Deserialize, Serialize};
use tower_lsp::jsonrpc::Result;
use tower_lsp::lsp_types::*;
use vhdl_lang::Project;

use crate::core::cache_storage::CacheResult;
use crate::core::hdlparam::FastHdlparam;
use crate::core::sv_parser::make_fast_from_syntaxtree;

use crate::core::vhdl_parser::make_fast_from_units;
use crate::core::scope_tree::common::VhdlProject;
use crate::{core, utils::*};
use crate::server::Backend;
use crate::sources::recovery_sv_parse_with_retry;


#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct DoFastApiRequestParams {
    path: String,
    file_type: String,
    tool_chain: String
}


#[derive(Clone)]
pub struct DoFastApi;

impl <'a>tower_lsp::jsonrpc::Method<&'a Arc<Backend>, (DoFastApiRequestParams, ), Result<FastHdlparam>> for DoFastApi {
    type Future = future::Ready<Result<FastHdlparam>>;
    fn invoke(&self, _server: &'a Arc<Backend>, _params: (DoFastApiRequestParams, )) -> Self::Future {        
        let request_param = _params.0;
        let path = request_param.path;
        let file_type = request_param.file_type;
        let tool_chain = request_param.tool_chain;
        let hdlparam = do_fast(path, file_type, tool_chain, _server);
        future::ready(hdlparam)
    }
}


#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct SyncFastApiRequestParams {
    path: String,
    file_type: String,
    tool_chain: String
}

#[derive(Clone)]
pub struct SyncFastApi;

impl <'a>tower_lsp::jsonrpc::Method<&'a Arc<Backend>, (SyncFastApiRequestParams, ), Result<FastHdlparam>> for SyncFastApi {
    type Future = future::Ready<Result<FastHdlparam>>;
    fn invoke(&self, _server: &'a Arc<Backend>, _params: (SyncFastApiRequestParams, )) -> Self::Future {        
        let request_param = _params.0;
        let path = request_param.path;
        let file_type = request_param.file_type;
        let tool_chain = request_param.tool_chain;
        let hdlparam = sync_fast(path, file_type, tool_chain, _server);
        future::ready(hdlparam)
    }
}

fn make_textdocumenitem_from_path(path_buf: &PathBuf) -> Option<TextDocumentItem> {
    if let Ok(url) = Url::from_file_path(path_buf) {
        if let Ok(text) = fs::read_to_string(path_buf) {
            let language_id = get_language_id_by_uri(&url);
            return Some(TextDocumentItem::new(url, language_id, -1, text));
        }
    }
    None
}

/// 前端交互接口： do_fast，输入文件路径，计算出对应的 fast 结构
pub fn do_fast(
    path: String,
    file_type: String,
    tool_chain: String,
    backend: &Arc<Backend>
) -> Result<FastHdlparam> {
    info!("parse fast {:?}, type: {:?}, toolchain: {:?}", path, file_type, tool_chain);
    
    // 根据 file_type 和 tool_chain 计算正确的 path
    let path = {
        if file_type == "ip" && tool_chain == "xilinx" {
            let pathbuf = PathBuf::from_str(&path).unwrap();
            let basename = pathbuf.file_name().unwrap().to_str().unwrap();
            format!("{}/synth/{}.vhd", path, basename)
        } else {
            path
        }
    };

    let language_id = get_language_id_by_path_str(&path);

    let parse_fast_by_language_id = |language_id: &str| {
        match language_id {
            "vhdl" => {
                do_vhdl_fast(
                    &path,
                    &file_type,
                    &tool_chain,
                    backend
                )
            }
    
            "verilog" | "systemverilog" => {
                do_sv_fast(
                    &path,
                    &file_type,
                    &tool_chain,
                    backend
                )
            }
    
            _ => Err(tower_lsp::jsonrpc::Error {
                code: tower_lsp::jsonrpc::ErrorCode::InvalidRequest,
                message: Cow::Owned(format!("invalid file: {path}, expect vhdl, verilog or system verilog!")),
                data: None
            })
        }
    };

    let path_buf = PathBuf::from_str(&path).unwrap();
    // 做缓存优化
    let cache = &backend.server.cache;
    match cache.try_get_fast_cache(&path_buf) {
        // 找到缓存，直接返回
        CacheResult::Ok(fast) => {
            return Ok(fast);
        }

        // cache 没找到，那么就需要计算并存入
        CacheResult::CacheNotFound => {
            match parse_fast_by_language_id(&language_id) {
                Ok(fast) => {
                    cache.update_cache(&path_buf, fast.clone());
                    return Ok(fast);
                },
                Err(err) => Err(err)
            }
        }

        // 不需要缓存的文件正常进行 fast 计算即可
        _ => {
            parse_fast_by_language_id(&language_id)
        }
    }
}

fn do_sv_fast(
    path: &str,
    #[allow(unused)]
    file_type: &str,
    #[allow(unused)]
    tool_chain: &str,
    backend: &Arc<Backend>
) -> Result<FastHdlparam> {
    let path_buf = PathBuf::from(&path);

    // 从 pathbuf 中读取文本并作为 TextDocumentItem 打开
    let doc = match make_textdocumenitem_from_path(&path_buf) {
        Some(doc) => doc,
        None => {
            let api_error = tower_lsp::jsonrpc::Error {
                code: tower_lsp::jsonrpc::ErrorCode::InvalidParams,
                message: Cow::Owned(format!("cannot make doc from path : {path}")),
                data: None
            };
        
            return Err(api_error);
        }
    };

    let uri = doc.uri;
    let text = Rope::from(doc.text);
    // fast 解析不需要 include
    let includes: Vec<PathBuf> = Vec::new();
    
    let parse_result = recovery_sv_parse_with_retry(
        &text, 
        &uri, 
        &None,
        &includes
    );

    let sources = &backend.server.srcs;
    if let Some((syntax_tree, parse_result)) = parse_result {
        if let Ok(mut fast) = make_fast_from_syntaxtree(&syntax_tree, &path_buf) {
            fast.file_type = file_type.to_string();
            let hdl_param = sources.hdl_param.clone();
            hdl_param.update_hdl_file(
                path.to_string(),
                fast.clone(),
                parse_result,
                Some(crate::sources::AstLike::Svlog(syntax_tree))
            );
            return Ok(fast);
        }
    }

    Err(tower_lsp::jsonrpc::Error {
        code: tower_lsp::jsonrpc::ErrorCode::ParseError,
        message: Cow::Owned(format!("error happen when parse {path} [do_sv_fast]")),
        data: None
    })
}

fn do_vhdl_fast(
    path: &str,
    #[allow(unused)]
    file_type: &str,
    #[allow(unused)]
    tool_chain: &str,
    backend: &Arc<Backend>
) -> Result<FastHdlparam> {
    let sources = &backend.server.srcs;
    let pathbuf = PathBuf::from_str(path).unwrap();
    let hdl_param = sources.hdl_param.clone();
    let vhdl_project = sources.vhdl_project.clone();

    // TODO: 支持对于 synth 下的 vhdl 文件的解析，从而提供更加丰富的 IP 支持
    if file_type == "ip" {
        match tool_chain {
            // 此时的 pathbuf 类似 {ip_name}/synth/{ip_name}.vhd
            "xilinx" => {
                // 如果 ip 描述文件存在，则解析它，否则，创建空的写入
                // IP 描述文件一般都不会很大，所以不需要缓存
                if !pathbuf.exists() {
                    let ip_name = pathbuf.file_name().unwrap().to_str().unwrap();
                    let ip_name = ip_name.strip_suffix(".vhd").unwrap();
                    let fake_content = vec![
                        core::hdlparam::Module {
                            name: ip_name.to_string(),
                            arch_name: "".to_string(),
                            params: vec![],
                            ports: vec![],
                            instances: vec![],
                            range: core::hdlparam::Range::default()
                        }
                    ];
                    let ip_fast = core::hdlparam::FastHdlparam {
                        fast_macro: core::hdlparam::Macro {
                            includes: vec![],
                            defines: vec![],
                            errors: vec![],
                            invalid: vec![]
                        },
                        file_type: "ip".to_string(),
                        content: fake_content
                    };

                    hdl_param.update_hdl_file(
                        path.to_string(),
                        ip_fast.clone(),
                        sv_parser::common::ParseResult::new(),
                        None
                    );
                    return Ok(ip_fast);
                } else if let Some(vhdl_project) = &mut *vhdl_project.write().unwrap() {
                    vhdl_project.config_file_strs.push(format!("{:?}", pathbuf));
                    let mut messages = Vec::new();
                    let config_str = format!(
                        r#"
                            [libraries]
                            digital_lsp.files = [{}]
                        "#, vhdl_project.config_file_strs.join(",")
                    );
                    let config = vhdl_lang::Config::from_str(&config_str, Path::new(""));
                    if let Ok(mut config) = config {
                        config.append(&vhdl_project.std_config, &mut messages);
                        let mut project = Project::from_config(config, &mut messages);
                        project.analyse();
                        *vhdl_project = VhdlProject { project, std_config: vhdl_project.std_config.clone(), config_file_strs: vhdl_project.config_file_strs.clone() };
                    }

                    let arch_and_entity = vhdl_project.project.get_analyzed_units(&pathbuf);

                    if let Some(mut fast) = make_fast_from_units(arch_and_entity) {
                        fast.file_type = file_type.to_string();
                        // IP 不需要内部的 instance，其实现是加密的，只需要暴露 module 的接口即可
                        // 所以此处需要清空所有的 module 中的 instance
                        for module in &mut fast.content {
                            module.instances.clear();
                        }
                        // 为了兼容 verilog 而制作的空的
                        hdl_param.update_hdl_file(
                            path.to_string(),
                            fast.clone(),
                            sv_parser::common::ParseResult::new(),
                            None
                        );
                        return Ok(fast);
                    }
                } else {
                    return Err(tower_lsp::jsonrpc::Error {
                        code: tower_lsp::jsonrpc::ErrorCode::ParseError,
                        message: Cow::Owned(format!("error happen when parse {path} in [do_vhdl_fast]")),
                        data: None
                    });
                }
            },
            _ => {}
        }
    }

    // 没有特殊情况，则正常解析并写入
    if let Some(vhdl_project) = &mut *vhdl_project.write().unwrap() {
        vhdl_project.config_file_strs.push(format!("{:?}", pathbuf));
        let mut messages = Vec::new();
        let config_str = format!(
            r#"
                [libraries]
                digital_lsp.files = [{}]
            "#, vhdl_project.config_file_strs.join(",")
        );
        let config = vhdl_lang::Config::from_str(&config_str, Path::new(""));
        if let Ok(mut config) = config {
            config.append(&vhdl_project.std_config, &mut messages);
            let mut project = Project::from_config(config, &mut messages);
            project.analyse();
            *vhdl_project = VhdlProject { project, std_config: vhdl_project.std_config.clone(), config_file_strs: vhdl_project.config_file_strs.clone() };
        }

        let arch_and_entity = vhdl_project.project.get_analyzed_units(&pathbuf);
        if let Some(mut fast) = make_fast_from_units(arch_and_entity) {
            fast.file_type = file_type.to_string();
            // for module in &fast.content {
            //     if module.name == "None" {
            //         info!("debug, module : {:?}, path: {:?}", module, pathbuf);
            //     }
            // }
            hdl_param.update_hdl_file(
                path.to_string(),
                fast.clone(),
                sv_parser::common::ParseResult::new(),
                None
            );
            return Ok(fast);
        }
    }

    Err(tower_lsp::jsonrpc::Error {
        code: tower_lsp::jsonrpc::ErrorCode::ParseError,
        message: Cow::Owned(format!("error happen when parse {path} in [do_vhdl_fast]")),
        data: None
    })
}

/// 将后端新的 fast 同步到前端去
pub fn sync_fast(
    path: String,
    file_type: String,
    tool_chain: String,
    backend: &Arc<Backend>
) -> Result<FastHdlparam> {
    // 根据 file_type 和 tool_chain 计算正确的 path
    let path = {
        if file_type == "ip" && tool_chain == "xilinx" {
            let pathbuf = PathBuf::from_str(&path).unwrap();
            let basename = pathbuf.file_name().unwrap().to_str().unwrap();
            format!("{}/synth/{}.vhd", path, basename)
        } else {
            path
        }
    };

    {
        let uri = Url::from_file_path(path.to_string()).unwrap();
        let path_string = from_uri_to_escape_path_string(&uri).unwrap();

        if let Some(source_handle) = backend.server.srcs.get_source(&path_string) {
            let _unused = source_handle.read().unwrap();
        }
    }

    let hdl_param = backend.server.srcs.hdl_param.clone();
    // TODO: 检查加锁的有效性，因为前端在请求该方法时，后端可能仍然在计算
    let path_to_hdl_file = hdl_param.path_to_hdl_file.read().unwrap();
    if let Some(hdl_file) = path_to_hdl_file.get(&path) {
        let fast = hdl_file.fast.clone();
        Ok(fast)
    } else {
        do_fast(path, file_type, tool_chain, backend)
    }
}