use std::{
    sync::{
        atomic::Ordering,
        mpsc::{Receiver, Sender, channel},
    },
    thread,
};

use lunar_lib::trace;

use crate::{
    SHUTDOWN,
    listener::{IpcListener, Listener},
    player::{PlayerError, PlayerEvent, PlayerRequest},
};

pub struct EventHandler {
    rx: Receiver<PlayerEvent>,

    listener_tx: Sender<PlayerEvent>,

    #[cfg(feature = "mpris")]
    mpris_tx: tokio::sync::mpsc::Sender<crate::mpris::MprisEvent>,
}

impl EventHandler {
    pub fn open(player_tx: Sender<PlayerRequest>) -> Result<Sender<PlayerEvent>, PlayerError> {
        let (tx, rx) = channel();

        let listener_tx = Listener::open(player_tx.clone())?;

        #[cfg(feature = "mpris")]
        let mpris_tx = crate::mpris::MprisConnector::open(player_tx);

        let event_handler = Self {
            rx,

            listener_tx,

            #[cfg(feature = "mpris")]
            mpris_tx,
        };

        thread::spawn(move || {
            event_handler.run();
            SHUTDOWN.store(true, Ordering::Relaxed);
        });

        Ok(tx)
    }

    fn run(self) {
        while let Ok(event) = self.rx.recv() {
            trace!("Sending event: {event}");

            let _ = self.listener_tx.send(event.clone());

            #[cfg(feature = "mpris")]
            {
                use crate::mpris::MprisEvent;

                let mpris_event = match event {
                    PlayerEvent::CurrentlyPlayingChanged {
                        ref currently_playing,
                    } => Some(MprisEvent::CurrentlyPlayingChanged {
                        currently_playing: currently_playing.clone(),
                    }),
                    PlayerEvent::PlaybackStopped => Some(MprisEvent::PlaybackStopped),
                    PlayerEvent::PlaybackIsPlayingChanged {
                        is_playing,
                        changed_at,
                    } => Some(MprisEvent::IsPlayingChanged {
                        is_playing,
                        changed_at,
                    }),
                    PlayerEvent::ShuffleModeChanged { shuffle_mode } => {
                        Some(MprisEvent::ShuffleStatusChanged { shuffle_mode })
                    }
                    PlayerEvent::LoopModeChanged { loop_mode } => {
                        Some(MprisEvent::LoopStatusChanged { loop_mode })
                    }
                    PlayerEvent::VolumeChanged { volume } => {
                        Some(MprisEvent::VolumeChanged { volume })
                    }
                    PlayerEvent::SeekOccured { time } => Some(MprisEvent::Seeked { time }),
                    _ => None,
                };

                if let Some(mpris_event) = mpris_event {
                    let _ = self.mpris_tx.blocking_send(mpris_event);
                }
            }
        }
    }
}

pub trait EventTx {
    fn event(&self, event: PlayerEvent);
}

impl EventTx for Sender<PlayerEvent> {
    fn event(&self, event: PlayerEvent) {
        let _ = self.send(event);
    }
}
