//! Go-to-definition provider
//!
//! Allows jumping to the definition of symbols

use tower_lsp::lsp_types::{
    GotoDefinitionParams,
    GotoDefinitionResponse,
    Location,
    Range,
    Url,
};

use super::document::Document;

/// Get the definition location for a symbol at a position
pub fn get_definition(
    doc: &Document,
    params: &GotoDefinitionParams,
    uri: &Url,
) -> Option<GotoDefinitionResponse> {
    let position = params.text_document_position_params.position;

    // Convert LSP position to byte offset
    let offset = position_to_offset(doc, position.line as usize, position.character as usize)?;

    // Get the word at the cursor
    let word = doc.word_at_offset(offset)?;

    // Look up the symbol in the name table
    let entry = doc.name_table.resolve_name(&word)?;

    let mut positions = doc.positions.clone();
    let (start_line, start_col) = positions.offset_to_position(entry.span.start);
    let (end_line, end_col) = positions.offset_to_position(entry.span.end);

    Some(GotoDefinitionResponse::Scalar(Location {
        uri: uri.clone(),
        range: Range {
            start: tower_lsp::lsp_types::Position {
                line: start_line as u32,
                character: start_col as u32,
            },
            end: tower_lsp::lsp_types::Position {
                line: end_line as u32,
                character: end_col as u32,
            },
        },
    }))
}

/// Convert LSP position to byte offset
fn position_to_offset(doc: &Document, line: usize, character: usize) -> Option<usize> {
    let line_start = doc.positions.line_offset(line)?;
    Some(line_start + character)
}
