use super::{extract_doc_comment, extract_signature, field_text, node_text};
use crate::types::*;

pub fn extract(tree: &tree_sitter::Tree, source: &[u8]) -> (Vec<Symbol>, Vec<Import>) {
    let root = tree.root_node();
    let mut symbols = Vec::new();
    let mut imports = Vec::new();
    let mut cursor = root.walk();

    for child in root.children(&mut cursor) {
        match child.kind() {
            "function_declaration" => {
                if let Some(sym) = extract_function(&child, source, None) {
                    symbols.push(sym);
                }
            }
            "class_declaration" => {
                if let Some(sym) = extract_class(&child, source) {
                    symbols.push(sym);
                }
            }
            "interface_declaration" => {
                if let Some(name) = field_text(&child, "name", source) {
                    symbols.push(Symbol {
                        name: name.to_string(),
                        kind: SymbolKind::Interface,
                        span: Span::from_node(&child),
                        signature: extract_signature(&child, "body", source),
                        doc_comment: extract_doc_comment(&child, source),
                        parent: None,
                        children: Vec::new(),
                    });
                }
            }
            "type_alias_declaration" => {
                if let Some(name) = field_text(&child, "name", source) {
                    symbols.push(Symbol {
                        name: name.to_string(),
                        kind: SymbolKind::TypeAlias,
                        span: Span::from_node(&child),
                        signature: node_text(&child, source).to_string(),
                        doc_comment: extract_doc_comment(&child, source),
                        parent: None,
                        children: Vec::new(),
                    });
                }
            }
            "enum_declaration" => {
                if let Some(name) = field_text(&child, "name", source) {
                    symbols.push(Symbol {
                        name: name.to_string(),
                        kind: SymbolKind::Enum,
                        span: Span::from_node(&child),
                        signature: extract_signature(&child, "body", source),
                        doc_comment: extract_doc_comment(&child, source),
                        parent: None,
                        children: Vec::new(),
                    });
                }
            }
            "import_statement" => {
                imports.push(Import {
                    path: node_text(&child, source).to_string(),
                    alias: None,
                    span: Span::from_node(&child),
                });
            }
            "export_statement" => {
                // export function foo() {} or export class Foo {}
                let mut inner = child.walk();
                for grandchild in child.children(&mut inner) {
                    match grandchild.kind() {
                        "function_declaration" => {
                            if let Some(sym) = extract_function(&grandchild, source, None) {
                                symbols.push(sym);
                            }
                        }
                        "class_declaration" => {
                            if let Some(sym) = extract_class(&grandchild, source) {
                                symbols.push(sym);
                            }
                        }
                        _ => {}
                    }
                }
            }
            "lexical_declaration" => {
                // const foo = ... or const foo: Type = ...
                // Also handles arrow functions: const foo = () => {}
                extract_lexical_declarations(&child, source, &mut symbols);
            }
            _ => {}
        }
    }

    (symbols, imports)
}

fn extract_function(
    node: &tree_sitter::Node,
    source: &[u8],
    parent: Option<&str>,
) -> Option<Symbol> {
    let name = field_text(node, "name", source)?;
    Some(Symbol {
        name: name.to_string(),
        kind: if parent.is_some() {
            SymbolKind::Method
        } else {
            SymbolKind::Function
        },
        span: Span::from_node(node),
        signature: extract_signature(node, "body", source),
        doc_comment: extract_doc_comment(node, source),
        parent: parent.map(|s| s.to_string()),
        children: Vec::new(),
    })
}

fn extract_class(node: &tree_sitter::Node, source: &[u8]) -> Option<Symbol> {
    let name = field_text(node, "name", source)?;
    let mut methods = Vec::new();

    if let Some(body) = node.child_by_field_name("body") {
        let mut cursor = body.walk();
        for child in body.children(&mut cursor) {
            if child.kind() == "method_definition" {
                if let Some(mname) = field_text(&child, "name", source) {
                    methods.push(Symbol {
                        name: mname.to_string(),
                        kind: SymbolKind::Method,
                        span: Span::from_node(&child),
                        signature: extract_signature(&child, "body", source),
                        doc_comment: extract_doc_comment(&child, source),
                        parent: Some(name.to_string()),
                        children: Vec::new(),
                    });
                }
            }
        }
    }

    Some(Symbol {
        name: name.to_string(),
        kind: SymbolKind::Class,
        span: Span::from_node(node),
        signature: extract_signature(node, "body", source),
        doc_comment: extract_doc_comment(node, source),
        parent: None,
        children: methods,
    })
}

fn extract_lexical_declarations(
    node: &tree_sitter::Node,
    source: &[u8],
    symbols: &mut Vec<Symbol>,
) {
    let mut cursor = node.walk();
    for child in node.children(&mut cursor) {
        if child.kind() == "variable_declarator" {
            if let Some(name) = field_text(&child, "name", source) {
                // Check if the value is an arrow function or function expression
                if let Some(value) = child.child_by_field_name("value") {
                    let kind = value.kind();
                    if kind == "arrow_function" || kind == "function" {
                        symbols.push(Symbol {
                            name: name.to_string(),
                            kind: SymbolKind::Function,
                            span: Span::from_node(node),
                            signature: extract_signature(node, "body", source),
                            doc_comment: extract_doc_comment(node, source),
                            parent: None,
                            children: Vec::new(),
                        });
                    } else {
                        // Regular const — only include if UPPER_CASE
                        if name.chars().all(|c| c.is_uppercase() || c == '_') {
                            symbols.push(Symbol {
                                name: name.to_string(),
                                kind: SymbolKind::Const,
                                span: Span::from_node(node),
                                signature: node_text(node, source).to_string(),
                                doc_comment: extract_doc_comment(node, source),
                                parent: None,
                                children: Vec::new(),
                            });
                        }
                    }
                }
            }
        }
    }
}
