use std::collections::HashMap;
use tree_sitter::{Language, Query, QueryCursor};
use tree_sitter_tags::{TagsConfiguration, TagsContext};

/// 单个符号（定义或引用）
pub struct ParsedSymbol {
    pub name: String,
    /// 语法类型名：function / method / class / struct / interface / call ... 由 grammar 的 tags 决定
    pub kind: String,
    /// true=定义，false=引用
    pub is_definition: bool,
    /// 1 基行号
    pub start_line: u32,
    pub end_line: u32,
    /// 定义所在行文本（截断），作为粗略签名
    pub signature: String,
}

/// 单个文件的解析结果
pub struct ParsedFile {
    pub lang: String,
    pub symbols: Vec<ParsedSymbol>,
    pub imports: Vec<String>,
    /// 继承关系：(子类型, 父类型/接口/trait)
    pub inherits: Vec<(String, String)>,
}

/// 一种语言的解析所需材料
struct LangDef {
    name: &'static str,
    language: Language,
    /// 拥有所有权：TS/TSX 需把 javascript 基础 tags 与 typescript 专有 tags 拼接
    tags_query: String,
    import_query: &'static str,
    /// 继承关系查询（捕获 @child 与 @parent）；空串表示该语言不抽继承
    inherit_query: &'static str,
}

/// 补充捕获箭头函数/函数表达式常量定义：`const x = () => {}` / `export const x = function(){}`
/// 官方 tags.scm 只认 function_declaration，不认这种赋值式定义；前端代码大量使用，必须补。
const ARROW_CONST_TAGS: &str = r#"
(variable_declarator
  name: (identifier) @name
  value: [(arrow_function) (function_expression)]) @definition.function
"#;

/// TypeScript 专有定义：类型别名与枚举（官方 tags 未覆盖）。仅 TS/TSX 语法有这些节点。
const TS_EXTRA_TAGS: &str = r#"
(type_alias_declaration name: (type_identifier) @name) @definition.type
(enum_declaration name: (identifier) @name) @definition.enum
"#;

// ---- 各语言的导入抽取查询（捕获名统一为 @module；不使用谓词，避免额外求值）----

const JS_IMPORTS: &str = r#"
(import_statement source: (string) @module)
(export_statement source: (string) @module)
"#;

const PY_IMPORTS: &str = r#"
(import_statement name: (dotted_name) @module)
(import_statement name: (aliased_import name: (dotted_name) @module))
(import_from_statement module_name: (dotted_name) @module)
(import_from_statement module_name: (relative_import) @module)
"#;

const RUST_IMPORTS: &str = r#"
(use_declaration argument: (_) @module)
"#;

const GO_IMPORTS: &str = r#"
(import_spec path: (interpreted_string_literal) @module)
"#;

// 以下为 P2-2 扩充语言的导入查询（best-effort：节点名不符时 Query 编译失败，
// extract_imports 优雅返回空，不影响符号抽取）
const PHP_IMPORTS: &str = r#"(namespace_use_declaration (namespace_use_clause (qualified_name) @module))"#;
const JAVA_IMPORTS: &str = r#"(import_declaration (scoped_identifier) @module)"#;
const CS_IMPORTS: &str = r#"(using_directive (qualified_name) @module)"#;
const C_IMPORTS: &str = r#"(preproc_include path: (_) @module)"#;
const RUBY_IMPORTS: &str = "";

// 继承关系查询（best-effort，捕获 @child / @parent；节点名不符则整条失败优雅降级）
const TS_INHERIT: &str = r#"
(class_declaration name: (type_identifier) @child (class_heritage (extends_clause (_) @parent)))
(class_declaration name: (type_identifier) @child (class_heritage (implements_clause (type_identifier) @parent)))
(interface_declaration name: (type_identifier) @child (extends_type_clause (type_identifier) @parent))
"#;
const JS_INHERIT: &str = r#"
(class_declaration name: (identifier) @child (class_heritage (identifier) @parent))
"#;
const JAVA_INHERIT: &str = r#"
(class_declaration name: (identifier) @child superclass: (superclass (type_identifier) @parent))
(class_declaration name: (identifier) @child interfaces: (super_interfaces (type_list (type_identifier) @parent)))
(interface_declaration name: (identifier) @child (extends_interfaces (type_list (type_identifier) @parent)))
"#;
const PY_INHERIT: &str = r#"
(class_definition name: (identifier) @child superclasses: (argument_list (identifier) @parent))
"#;
const CS_INHERIT: &str = r#"
(class_declaration name: (identifier) @child (base_list (identifier) @parent))
(interface_declaration name: (identifier) @child (base_list (identifier) @parent))
"#;
const PHP_INHERIT: &str = r#"
(class_declaration name: (name) @child (base_clause (name) @parent))
(class_declaration name: (name) @child (class_interface_clause (name) @parent))
"#;
const CPP_INHERIT: &str = r#"
(class_specifier name: (type_identifier) @child (base_class_clause (type_identifier) @parent))
(struct_specifier name: (type_identifier) @child (base_class_clause (type_identifier) @parent))
"#;
const RUST_INHERIT: &str = r#"
(impl_item trait: (type_identifier) @parent type: (type_identifier) @child)
"#;

