use std::sync::Arc;

use lsp_types::GotoDefinitionParams;
use rowan::ast::AstNode;

use crate::{features::cursor::CursorContext, syntax::latex};

use super::DefinitionResult;

pub(super) fn goto_command_definition(
    context: &CursorContext<GotoDefinitionParams>,
) -> Option<Vec<DefinitionResult>> {
    let name = context
        .cursor
        .as_latex()
        .filter(|token| token.kind().is_command_name())?;

    let origin_selection_range = name.text_range();

    for document in context.request.workspace.documents_by_uri.values() {
        if let Some(data) = document.data.as_latex() {
            let root = latex::SyntaxNode::new_root(data.green.clone());

            if let Some(result) = root
                .descendants()
                .filter_map(latex::CommandDefinition::cast)
                .filter(|def| {
                    def.name()
                        .and_then(|name| name.command())
                        .map_or(false, |node| node.text() == name.text())
                })
                .find_map(|def| {
                    Some(DefinitionResult {
                        origin_selection_range,
                        target_uri: Arc::clone(&document.uri),
                        target_range: latex::small_range(&def),
                        target_selection_range: def.name()?.command()?.text_range(),
                    })
                })
            {
                return Some(vec![result]);
            }
        }
    }

    None
}
