// SPDX-FileCopyrightText: 2022 Matteo Settenvini <matteo.settenvini@montecristosoftware.eu>
// SPDX-License-Identifier: GPL-3.0-or-later

use {
    nix::unistd::Uid,
    serde::{Deserialize, Serialize},
    std::net::IpAddr,
    zbus::{dbus_proxy, Result},
};

#[dbus_proxy(
    default_service = "com.endlessm.ParentalControls",
    interface = "com.endlessm.ParentalControls.Dns",
    default_path = "/com/endlessm/ParentalControls/Dns",
    gen_blocking = false
)]
trait MalcontentDns {
    fn get_restrictions(&self, uid: u32) -> Result<Restrictions>;
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, zvariant::Type)]
pub struct Restriction {
    pub ip: IpAddr,
    pub hostname: String,
}

pub type Restrictions = Vec<Restriction>;

pub async fn restrictions_for(user: Uid) -> anyhow::Result<Vec<Restriction>, anyhow::Error> {
    #[cfg(not(feature = "integration_test"))]
    let proxy = {
        // This is the normal behavior
        let connection = zbus::Connection::system().await?;
        MalcontentDnsProxy::new(&connection).await?
    };

    #[cfg(feature = "integration_test")]
    let proxy = {
        use tokio::net::UnixStream;

        // During integration testing, we want to connect to a private
        // bus name to avoid clashes with existing system services.
        let socket_path = std::env::var("TEST_DBUS_SOCKET")
            .expect("The test has not set the TEST_DBUS_SOCKET environment variable to the unix socket to connect to");

        let socket = loop {
            match UnixStream::connect(&socket_path).await {
                Ok(stream) => break stream,
                Err(e) if e.kind() == std::io::ErrorKind::ConnectionRefused => {
                    tokio::task::yield_now().await;
                    continue;
                }
                Err(e) => anyhow::bail!(e),
            }
        };

        let connection = zbus::ConnectionBuilder::unix_stream(socket)
            .p2p()
            .build()
            .await?;

        MalcontentDnsProxy::builder(&connection)
            .build()
            .await
            .expect("Unable to build DBus proxy object")
    };

    let restrictions = proxy.get_restrictions(user.as_raw()).await;
    log::trace!(
        "malcontent-nss: user {} restrictions are {:?}",
        user,
        &restrictions
    );

    Ok(restrictions?)
}