/// C# 自带 TAGS_QUERY 与 tree-sitter-tags 0.24 不兼容（构建失败导致无符号），改用内联最小查询
const CS_TAGS: &str = r#"
(class_declaration name: (identifier) @name) @definition.class
(struct_declaration name: (identifier) @name) @definition.struct
(interface_declaration name: (identifier) @name) @definition.interface
(enum_declaration name: (identifier) @name) @definition.enum
(method_declaration name: (identifier) @name) @definition.method
(constructor_declaration name: (identifier) @name) @definition.method
(property_declaration name: (identifier) @name) @definition.property
(namespace_declaration name: (identifier) @name) @definition.module
"#;

/// 按扩展名映射到语言定义；未知扩展返回 None（不索引）
fn lang_for_ext(ext: &str) -> Option<LangDef> {
    // TS/TSX 的 tags 继承自 javascript：必须拼接 js 基础规则（函数/类/方法/调用）
    // + ts 专有规则（接口/类型）+ 箭头常量补充，否则只能认到接口。
    let ts_tags = || {
        format!(
            "{}\n{}\n{}\n{}",
            tree_sitter_javascript::TAGS_QUERY,
            tree_sitter_typescript::TAGS_QUERY,
            ARROW_CONST_TAGS,
            TS_EXTRA_TAGS
        )
    };
    let d = match ext {
        "rs" => LangDef {
            name: "rust",
            language: tree_sitter_rust::LANGUAGE.into(),
            tags_query: tree_sitter_rust::TAGS_QUERY.to_string(),
            import_query: RUST_IMPORTS,
            inherit_query: RUST_INHERIT,
        },
        "js" | "mjs" | "cjs" | "jsx" => LangDef {
            name: "javascript",
            language: tree_sitter_javascript::LANGUAGE.into(),
            tags_query: format!("{}\n{}", tree_sitter_javascript::TAGS_QUERY, ARROW_CONST_TAGS),
            import_query: JS_IMPORTS,
            inherit_query: JS_INHERIT,
        },
        "ts" | "mts" | "cts" => LangDef {
            name: "typescript",
            language: tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
            tags_query: ts_tags(),
            import_query: JS_IMPORTS,
            inherit_query: TS_INHERIT,
        },
        "tsx" => LangDef {
            name: "tsx",
            language: tree_sitter_typescript::LANGUAGE_TSX.into(),
            tags_query: ts_tags(),
            import_query: JS_IMPORTS,
            inherit_query: TS_INHERIT,
        },
        "py" | "pyi" => LangDef {
            name: "python",
            language: tree_sitter_python::LANGUAGE.into(),
            tags_query: tree_sitter_python::TAGS_QUERY.to_string(),
            import_query: PY_IMPORTS,
            inherit_query: PY_INHERIT,
        },
        "go" => LangDef {
            name: "go",
            language: tree_sitter_go::LANGUAGE.into(),
            tags_query: tree_sitter_go::TAGS_QUERY.to_string(),
            import_query: GO_IMPORTS,
            inherit_query: "",
        },
        "php" | "phtml" => LangDef {
            name: "php",
            language: tree_sitter_php::LANGUAGE_PHP.into(),
            tags_query: tree_sitter_php::TAGS_QUERY.to_string(),
            import_query: PHP_IMPORTS,
            inherit_query: PHP_INHERIT,
        },
        "java" => LangDef {
            name: "java",
            language: tree_sitter_java::LANGUAGE.into(),
            tags_query: tree_sitter_java::TAGS_QUERY.to_string(),
            import_query: JAVA_IMPORTS,
            inherit_query: JAVA_INHERIT,
        },
        "cs" => LangDef {
            name: "csharp",
            language: tree_sitter_c_sharp::LANGUAGE.into(),
            tags_query: CS_TAGS.to_string(),
            import_query: CS_IMPORTS,
            inherit_query: CS_INHERIT,
        },
        "c" | "h" => LangDef {
            name: "c",
            language: tree_sitter_c::LANGUAGE.into(),
            tags_query: tree_sitter_c::TAGS_QUERY.to_string(),
            import_query: C_IMPORTS,
            inherit_query: "",
        },
        "cpp" | "cc" | "cxx" | "hpp" | "hh" | "hxx" => LangDef {
            name: "cpp",
            language: tree_sitter_cpp::LANGUAGE.into(),
            tags_query: tree_sitter_cpp::TAGS_QUERY.to_string(),
            import_query: C_IMPORTS,
            inherit_query: CPP_INHERIT,
        },
        "rb" => LangDef {
            name: "ruby",
            language: tree_sitter_ruby::LANGUAGE.into(),
            tags_query: tree_sitter_ruby::TAGS_QUERY.to_string(),
            import_query: RUBY_IMPORTS,
            inherit_query: "",
        },
        _ => return None,
    };
    Some(d)
}

