use pyo3::prelude::*;

use crate::python::service::{PyServiceHandler, PythonServiceBridge};
use crate::python::types::PyTlsConfig;
use crate::server::Server;

/// Python-visible RPC server.
///
/// All async methods return native Python awaitables via `future_into_py`.
#[pyclass(name = "RpcServer")]
pub struct PyRpcServer {
    bind_addr: String,
    tls_config: PyTlsConfig,
    services: Vec<PythonServiceBridge>,
}

#[pymethods]
impl PyRpcServer {
    #[new]
    fn new(bind_addr: String, tls: PyTlsConfig) -> Self {
        Self {
            bind_addr,
            tls_config: tls,
            services: Vec::new(),
        }
    }

    /// Register a service handler.
    fn add_service(&mut self, handler: &PyServiceHandler) {
        self.services
            .push(PythonServiceBridge::from_handler(handler));
    }

    /// Start the server. Returns a Python awaitable that resolves when the
    /// server stops.
    fn start<'py>(&mut self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
        let bind_addr = self.bind_addr.clone();
        let tls_config = self.tls_config.inner.clone();
        let services = std::mem::take(&mut self.services);

        pyo3_async_runtimes::tokio::future_into_py(py, async move {
            let mut builder = Server::builder();

            builder = builder
                .bind_str(&bind_addr)
                .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;

            builder = builder.tls(tls_config);

            for service in services {
                builder = builder.add_service(service);
            }

            let server = builder
                .build()
                .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;

            server
                .serve()
                .await
                .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;

            Ok(())
        })
    }
}
