use crate::process::Pid;
use std::collections::HashMap;
use tokio::sync::{broadcast, mpsc};

pub struct IpcBus {
    inboxes: HashMap<Pid, mpsc::Sender<IpcMessage>>,

    signal_tx: broadcast::Sender<Signal>,

    pipes: HashMap<String, mpsc::Sender<Vec<u8>>>,

    shared: HashMap<String, serde_json::Value>,
}

#[derive(Debug, Clone)]
pub struct IpcMessage {
    pub from: Pid,
    pub to: Pid,
    pub kind: MessageKind,
    pub payload: String,
}

#[derive(Debug, Clone)]
pub enum MessageKind {
    Text,
    Delegate,
    DelegateResult,
    Data,
    AgentPass,
}

#[derive(Debug, Clone)]
pub struct Signal {
    pub target: SignalTarget,
    pub kind: SignalKind,
    pub from: Pid,
}

#[derive(Debug, Clone)]
pub enum SignalTarget {
    Process(Pid),
    All,
}

#[derive(Debug, Clone, Copy)]
pub enum SignalKind {
    Wake,
    Interrupt,
    Term,
    Kill,
    Stop,
    Continue,
}

impl IpcBus {
    pub fn new() -> Self {
        let (signal_tx, _) = broadcast::channel(64);
        Self {
            inboxes: HashMap::new(),
            signal_tx,
            pipes: HashMap::new(),
            shared: HashMap::new(),
        }
    }

    pub fn register(&mut self, pid: Pid) -> mpsc::Receiver<IpcMessage> {
        let (tx, rx) = mpsc::channel(32);
        self.inboxes.insert(pid, tx);
        rx
    }

    pub async fn send(&self, msg: IpcMessage) -> bool {
        if let Some(tx) = self.inboxes.get(&msg.to) {
            tx.send(msg).await.is_ok()
        } else {
            false
        }
    }

    pub fn signal(&self, signal: Signal) {
        let _ = self.signal_tx.send(signal);
    }

    pub fn subscribe_signals(&self) -> broadcast::Receiver<Signal> {
        self.signal_tx.subscribe()
    }

    pub fn shm_write(&mut self, key: impl Into<String>, value: serde_json::Value) {
        self.shared.insert(key.into(), value);
    }

    pub fn shm_read(&self, key: &str) -> Option<&serde_json::Value> {
        self.shared.get(key)
    }

    pub fn create_pipe(&mut self, name: impl Into<String>) -> mpsc::Receiver<Vec<u8>> {
        let (tx, rx) = mpsc::channel(64);
        self.pipes.insert(name.into(), tx);
        rx
    }

    pub async fn pipe_write(&self, name: &str, data: Vec<u8>) -> bool {
        if let Some(tx) = self.pipes.get(name) {
            tx.send(data).await.is_ok()
        } else {
            false
        }
    }

    pub fn unregister(&mut self, pid: Pid) {
        self.inboxes.remove(&pid);
    }
}

impl Default for IpcBus {
    fn default() -> Self {
        Self::new()
    }
}