/// 当前支持索引的扩展名
pub fn is_indexable(ext: &str) -> bool {
    lang_for_ext(ext).is_some()
}

/// 复用解析器（在单个后台工作线程内长期持有，缓存各语言的 tags 配置避免重复编译查询）
pub struct Indexer {
    ctx: TagsContext,
    tag_configs: HashMap<&'static str, Option<TagsConfiguration>>,
}

impl Indexer {
    pub fn new() -> Self {
        Self {
            ctx: TagsContext::new(),
            tag_configs: HashMap::new(),
        }
    }

    /// 解析单个文件内容为符号 + 导入；不支持的语言返回 None
    pub fn parse(&mut self, ext: &str, source: &[u8]) -> Option<ParsedFile> {
        let def = lang_for_ext(ext)?;
        if !self.tag_configs.contains_key(def.name) {
            // 构建失败（查询与语法不匹配等）记为 None，后续同语言不再重试
            let cfg = TagsConfiguration::new(def.language.clone(), &def.tags_query, "").ok();
            self.tag_configs.insert(def.name, cfg);
        }
        let config = self.tag_configs.get(def.name).and_then(|o| o.as_ref())?;

        let mut symbols = Vec::new();
        if let Ok((tags, _)) = self.ctx.generate_tags(config, source, None) {
            // tag.span 只是名字的位置；定义整体范围用 tag.range（字节范围）换算行号
            let line_starts = compute_line_starts(source);
            for tag in tags.flatten() {
                let name = match std::str::from_utf8(&source[tag.name_range.clone()]) {
                    Ok(s) if !s.is_empty() => s,
                    _ => continue,
                };
                let kind = config.syntax_type_name(tag.syntax_type_id).to_string();
                let start_line = line_of(&line_starts, tag.range.start);
                let last_byte = tag.range.end.saturating_sub(1).max(tag.range.start);
                let end_line = line_of(&line_starts, last_byte);
                // 定义取多行签名头部（参数换行/装饰器不丢）；引用取所在行
                let signature = if tag.is_definition {
                    let def_text = std::str::from_utf8(&source[tag.range.clone()]).unwrap_or("");
                    extract_signature(def_text, def.name == "python")
                } else {
                    let line = std::str::from_utf8(&source[tag.line_range.clone()])
                        .unwrap_or("")
                        .trim();
                    truncate(line, 200)
                };
                symbols.push(ParsedSymbol {
                    name: name.to_string(),
                    kind,
                    is_definition: tag.is_definition,
                    start_line,
                    end_line,
                    signature,
                });
            }
        }

        let imports = extract_imports(&def, source);
        let inherits = extract_inherits(&def, source);
        Some(ParsedFile {
            lang: def.name.to_string(),
            symbols,
            imports,
            inherits,
        })
    }
}

/// 用 tree-sitter 查询抽取导入语句的模块名；查询编译失败则该文件无导入（优雅降级）
fn extract_imports(def: &LangDef, source: &[u8]) -> Vec<String> {
    use streaming_iterator::StreamingIterator;
    let query = match Query::new(&def.language, def.import_query) {
        Ok(q) => q,
        Err(_) => return Vec::new(),
    };
    let mut parser = tree_sitter::Parser::new();
    if parser.set_language(&def.language).is_err() {
        return Vec::new();
    }
    let tree = match parser.parse(source, None) {
        Some(t) => t,
        None => return Vec::new(),
    };
    let mut cursor = QueryCursor::new();
    let mut out: Vec<String> = Vec::new();
    let mut it = cursor.matches(&query, tree.root_node(), source);
    while let Some(m) = it.next() {
        for cap in m.captures {
            if let Ok(text) = cap.node.utf8_text(source) {
                let cleaned = text
                    .trim()
                    .trim_matches(|c: char| c == '"' || c == '\'' || c == '`')
                    .to_string();
                if !cleaned.is_empty() && !out.contains(&cleaned) {
                    out.push(cleaned);
                }
            }
        }
    }
    out
}

