use std::io::BufReader;

use async_recursion::async_recursion;
use zbus::fdo::DBusProxy;
use zbus::names::OwnedBusName;
use zbus::zvariant::ObjectPath;
use zbus::{Connection, Result};
use zbus_xml::Node;

#[async_recursion]
async fn print_all_interfaces(
    connection: &Connection,
    service: &OwnedBusName,
    path: ObjectPath<'async_recursion>,
    indent: usize,
) -> std::result::Result<(), zbus::Error> {
    println!("{:indent$}{} ", "", path.as_str(), indent = indent);
    let introspectable_proxy = zbus::fdo::IntrospectableProxy::builder(connection)
        .destination(service)?
        .path(path.clone())?
        .build()
        .await?;
    let introspect_xml = introspectable_proxy.introspect().await?;
    let introspect = Node::from_reader(BufReader::new(introspect_xml.as_bytes())).unwrap();
    println!("{:indent$}Interfaces: ", "", indent = indent + 4);
    for interface in introspect.interfaces() {
        println!("{:indent$}{} ", "", interface.name(), indent = indent + 8);
        println!("{:indent$}Methods: ", "", indent = indent + 12);
        for method in interface.methods() {
            println!("{:indent$}{} ", "", method.name(), indent = indent + 16);
        }
        println!("{:indent$}Signals: ", "", indent = indent + 12);
        for signal in interface.signals() {
            println!("{:indent$}{} ", "", signal.name(), indent = indent + 16);
        }
        println!("{:indent$}Properties: ", "", indent = indent + 12);
        for property in interface.properties() {
            println!("{:indent$}{} ", "", property.name(), indent = indent + 16);
        }
        println!("{:indent$}Annotations: ", "", indent = indent + 12);
        for annotation in interface.annotations() {
            println!("{:indent$}{} ", "", annotation.name(), indent = indent + 16);
        }
    }
    for node in introspect.nodes() {
        let node_name = node.name().unwrap();

        let path_name = if path.as_str().ends_with('/') {
            path.as_str().to_string() + node_name
        } else {
            path.as_str().to_string() + "/" + node_name
        };
        let sub_path = ObjectPath::try_from(path_name)?;

        print_all_interfaces(connection, service, sub_path, indent).await?;
    }
    Ok(())
}
#[tokio::main]
async fn main() -> Result<()> {
    let connection = Connection::session().await?;

    let dbusproxy = DBusProxy::new(&connection).await?;
    let reply = dbusproxy.list_names().await?;
    let _service = reply.first().unwrap();
    for service in reply {
        if service.as_str().contains(':') {
            continue;
        }
        println!("Service: {}", service.as_str());
        let path_name = "/".to_string();
        let path = ObjectPath::try_from(path_name)?;
        print_all_interfaces(&connection, &service, path, 4).await?;
    }

    Ok(())
}
