// Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only

// TODO: Both this and server proxy could emit same events, have way to generate stream from either?

use std::any::TypeId;

use cosmic::iced::{self, Subscription};
use futures::{StreamExt, stream};

use crate::subscriptions::status_notifier_item::StatusNotifierItem;

mod client;
pub(crate) mod server;

#[derive(Clone, Debug)]
pub enum Event {
    Connected(zbus::Connection),
    Registered(StatusNotifierItem),
    Unregistered(String),
    Error(String), // XXX
}

enum State {
    NotConnected,
    Connected(client::EventStream),
    Failed,
}

pub fn subscription() -> iced::Subscription<Event> {
    pub struct MyID;
    Subscription::run_with(TypeId::of::<MyID>(), |_| {
        stream::unfold(State::NotConnected, |state| async move {
            match state {
                State::NotConnected => match connect().await {
                    Ok((connection, stream)) => {
                        Some((Event::Connected(connection), State::Connected(stream)))
                    }
                    Err(err) => Some((Event::Error(err.to_string()), State::Failed)),
                },
                State::Connected(mut stream) => stream
                    .next()
                    .await
                    .map(|event| (event, State::Connected(stream))),
                State::Failed => None,
            }
        })
    })
}

async fn connect() -> zbus::Result<(zbus::Connection, client::EventStream)> {
    // Connect to session dbus socket
    let connection = zbus::Connection::session().await?;

    // Start `StatusNotifierWatcher` service, if there isn't one running already
    if let Err(err) = crate::status_notifier_watcher::cosmic_register(&connection).await {
        eprintln!("Failed to start status notifier watcher: {}", err);
    }

    // Connect client and listen for registered/unregistered
    let stream = client::watch(&connection).await?;

    Ok((connection, stream))
}