/// 抽取继承关系：每个 match 内把 @child 与各 @parent 配对。查询编译失败/无对应捕获则返回空。
fn extract_inherits(def: &LangDef, source: &[u8]) -> Vec<(String, String)> {
    use streaming_iterator::StreamingIterator;
    if def.inherit_query.is_empty() {
        return Vec::new();
    }
    let query = match Query::new(&def.language, def.inherit_query) {
        Ok(q) => q,
        Err(_) => return Vec::new(),
    };
    let (Some(ci), Some(pi)) = (
        query.capture_index_for_name("child"),
        query.capture_index_for_name("parent"),
    ) else {
        return Vec::new();
    };
    let mut parser = tree_sitter::Parser::new();
    if parser.set_language(&def.language).is_err() {
        return Vec::new();
    }
    let tree = match parser.parse(source, None) {
        Some(t) => t,
        None => return Vec::new(),
    };
    let mut cursor = QueryCursor::new();
    let mut out: Vec<(String, String)> = Vec::new();
    let mut it = cursor.matches(&query, tree.root_node(), source);
    while let Some(m) = it.next() {
        let mut child: Option<String> = None;
        let mut parents: Vec<String> = Vec::new();
        for cap in m.captures {
            if let Ok(t) = cap.node.utf8_text(source) {
                if cap.index == ci {
                    child = Some(clean_type(t));
                } else if cap.index == pi {
                    parents.push(clean_type(t));
                }
            }
        }
        if let Some(c) = child {
            for p in parents {
                if !c.is_empty() && !p.is_empty() {
                    out.push((c.clone(), p));
                }
            }
        }
    }
    out
}

/// 清洗类型名：去掉泛型参数与限定前缀，便于匹配（List<Foo> -> List，a.b.Foo -> Foo）
fn clean_type(s: &str) -> String {
    let base = s.split('<').next().unwrap_or(s).trim();
    let last = base
        .rsplit(|c| c == '.' || c == ':')
        .next()
        .unwrap_or(base)
        .trim();
    last.to_string()
}

/// 预计算每行起始字节偏移（line_starts[0]=0，其后为每个 \n 之后的位置）
fn compute_line_starts(source: &[u8]) -> Vec<usize> {
    let mut starts = vec![0usize];
    for (i, &b) in source.iter().enumerate() {
        if b == b'\n' {
            starts.push(i + 1);
        }
    }
    starts
}

/// 字节偏移 → 1 基行号
fn line_of(line_starts: &[usize], offset: usize) -> u32 {
    match line_starts.binary_search(&offset) {
        Ok(i) => i as u32 + 1,
        Err(i) => i as u32,
    }
}

/// 从定义全文中提取签名头部：跟踪 ()/[] 深度，在顶层第一个函数体起始处截断。
/// 花括号语言截到顶层 `{`（destructuring/对象参数里的 `{` 在括号内会跳过）；
/// colon_body=true（Python）截到顶层 `:`。两者都找不到则退回首行。空白折叠为单空格。
fn extract_signature(def_text: &str, colon_body: bool) -> String {
    let bytes = def_text.as_bytes();
    let mut depth: i32 = 0;
    let mut cut: Option<usize> = None;
    let mut i = 0;
    while i < bytes.len() {
        match bytes[i] {
            b'(' | b'[' => depth += 1,
            b')' | b']' => depth -= 1,
            b'{' if !colon_body && depth <= 0 => {
                cut = Some(i);
                break;
            }
            b':' if colon_body && depth <= 0 => {
                cut = Some(i + 1);
                break;
            }
            _ => {}
        }
        i += 1;
    }
    let header = match cut {
        Some(end) => &def_text[..end.min(def_text.len())],
        None => def_text.lines().next().unwrap_or(def_text),
    };
    let collapsed = header.split_whitespace().collect::<Vec<_>>().join(" ");
    truncate(&collapsed, 400)
}

