use tower_lsp::lsp_types;

/// A Language Server implementation for Rott.
///  
/// Implements the [`tower_lsp::LanguageServer`] trait to handle LSP requests
/// (e.g. initialization, text synchronization, open notifications)
/// asynchronously.
struct RottLanguageServer {
    /// Client handle for sending notifications and requests to the editor.
    client: tower_lsp::Client,
}

#[tower_lsp::async_trait]
impl tower_lsp::LanguageServer for RottLanguageServer {
    // Inform the client of our server capabilities during initialization.
    async fn initialize(
        &self,
        _: lsp_types::InitializeParams,
    ) -> tower_lsp::jsonrpc::Result<lsp_types::InitializeResult> {
        Ok(lsp_types::InitializeResult {
            capabilities: lsp_types::ServerCapabilities {
                // We can synchronize the text of files, which means we request
                // to receive full updates whenever a file is opened or changed.
                // `lsp_types::TextDocumentSyncKind::FULL` means we require full text
                // every time.
                text_document_sync: Some(lsp_types::TextDocumentSyncCapability::Kind(
                    lsp_types::TextDocumentSyncKind::FULL,
                )),
                ..Default::default()
            },
            ..Default::default()
        })
    }

    // On file open, tokenize the new document and log any lexing errors.
    async fn did_open(&self, params: lsp_types::DidOpenTextDocumentParams) {
        // Measure lexing performance to track parser responsiveness.
        let start_time = std::time::Instant::now();
        let has_errors =
            rottlib::lexer::TokenizedFile::tokenize(&params.text_document.text).has_errors();
        let elapsed_time = start_time.elapsed();

        self.client
            .log_message(
                lsp_types::MessageType::INFO,
                format!(
                    "Tokenized {} in {:?}",
                    params.text_document.uri.path(),
                    elapsed_time
                ),
            )
            .await;

        if has_errors {
            self.client
                .log_message(
                    lsp_types::MessageType::INFO,
                    format!(
                        "There was an error while tokenizing {}",
                        params.text_document.uri.path(),
                    ),
                )
                .await;
        }
    }

    // Handle shutdown signal.
    async fn shutdown(&self) -> tower_lsp::jsonrpc::Result<()> {
        // No cleanup required on shutdown; simply acknowledge the request.
        Ok(())
    }
}

#[tokio::main]
async fn main() {
    // We are using standard input and output for communicating with an editor,
    // so we need to avoid methods or macros that write or read using them,
    // e.g. `println!`.
    let (stdin, stdout) = (tokio::io::stdin(), tokio::io::stdout());
    let (service, socket) = tower_lsp::LspService::new(|client| RottLanguageServer { client });
    tower_lsp::Server::new(stdin, stdout, socket)
        .serve(service)
        .await;
}
