use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::Mutex;
use tower_lsp_server::{Client, LspService, Server};

use rowan::GreenNode;

mod config;
mod conversions;
mod documents;
mod handlers;
mod helpers;
mod server;

/// State for a single document in the LSP.
#[derive(Clone)]
pub struct DocumentState {
    /// The document text content.
    pub text: String,
    /// Parsed metadata from YAML frontmatter (if present).
    pub metadata: Option<crate::metadata::DocumentMetadata>,
    /// Cached syntax tree for incremental parsing.
    pub tree: GreenNode,
}

pub struct PanacheLsp {
    client: Client,
    // Use String keys since Uri doesn't implement Send
    document_map: Arc<Mutex<HashMap<String, DocumentState>>>,
    workspace_root: Arc<Mutex<Option<PathBuf>>>,
}

impl PanacheLsp {
    pub fn new(client: Client) -> Self {
        Self {
            client,
            document_map: Arc::new(Mutex::new(HashMap::new())),
            workspace_root: Arc::new(Mutex::new(None)),
        }
    }

    /// Get access to the document map for testing purposes.
    ///
    /// This method is only available when the `lsp` feature is enabled
    /// and is intended for use in integration tests.
    #[doc(hidden)]
    pub fn document_map(&self) -> Arc<Mutex<HashMap<String, DocumentState>>> {
        Arc::clone(&self.document_map)
    }

    /// Get access to the workspace root for testing purposes.
    ///
    /// This method is only available when the `lsp` feature is enabled
    /// and is intended for use in integration tests.
    #[doc(hidden)]
    pub fn workspace_root(&self) -> Arc<Mutex<Option<PathBuf>>> {
        Arc::clone(&self.workspace_root)
    }
}

pub async fn run() -> std::io::Result<()> {
    let stdin = tokio::io::stdin();
    let stdout = tokio::io::stdout();

    let (service, socket) = LspService::new(PanacheLsp::new);
    Server::new(stdin, stdout, socket).serve(service).await;

    Ok(())
}
