use ::line_index::LineIndex;
use anyhow::Result;
use lsp_types::{DocumentSymbol, DocumentSymbolParams, DocumentSymbolResponse, SymbolKind};
use squawk_ide::db::line_index;
use squawk_ide::document_symbols::{DocumentSymbolKind, document_symbols};

use crate::global_state::Snapshot;
use crate::lsp_utils;

pub(crate) fn handle_document_symbol(
    snapshot: &Snapshot,
    params: DocumentSymbolParams,
) -> Result<Option<DocumentSymbolResponse>> {
    let uri = params.text_document.uri;

    let db = snapshot.db();
    let file = snapshot.file(&uri).unwrap();
    let line_index = line_index(db, file);

    let symbols = document_symbols(db, file);

    fn convert_symbol(
        sym: squawk_ide::document_symbols::DocumentSymbol,
        line_index: &LineIndex,
    ) -> DocumentSymbol {
        let range = lsp_utils::range(line_index, sym.full_range);
        let selection_range = lsp_utils::range(line_index, sym.focus_range);

        let children = sym
            .children
            .into_iter()
            .map(|child| convert_symbol(child, line_index))
            .collect::<Vec<_>>();

        let children = (!children.is_empty()).then_some(children);

        DocumentSymbol {
            name: sym.name,
            detail: sym.detail,
            kind: match sym.kind {
                DocumentSymbolKind::Schema => SymbolKind::NAMESPACE,
                DocumentSymbolKind::Table => SymbolKind::STRUCT,
                DocumentSymbolKind::View => SymbolKind::STRUCT,
                DocumentSymbolKind::MaterializedView => SymbolKind::STRUCT,
                DocumentSymbolKind::Function => SymbolKind::FUNCTION,
                DocumentSymbolKind::Aggregate => SymbolKind::FUNCTION,
                DocumentSymbolKind::Procedure => SymbolKind::FUNCTION,
                DocumentSymbolKind::Type => SymbolKind::CLASS,
                DocumentSymbolKind::Enum => SymbolKind::ENUM,
                DocumentSymbolKind::Index => SymbolKind::KEY,
                DocumentSymbolKind::Domain => SymbolKind::CLASS,
                DocumentSymbolKind::Sequence => SymbolKind::CONSTANT,
                DocumentSymbolKind::Trigger => SymbolKind::EVENT,
                DocumentSymbolKind::Tablespace => SymbolKind::NAMESPACE,
                DocumentSymbolKind::Database => SymbolKind::MODULE,
                DocumentSymbolKind::Server => SymbolKind::OBJECT,
                DocumentSymbolKind::Extension => SymbolKind::PACKAGE,
                DocumentSymbolKind::Column => SymbolKind::FIELD,
                DocumentSymbolKind::Variant => SymbolKind::ENUM_MEMBER,
                DocumentSymbolKind::Cursor => SymbolKind::VARIABLE,
                DocumentSymbolKind::PreparedStatement => SymbolKind::VARIABLE,
                DocumentSymbolKind::Channel => SymbolKind::EVENT,
                DocumentSymbolKind::EventTrigger => SymbolKind::EVENT,
                DocumentSymbolKind::Role => SymbolKind::CLASS,
                DocumentSymbolKind::Policy => SymbolKind::VARIABLE,
                DocumentSymbolKind::PropertyGraph => SymbolKind::STRUCT,
            },
            tags: None,
            range,
            selection_range,
            children,
            #[allow(deprecated)]
            deprecated: None,
        }
    }

    let lsp_symbols: Vec<DocumentSymbol> = symbols
        .into_iter()
        .map(|sym| convert_symbol(sym, &line_index))
        .collect();

    Ok(Some(DocumentSymbolResponse::Nested(lsp_symbols)))
}