/// 按字符边界截断，避免切坏 UTF-8
fn truncate(s: &str, max: usize) -> String {
    if s.len() <= max {
        return s.to_string();
    }
    let end = s
        .char_indices()
        .map(|(i, _)| i)
        .take_while(|&i| i <= max)
        .last()
        .unwrap_or(0);
    s[..end].to_string()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn parses_rust_symbols_and_imports() {
        let mut idx = Indexer::new();
        let src = b"use std::fs;\npub fn hello() {}\nstruct Foo;\n";
        let parsed = idx.parse("rs", src).expect("rust 应能解析");
        assert_eq!(parsed.lang, "rust");
        let defs: Vec<&str> = parsed
            .symbols
            .iter()
            .filter(|s| s.is_definition)
            .map(|s| s.name.as_str())
            .collect();
        assert!(defs.contains(&"hello"), "未抽到函数定义 hello: {defs:?}");
        assert!(defs.contains(&"Foo"), "未抽到结构体定义 Foo: {defs:?}");
        assert!(
            parsed.imports.iter().any(|m| m == "std::fs"),
            "未抽到导入 std::fs: {:?}",
            parsed.imports
        );
    }

    #[test]
    fn parses_typescript() {
        let mut idx = Indexer::new();
        let src = b"import { x } from './a'\nexport function greet(): void {}\nclass Svc {}\ninterface Iface {}\ntype Alias = string\nexport const arrowFn = () => {}\n";
        let parsed = idx.parse("ts", src).expect("ts 应能解析");
        let defs: Vec<&str> = parsed
            .symbols
            .iter()
            .filter(|s| s.is_definition)
            .map(|s| s.name.as_str())
            .collect();
        // 继承 js 基础规则：函数/类/接口
        assert!(defs.contains(&"greet"), "未抽到函数 greet: {defs:?}");
        assert!(defs.contains(&"Svc"), "未抽到类 Svc: {defs:?}");
        assert!(defs.contains(&"Iface"), "未抽到接口 Iface: {defs:?}");
        // 箭头常量补充规则
        assert!(defs.contains(&"arrowFn"), "未抽到箭头常量 arrowFn: {defs:?}");
        // TS 专有：类型别名
        assert!(defs.contains(&"Alias"), "未抽到类型别名 Alias: {defs:?}");
        assert!(
            parsed.imports.iter().any(|m| m == "./a"),
            "未抽到导入 ./a: {:?}",
            parsed.imports
        );
    }

    #[test]
    fn parses_tsx() {
        let mut idx = Indexer::new();
        let src = b"export function Comp() { return null }\nclass TsxCls {}\n";
        let parsed = idx.parse("tsx", src).expect("tsx 应能解析");
        let defs: Vec<&str> = parsed
            .symbols
            .iter()
            .filter(|s| s.is_definition)
            .map(|s| s.name.as_str())
            .collect();
        assert!(defs.contains(&"Comp"), "未抽到 Comp: {defs:?}");
        assert!(defs.contains(&"TsxCls"), "未抽到 TsxCls: {defs:?}");
    }

    #[test]
    fn unknown_extension_skipped() {
        let mut idx = Indexer::new();
        assert!(idx.parse("txt", b"hello world").is_none());
    }

    #[test]
    fn extracts_inheritance() {
        let mut idx = Indexer::new();
        // TS：extends + implements
        let ts = idx
            .parse("ts", b"class Dog extends Animal implements Pet {}")
            .unwrap();
        assert!(
            ts.inherits.iter().any(|(c, p)| c == "Dog" && p == "Animal"),
            "TS extends 未抽到: {:?}",
            ts.inherits
        );
        // Rust：impl Trait for Type
        let rs = idx.parse("rs", b"impl Display for Foo {}").unwrap();
        assert!(
            rs.inherits.iter().any(|(c, p)| c == "Foo" && p == "Display"),
            "Rust impl 未抽到: {:?}",
            rs.inherits
        );
        // Java：extends
        let java = idx
            .parse("java", b"class Cat extends Pet {}")
            .unwrap();
        assert!(
            java.inherits.iter().any(|(c, p)| c == "Cat" && p == "Pet"),
            "Java extends 未抽到: {:?}",
            java.inherits
        );
    }

    #[test]
    fn parses_csharp() {
        let mut idx = Indexer::new();
        let src = b"namespace N { class C { void M() {} } }";
        let parsed = idx.parse("cs", src).expect("cs 应能解析");
        let defs: Vec<&str> = parsed
            .symbols
            .iter()
            .filter(|s| s.is_definition)
            .map(|s| s.name.as_str())
            .collect();
        assert!(defs.contains(&"C"), "未抽到类 C: {defs:?}");
        assert!(defs.contains(&"M"), "未抽到方法 M: {defs:?}");
    }
}
