use std::{path::Path, sync::Arc};

use compio::net::{TcpListener, ToSocketAddrsAsync, UnixListener};
use compio::runtime::RuntimeBuilder;

use super::{handler::RpcHandler, server::*};
use crate::logdb::{RawTree, db::Db};
use crate::transactional::{db::TxDb, raw::TxRawTree};

impl Db {
    pub fn register_rpc_tree(&self, tree: &RawTree) {
        self.rpc_trees
            .write()
            .insert(tree.hashname, Arc::new(tree.clone()) as Arc<dyn RpcHandler>);
    }

    pub fn listen_tcp(&self, addr: impl ToSocketAddrsAsync + Send + 'static) {
        let trees: TreeMap = Arc::new(self.rpc_trees.read().clone());
        std::thread::spawn(move || {
            RuntimeBuilder::new()
                .build()
                .expect("Failed to build compio runtime")
                .block_on(async {
                    let listener = TcpListener::bind(addr)
                        .await
                        .expect("Failed to bind TCP listener");
                    accept_tcp(listener, trees).await;
                });
        });
    }

    pub fn listen_uds(&self, path: impl AsRef<Path> + Send + 'static) {
        let trees: TreeMap = Arc::new(self.rpc_trees.read().clone());
        std::thread::spawn(move || {
            RuntimeBuilder::new()
                .build()
                .expect("Failed to build compio runtime")
                .block_on(async {
                    let listener = UnixListener::bind(path)
                        .await
                        .expect("Failed to bind UDS listener");
                    accept_uds(listener, trees).await;
                });
        });
    }
}

impl TxDb {
    pub fn register_rpc_tree(&self, tree: &TxRawTree) {
        self.rpc_trees
            .write()
            .insert(tree.hashname, Arc::new(tree.clone()) as Arc<dyn RpcHandler>);
    }

    pub fn listen_tcp(&self, addr: impl ToSocketAddrsAsync + Send + 'static) {
        let trees: TreeMap = Arc::new(self.rpc_trees.read().clone());
        std::thread::spawn(move || {
            RuntimeBuilder::new()
                .build()
                .expect("Failed to build compio runtime")
                .block_on(async {
                    let listener = TcpListener::bind(addr)
                        .await
                        .expect("Failed to bind TCP listener");
                    accept_tcp(listener, trees).await;
                });
        });
    }

    pub fn listen_uds(&self, path: impl AsRef<Path> + Send + 'static) {
        let trees: TreeMap = Arc::new(self.rpc_trees.read().clone());
        std::thread::spawn(move || {
            RuntimeBuilder::new()
                .build()
                .expect("Failed to build compio runtime")
                .block_on(async {
                    let listener = UnixListener::bind(path)
                        .await
                        .expect("Failed to bind UDS listener");
                    accept_uds(listener, trees).await;
                });
        });
    }
}
