use std::{
    collections::HashMap,
    sync::{Arc, atomic::AtomicI32},
};

use lsp_server::{Connection, Message, Notification, RequestId, Response};
use lsp_types::{
    ApplyWorkspaceEditParams, ApplyWorkspaceEditResponse, ConfigurationParams, MessageActionItem,
    PublishDiagnosticsParams, RegistrationParams, ShowMessageParams, ShowMessageRequestParams,
    UnregistrationParams,
};
use serde::de::DeserializeOwned;
use tokio::{
    select,
    sync::{Mutex, oneshot},
};
use tokio_util::sync::CancellationToken;

pub struct ClientProxy {
    conn: Connection,
    id_counter: AtomicI32,
    response_manager: Arc<Mutex<HashMap<RequestId, oneshot::Sender<Response>>>>,
}

#[allow(unused)]
impl ClientProxy {
    pub fn new(conn: Connection) -> Self {
        Self {
            conn,
            id_counter: AtomicI32::new(0),
            response_manager: Arc::new(Mutex::new(HashMap::new())),
        }
    }

    pub fn send_notification(&self, method: &str, params: impl serde::Serialize) {
        let _ = self.conn.sender.send(Message::Notification(Notification {
            method: method.to_string(),
            params: serde_json::to_value(params).unwrap(),
        }));
    }

    pub async fn send_request(
        &self,
        id: RequestId,
        method: &str,
        params: impl serde::Serialize,
        cancel_token: CancellationToken,
    ) -> Option<Response> {
        let (sender, receiver) = oneshot::channel();
        self.response_manager
            .lock()
            .await
            .insert(id.clone(), sender);
        let _ = self.conn.sender.send(Message::Request(lsp_server::Request {
            id: id.clone(),
            method: method.to_string(),
            params: serde_json::to_value(params).unwrap(),
        }));
        let response = select! {
            response = receiver => response.ok(),
            _ = cancel_token.cancelled() => None,
        };
        self.response_manager.lock().await.remove(&id);
        response
    }

    fn send_request_no_wait(&self, id: RequestId, method: &str, params: impl serde::Serialize) {
        let _ = self.conn.sender.send(Message::Request(lsp_server::Request {
            id,
            method: method.to_string(),
            params: serde_json::to_value(params).unwrap(),
        }));
    }

    pub async fn on_response(&self, response: Response) -> Option<()> {
        let sender = self.response_manager.lock().await.remove(&response.id)?;
        let _ = sender.send(response);
        Some(())
    }

    pub fn next_id(&self) -> RequestId {
        let id = self
            .id_counter
            .fetch_add(1, std::sync::atomic::Ordering::SeqCst);

        id.into()
    }

    pub async fn get_configuration<C>(
        &self,
        params: ConfigurationParams,
        cancel_token: CancellationToken,
    ) -> Option<Vec<C>>
    where
        C: DeserializeOwned,
    {
        let request_id = self.next_id();
        let response = self
            .send_request(request_id, "workspace/configuration", params, cancel_token)
            .await?;
        serde_json::from_value(response.result?).ok()
    }

    pub fn dynamic_register_capability(&self, registration_param: RegistrationParams) {
        let request_id = self.next_id();
        self.send_request_no_wait(request_id, "client/registerCapability", registration_param);
    }

    pub fn dynamic_unregister_capability(&self, registration_param: UnregistrationParams) {
        let request_id = self.next_id();
        self.send_request_no_wait(
            request_id,
            "client/unregisterCapability",
            registration_param,
        );
    }

    pub fn show_message(&self, message: ShowMessageParams) {
        self.send_notification("window/showMessage", message);
    }

    pub async fn show_message_request(
        &self,
        params: ShowMessageRequestParams,
        cancel_token: CancellationToken,
    ) -> Option<MessageActionItem> {
        let request_id = self.next_id();
        let response = self
            .send_request(
                request_id,
                "window/showMessageRequest",
                params,
                cancel_token,
            )
            .await?;
        serde_json::from_value(response.result?).ok()
    }

    pub fn publish_diagnostics(&self, params: PublishDiagnosticsParams) {
        self.send_notification("textDocument/publishDiagnostics", params);
    }

    pub async fn apply_edit(
        &self,
        params: ApplyWorkspaceEditParams,
        cancel_token: CancellationToken,
    ) -> Option<ApplyWorkspaceEditResponse> {
        let request_id = self.next_id();
        let r = self
            .send_request(request_id, "workspace/applyEdit", params, cancel_token)
            .await?;
        serde_json::from_value(r.result?).ok()
    }

    pub fn send_request_no_response(&self, method: &str, params: impl serde::Serialize) {
        let request_id = self.next_id();
        self.send_request_no_wait(request_id, method, params);
    }

    pub fn refresh_workspace_diagnostics(&self) {
        self.send_request_no_response("workspace/diagnostic/refresh", ());
    }
}
