use std::collections::{HashMap, HashSet};

use log::{debug, error, info, warn};
use mt_sea::net::{self, Packet};
use mt_sea::{coordinator::CoordinatorImpl, net::PacketKind};

use mt_net::{ActionPlan, COMPARE_NODE_NAME, RatPubRegisterKind, Rules, VariableHuman};
use mt_sea::{Coordinator, DISCONNECT_TIMEOUT_MS, Qos, ShipKind, WindData};

#[derive(Debug)]
enum MinotTask {
    AppendRule {
        variable: String,
        commands: Vec<VariableHuman>,
    },
    LockNext {
        unlock_first: bool,
    },
    RulesClear,
    SendRatAction {
        ship_name: String,
        var_name: String,
        data: ActionPlan,
    },
    WindDynamicVarReq(String),
    Unlock,
    RegisterShipAtVar {
        ship: String,
        var: String,
        kind: net::RatPubRegisterKind,
        node_mode: Qos,
    },
    /// A peer monitor on another node declared this ship dead.
    PeerDead {
        ship: String,
    },
    /// Push updated routes for a variable to all involved ships (async, runs in coord_rx).
    PushRoutesForVar {
        var: String,
    },
    /// Forward a Torpedo to all connected ships and then stop this coordinator.
    BroadcastTorpedo {
        dead_clients: Vec<String>,
    },
}

#[derive(Debug)]
enum WindTaskKind {
    Fix(Vec<WindData>),
    Dynamic,
}

#[derive(Debug)]
struct WindTask {
    kind: WindTaskKind,
    already_seen: bool,
}

pub fn run_coordinator(
    locked_start: bool,
    clients: HashSet<String>,
    rules: Rules,
    torpedo_tx: Option<tokio::sync::mpsc::Sender<()>>,
) {
    let coordinator_shutting_down = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
    let coordinator_shutting_down_in_coord = std::sync::Arc::clone(&coordinator_shutting_down);
    let coordinator_shutting_down_in_scope = std::sync::Arc::clone(&coordinator_shutting_down);

    // Zenoh handles coordinator discovery and port management
    let clients_wait_for_ack = std::sync::Arc::new(std::sync::RwLock::new(!clients.is_empty()));
    let rules = std::sync::Arc::new(std::sync::RwLock::new(rules));
    let rules_changer = std::sync::Arc::clone(&rules);

    let winds_var = std::sync::Arc::new(std::sync::RwLock::new(
        HashMap::<String, Vec<WindTask>>::new(),
    ));
    let winds_changer = std::sync::Arc::clone(&winds_var);

    // Bounded queue to apply backpressure when wind forwarding is slower than producers.
    let (wind_tx, mut wind_rx) = tokio::sync::mpsc::channel::<Vec<WindData>>(64);
    let wind_clients: std::sync::Arc<std::sync::RwLock<HashSet<String>>> =
        std::sync::Arc::new(std::sync::RwLock::new(HashSet::new()));

    // Shared set of currently connected client names (for Sonar responses)
    let connected_clients: std::sync::Arc<std::sync::RwLock<HashSet<String>>> =
        std::sync::Arc::new(std::sync::RwLock::new(HashSet::new()));

    // Shared set of best-effort client names (subset of connected_clients)
    let best_effort_clients: std::sync::Arc<std::sync::RwLock<HashSet<String>>> =
        std::sync::Arc::new(std::sync::RwLock::new(HashSet::new()));

    // Task for centralized coordinator task work that is generated by the tasks implemented after this
    let (coord_tx, mut coord_rx) = tokio::sync::mpsc::unbounded_channel::<MinotTask>();
    let coord_tx_new_client = coord_tx.clone();
    let cwa_write = std::sync::Arc::clone(&clients_wait_for_ack);
    tokio::spawn(async move {
        let coordinator =
            std::sync::Arc::new(CoordinatorImpl::new(None, clients_wait_for_ack).await);
        let coordinator_for_wind_dispatch = std::sync::Arc::clone(&coordinator);
        let wind_clients_for_dispatch = std::sync::Arc::clone(&wind_clients);

        tokio::spawn(async move {
            let mut waiting_for_wind = false;
            while let Some(data) = wind_rx.recv().await {
                let targets: Vec<String> = loop {
                    let current = wind_clients_for_dispatch
                        .read()
                        .unwrap()
                        .iter()
                        .cloned()
                        .collect::<Vec<_>>();
                    if !current.is_empty() {
                        if waiting_for_wind {
                            info!("Wind client connected; resuming queued wind forwarding.");
                            waiting_for_wind = false;
                        }
                        break current;
                    }
                    if !waiting_for_wind {
                        warn!(
                            "Received wind data but no connected winds; pausing forwarding until a wind client connects."
                        );
                        waiting_for_wind = true;
                    }
                    tokio::time::sleep(std::time::Duration::from_millis(100)).await;
                };

                for ship_name in targets {
                    let ship_known = coordinator_for_wind_dispatch
                        .rat_qs
                        .read()
                        .await
                        .contains_key(&ship_name);
                    if !ship_known {
                        warn!(
                            "Skipping stale wind target '{}' while forwarding wind data.",
                            ship_name
                        );
                        wind_clients_for_dispatch
                            .write()
                            .unwrap()
                            .remove(&ship_name);
                        continue;
                    }
                    if let Err(e) = coordinator_for_wind_dispatch
                        .blow_wind(ship_name.clone(), data.clone())
                        .await
                    {
                        error!("Error while blowing wind to {}: {}", ship_name, e);
                        wind_clients_for_dispatch
                            .write()
                            .unwrap()
                            .remove(&ship_name);
                    }
                }
            }
            debug!("wind_rx closed");
        });

        // unlock clients to send us stuff when all our expected clients are connected
        let total_clients_check_rats = std::sync::Arc::clone(&coordinator.rat_qs);
        let new_client_notify = coordinator.new_client_notify.clone();
        let (clients_collected_tx, mut clients_collected_rx) = tokio::sync::mpsc::channel(1);
        tokio::spawn(async move {
            debug!("waiting for all clients to be connected");
            CoordinatorImpl::ensure_clients_connected(
                total_clients_check_rats.clone(),
                clients,
                new_client_notify,
            )
            .await;
            debug!("all clients connected, sending ack to clients.");
            clients_collected_tx.send(()).await.unwrap();
            let clients = total_clients_check_rats.read().await;
            let ack = net::Packet {
                header: net::Header::default(),
                data: net::PacketKind::Acknowledge,
            };
            for (_client_name, client) in clients.iter() {
                if let Err(e) = client.sender.send(ack.clone()).await {
                    error!("Could not send ack to unlock client: {e}");
                }
            }
            // allow other clients to join after all predefined clients are connected
            let mut cwa = cwa_write.write().unwrap();
            *cwa = false;
        });

        let mut new_clients_chan = coordinator.sea.network_clients_chan.subscribe();
        let minot_client_connected = std::sync::Arc::new(std::sync::RwLock::new(false));
        let tasks_minot_connect = std::sync::Arc::clone(&minot_client_connected);
        let rules_change_for_disconnect = std::sync::Arc::clone(&rules_changer);
        let cc_for_spawn = std::sync::Arc::clone(&connected_clients);
        let be_for_spawn = std::sync::Arc::clone(&best_effort_clients);
        let wind_clients_for_spawn = std::sync::Arc::clone(&wind_clients);
        // Keep a clone for the coord_rx loop; torpedo_tx itself is moved into the inner spawn.
        let torpedo_tx_for_coord = torpedo_tx.clone();
        let client_handlers: std::sync::Arc<
            std::sync::Mutex<HashMap<String, tokio::task::AbortHandle>>,
        > = std::sync::Arc::new(std::sync::Mutex::new(HashMap::new()));
        tokio::spawn(async move {
            loop {
                match new_clients_chan.recv().await {
                    Ok(client) => {
                        let wind_tx_inner = wind_tx.clone();
                        match client.name {
                            ShipKind::Rat(name) => {
                                if name == COMPARE_NODE_NAME {
                                    let mut lock = tasks_minot_connect.write().unwrap();
                                    *lock = true;
                                    // New run starting: clear stale wind state so setup packets
                                    // from this run don't accumulate on top of previous ones.
                                    winds_changer.write().unwrap().clear();
                                }
                                // Abort any existing handler for this client name so the old
                                // zenoh subscriber (keyed by name) also exits and stops
                                // delivering stale packets to the old handler.
                                if let Some(old) = client_handlers.lock().unwrap().remove(&name) {
                                    old.abort();
                                }
                                cc_for_spawn.write().unwrap().insert(name.clone());
                                if client.node_mode == Qos::BestEffort {
                                    be_for_spawn.write().unwrap().insert(name.clone());
                                }
                                let mut client_news = client.recv.subscribe();
                                let inner_name = name.clone();
                                let rat_rules = std::sync::Arc::clone(&rules);
                                let rat_coord_tx = coord_tx_new_client.clone();
                                let winds_inner = std::sync::Arc::clone(&winds_changer);
                                let rules_change_for_disconnect_inner =
                                    std::sync::Arc::clone(&rules_change_for_disconnect);
                                let connected_clients_inner = std::sync::Arc::clone(&cc_for_spawn);
                                let best_effort_clients_inner =
                                    std::sync::Arc::clone(&be_for_spawn);
                                let handlers_for_task = std::sync::Arc::clone(&client_handlers);
                                let name_for_task = name.clone();
                                // spawn task to handle variable requests for this rat
                                let handle = tokio::spawn(async move {
                                    let minot_compare_name = COMPARE_NODE_NAME.to_string();
                                    match inner_name.as_str() {
                                        COMPARE_NODE_NAME => loop {
                                            match tokio::time::timeout(
                                                tokio::time::Duration::from_millis(DISCONNECT_TIMEOUT_MS),
                                                client_news.recv(),
                                            )
                                            .await
                                            {
                                                Ok(Ok((packet, _))) => match packet.data {
                                                    PacketKind::RuleAppend {
                                                        variable,
                                                        commands,
                                                    } => {
                                                        rat_coord_tx
                                                            .send(MinotTask::AppendRule {
                                                                variable,
                                                                commands,
                                                            })
                                                            .unwrap();
                                                    }
                                                    PacketKind::RulesClear => {
                                                        rat_coord_tx
                                                            .send(MinotTask::RulesClear)
                                                            .unwrap();
                                                    }
                                                    PacketKind::LockNext { unlock_first } => {
                                                        rat_coord_tx
                                                            .send(MinotTask::LockNext {
                                                                unlock_first,
                                                            })
                                                            .unwrap();
                                                    }
                                                    PacketKind::Unlock => {
                                                        rat_coord_tx
                                                            .send(MinotTask::Unlock)
                                                            .unwrap();
                                                    }
                                                    PacketKind::WindDynamic(var) => {
                                                        let mut w = winds_inner.write().unwrap();
                                                        let ty = WindTask {
                                                            already_seen: false,
                                                            kind: WindTaskKind::Dynamic,
                                                        };
                                                        match w.get_mut(&var) {
                                                            Some(wts) => {
                                                                wts.push(ty);
                                                            }
                                                            None => {
                                                                w.insert(var, vec![ty]);
                                                            }
                                                        }
                                                    }
                                                    PacketKind::Wind(bwd) => {
                                                        let mut send_now =
                                                            Vec::with_capacity(bwd.len());
                                                        for wd in bwd {
                                                            match wd.at_var {
                                                                Some(var) => {
                                                                    let mut w = winds_inner
                                                                        .write()
                                                                        .unwrap();
                                                                    match w.get_mut(&var) {
                                                                        Some(wt) => {
                                                                            wt.push(WindTask { kind: WindTaskKind::Fix(vec![wd.data]), already_seen: false });
                                                                        }
                                                                        None => {
                                                                            w.insert(var,
                                                                            vec![WindTask {
                                                                                already_seen: false,
                                                                                kind: WindTaskKind::Fix(vec![wd.data])
                                                                            }]);
                                                                        }
                                                                    }
                                                                }
                                                                None => {
                                                                    send_now.push(wd.data);
                                                                }
                                                            }
                                                        }

                                                        if !send_now.is_empty() {
                                                            if let Err(e) = wind_tx_inner.send(send_now).await {
                                                                error!(
                                                                    "Could not enqueue wind data from Minot TUI for forwarding: {}",
                                                                    e
                                                                );
                                                            }
                                                        }
                                                    }

                                                    PacketKind::Heartbeat => {
                                                        debug!(
                                                            "Received heartbeat from Minot TUI"
                                                        );
                                                    }
                                                    PacketKind::PeerDead { ship } => {
                                                        rat_coord_tx
                                                            .send(MinotTask::PeerDead { ship })
                                                            .unwrap();
                                                    }
                                                    _ => {
                                                        warn!(
                                                            "Received unexpected packet from Minot TUI"
                                                        );
                                                    }
                                                },
                                                Ok(Err(
                                                    tokio::sync::broadcast::error::RecvError::Lagged(
                                                        n,
                                                    ),
                                                )) => {
                                                    warn!(
                                                        "Minot TUI receiver lagged by {} messages",
                                                        n
                                                    );
                                                }
                                                Ok(Err(
                                                    tokio::sync::broadcast::error::RecvError::Closed,
                                                )) => {
                                                    info!("Minot TUI disconnected");
                                                    connected_clients_inner
                                                        .write()
                                                        .unwrap()
                                                        .remove(COMPARE_NODE_NAME);
                                                    return;
                                                }
                                                Err(_elapsed) => {
                                                    info!(
                                                        "Minot TUI timed out (no heartbeat), treating as disconnected"
                                                    );
                                                    connected_clients_inner
                                                        .write()
                                                        .unwrap()
                                                        .remove(COMPARE_NODE_NAME);
                                                    return;
                                                }
                                            }
                                        },
                                        _ => {
                                            loop {
                                                match client_news.recv().await {
                                                    Ok((packet, _)) => {
                                                        match packet.data {
                                                            PacketKind::RegisterShipAtVar {
                                                                ship,
                                                                var,
                                                                kind,
                                                                node_mode,
                                                            } => {
                                                                // Acknowledge is sent by coord_rx after validation
                                                                rat_coord_tx.send(MinotTask::RegisterShipAtVar {ship,var,kind,node_mode},).unwrap();
                                                            }
                                                            PacketKind::VariableTaskRequest(
                                                                variable,
                                                            ) => {
                                                                let (minot_actions, mut my_actions) = {
                                                                    let rat_rules_ref =
                                                                        rat_rules.read().unwrap();

                                                                    (
                                                                        mt_sea::get_strategies(
                                                                            &rat_rules_ref,
                                                                            &minot_compare_name,
                                                                            variable.clone(),
                                                                            Some(&inner_name),
                                                                        ),
                                                                        mt_sea::get_strategies(
                                                                            &rat_rules_ref,
                                                                            &inner_name,
                                                                            variable.clone(),
                                                                            None,
                                                                        ),
                                                                    )
                                                                };
                                                                for action in minot_actions {
                                                                    if !matches!(
                                                                        action,
                                                                        ActionPlan::Sail
                                                                    ) {
                                                                        rat_coord_tx.send(
                                                                    MinotTask::SendRatAction {
                                                                        ship_name: minot_compare_name
                                                                            .clone(),
                                                                        var_name: variable.clone(),
                                                                        data: action,
                                                                    },
                                                                )
                                                                .unwrap();
                                                                    }
                                                                }

                                                                if my_actions.is_empty() {
                                                                    my_actions
                                                                        .push(ActionPlan::Sail);
                                                                }

                                                                debug!(
                                                                    "sending actions: {:?}",
                                                                    my_actions
                                                                );

                                                                // answer the client that asked
                                                                for action in my_actions {
                                                                    rat_coord_tx.send(MinotTask::SendRatAction {
                                                                        ship_name: inner_name.clone(),
                                                                        var_name: variable.clone(),
                                                                        data: action,
                                                                    })
                                                                    .unwrap();
                                                                }

                                                                let mut pending_fixed =
                                                                    Vec::<Vec<WindData>>::new();
                                                                {
                                                                    let mut winds =
                                                                        winds_inner.write().unwrap();

                                                                    let mut asked_for_dynamic = false;
                                                                    if let Some(wt) =
                                                                        winds.get_mut(&variable)
                                                                    {
                                                                        for wte in wt.iter_mut() {
                                                                            if !wte.already_seen {
                                                                                match &mut wte.kind {
                                                                                    WindTaskKind::Fix(wind_datas) => {
                                                                                        pending_fixed.push(wind_datas.clone());
                                                                                    }
                                                                                    WindTaskKind::Dynamic => {
                                                                                        if asked_for_dynamic {
                                                                                            wte.already_seen = true;
                                                                                            continue;
                                                                                        }
                                                                                        asked_for_dynamic = true;

                                                                                        info!(
                                                                                            "asking Minot TUI for dyn for {}",
                                                                                            &variable
                                                                                        );
                                                                                        let ret = rat_coord_tx.send(
                                                                                            MinotTask::WindDynamicVarReq(
                                                                                                variable.clone(),
                                                                                            ),
                                                                                        );
                                                                                        match ret {
                                                                                            Ok(_) => {}
                                                                                            Err(e) => {
                                                                                                error!("Could not find connected Minot TUI for asking dynamic wind. {e}");
                                                                                            }
                                                                                        }
                                                                                    }
                                                                                };
                                                                                wte.already_seen = true;
                                                                            }
                                                                        }
                                                                    }

                                                                    // set all other to be sent again
                                                                    for (var, wind) in winds.iter_mut() {
                                                                        if var == &variable {
                                                                            continue;
                                                                        }

                                                                        wind.iter_mut().for_each(
                                                                            |wte| {
                                                                                wte.already_seen =
                                                                                    false;
                                                                            },
                                                                        );
                                                                    }
                                                                }

                                                                for wind_data in pending_fixed {
                                                                    if let Err(e) =
                                                                        wind_tx_inner.send(wind_data).await
                                                                    {
                                                                        error!(
                                                                            "Could not enqueue stored wind data for forwarding: {}",
                                                                            e
                                                                        );
                                                                    }
                                                                }
                                                            }
                                                            PacketKind::Heartbeat => {
                                                                debug!(
                                                                    "Received heartbeat from {}",
                                                                    inner_name
                                                                );
                                                                let _ = client
                                                                    .send
                                                                    .send(Packet {
                                                                        header: net::Header::default(),
                                                                        data: PacketKind::Heartbeat,
                                                                    })
                                                                    .await;
                                                            }
                                                            PacketKind::Sonar => {
                                                                let reliable = connected_clients_inner
                                                                    .read()
                                                                    .unwrap()
                                                                    .clone();
                                                                let best_effort = best_effort_clients_inner
                                                                    .read()
                                                                    .unwrap()
                                                                    .clone();
                                                                let _ = client
                                                                    .send
                                                                    .send(Packet {
                                                                        header: net::Header::default(),
                                                                        data: PacketKind::ClientsHash { reliable, best_effort },
                                                                    })
                                                                    .await;
                                                            }
                                                            PacketKind::Wind(bwd) => {
                                                                let mut send_now =
                                                                    Vec::with_capacity(bwd.len());
                                                                for wd in bwd {
                                                                    match wd.at_var {
                                                                        Some(var) => {
                                                                            let mut w = winds_inner
                                                                                .write()
                                                                                .unwrap();
                                                                            match w.get_mut(&var) {
                                                                                Some(wt) => {
                                                                                    wt.push(WindTask { kind: WindTaskKind::Fix(vec![wd.data]), already_seen: false });
                                                                                }
                                                                                None => {
                                                                                    w.insert(var,
                                                                                    vec![WindTask {
                                                                                        already_seen: false,
                                                                                        kind: WindTaskKind::Fix(vec![wd.data])
                                                                                    }]);
                                                                                }
                                                                            }
                                                                        }
                                                                        None => {
                                                                            send_now.push(wd.data);
                                                                        }
                                                                    }
                                                                }
                                                                if !send_now.is_empty() {
                                                                    if let Err(e) = wind_tx_inner.send(send_now).await {
                                                                        error!(
                                                                            "Could not enqueue wind data from client '{}' for forwarding: {}",
                                                                            inner_name,
                                                                            e
                                                                        );
                                                                    }
                                                                }
                                                            }
                                                            PacketKind::Torpedo(dead_clients) => {
                                                                debug!(
                                                                    "Received Torpedo: {:?}",
                                                                    dead_clients
                                                                );
                                                                let _ = client
                                                                    .send
                                                                    .send(Packet {
                                                                        header: net::Header::default(),
                                                                        data: PacketKind::Acknowledge,
                                                                    })
                                                                    .await;
                                                                rat_coord_tx
                                                                    .send(MinotTask::BroadcastTorpedo {
                                                                        dead_clients,
                                                                    })
                                                                    .unwrap();
                                                            }
                                                            PacketKind::PeerDead { ship } => {
                                                                debug!(
                                                                    "PeerDead reported by {}: {}",
                                                                    inner_name, ship
                                                                );
                                                                rat_coord_tx
                                                                    .send(MinotTask::PeerDead { ship })
                                                                    .unwrap();
                                                            }
                                                            _ => {
                                                                warn!(
                                                                    "Received unexpected packet {:?}",
                                                                    packet.data
                                                                );
                                                            }
                                                        }
                                                    }
                                                    Err(
                                                        tokio::sync::broadcast::error::RecvError::Closed,
                                                    ) => {
                                                        if client.remove_rules_on_disconnect {
                                                            let affected_vars = rules_change_for_disconnect_inner
                                                                .read()
                                                                .unwrap()
                                                                .all_vars_for_ship(&inner_name);
                                                            rules_change_for_disconnect_inner
                                                                .write()
                                                                .unwrap()
                                                                .remove_client(&inner_name);
                                                            for var in affected_vars {
                                                                rat_coord_tx
                                                                    .send(MinotTask::PushRoutesForVar { var })
                                                                    .ok();
                                                            }
                                                        }
                                                        connected_clients_inner
                                                            .write()
                                                            .unwrap()
                                                            .remove(&inner_name);
                                                        best_effort_clients_inner
                                                            .write()
                                                            .unwrap()
                                                            .remove(&inner_name);
                                                        return;
                                                    }
                                                    Err(
                                                        tokio::sync::broadcast::error::RecvError::Lagged(
                                                            n,
                                                        ),
                                                    ) => {
                                                        warn!(
                                                            "Client {} receiver lagged by {} messages",
                                                            inner_name, n
                                                        );
                                                    }
                                                }
                                            }
                                        }
                                    };
                                });
                                handlers_for_task
                                    .lock()
                                    .unwrap()
                                    .insert(name_for_task, handle.abort_handle());
                            }
                            ShipKind::Wind(name) => {
                                if let Some(old) = client_handlers.lock().unwrap().remove(&name) {
                                    old.abort();
                                }
                                wind_clients_for_spawn.write().unwrap().insert(name.clone());
                                let mut client_news = client.recv.subscribe();
                                let inner_name_rx = name.clone();
                                let winds_on_disconnect = std::sync::Arc::clone(&winds_changer);
                                let wind_clients_on_disconnect =
                                    std::sync::Arc::clone(&wind_clients_for_spawn);
                                let wind_recv_handle = tokio::spawn(async move {
                                    loop {
                                        match client_news.recv().await {
                                            Ok((packet, _)) => match packet.data {
                                                PacketKind::Acknowledge => {
                                                    debug!(
                                                        "Received wind ack from {inner_name_rx}"
                                                    );
                                                }
                                                PacketKind::Heartbeat => {
                                                    debug!(
                                                        "Received heartbeat from wind {inner_name_rx}"
                                                    );
                                                }
                                                _ => {
                                                    warn!(
                                                        "Unexpected packet from wind: {packet:?}"
                                                    );
                                                }
                                            },
                                            Err(e) => {
                                                match e {
                                                    tokio::sync::broadcast::error::RecvError::Closed => {},
                                                    _ => {
                                                        error!(
                                                            "Could not receive packet from wind client {inner_name_rx}: {e}"
                                                        );
                                                    }
                                                }
                                                info!(
                                                    "Wind client {inner_name_rx} disconnected, clearing wind state"
                                                );
                                                wind_clients_on_disconnect
                                                    .write()
                                                    .unwrap()
                                                    .remove(&inner_name_rx);
                                                winds_on_disconnect.write().unwrap().clear();
                                                return;
                                            }
                                        }
                                    }
                                });
                                client_handlers
                                    .lock()
                                    .unwrap()
                                    .insert(name, wind_recv_handle.abort_handle());
                            }
                        }
                    }
                    Err(e) => {
                        error!("Could not receive new client: {}", e);
                    }
                }
            }
        });

        // wait here until all clients are connected.
        clients_collected_rx.recv().await;

        let mut lock_next = locked_start;
        while let Some(task) = coord_rx.recv().await {
            match task {
                MinotTask::WindDynamicVarReq(var) => {
                    {
                        let minot_tui_connected = minot_client_connected.read().unwrap();
                        if !*minot_tui_connected {
                            error!(
                                "Wants to ask Minot TUI for wind for var {var} but is not connected."
                            );
                            continue;
                        }
                    }

                    let ret = coordinator
                        .rat_send(
                            COMPARE_NODE_NAME.to_owned(),
                            PacketKind::VariableTaskRequest(var),
                        )
                        .await;
                    if let Err(e) = ret {
                        error!("Could not send Dynamic Var Request to LH TUI: {e}");
                    }
                }
                MinotTask::AppendRule { variable, commands } => {
                    let mut current_rules = rules_changer.write().unwrap();
                    current_rules.insert(variable, commands);
                    info!("added new rule");
                }
                MinotTask::RulesClear => {
                    let mut current_rules = rules_changer.write().unwrap();
                    current_rules.clear();
                    info!("cleared all rules");
                }
                MinotTask::LockNext { unlock_first } => {
                    info!("locking next");
                    if unlock_first {
                        let clients = coordinator.rat_qs.read().await;
                        for (c, _) in clients.iter() {
                            if let Err(e) = coordinator
                                .rat_send(c.clone(), PacketKind::Acknowledge)
                                .await
                            {
                                error!("Error while sending ack for unlocking to Rat {}: {}", c, e);
                                std::process::exit(1);
                            }
                        }
                    }

                    lock_next = true;
                }
                MinotTask::Unlock => {
                    let clients = coordinator.rat_qs.read().await;
                    for (c, _) in clients.iter() {
                        if let Err(e) = coordinator
                            .rat_send(c.clone(), PacketKind::Acknowledge)
                            .await
                        {
                            error!("Error while sending ack for unlocking to Rat {}: {}", c, e);
                            std::process::exit(1);
                        }
                    }
                    lock_next = false;
                }
                MinotTask::SendRatAction {
                    ship_name,
                    var_name,
                    mut data,
                } => {
                    {
                        let minot_tui_connected = minot_client_connected.read().unwrap();
                        if !*minot_tui_connected {
                            if ship_name == COMPARE_NODE_NAME {
                                continue;
                            }
                            data = match &data {
                                ActionPlan::Sail => data.clone(),
                                ActionPlan::Shoot { target, id } => {
                                    let ntargets = target
                                        .iter()
                                        .filter(|&t| t != COMPARE_NODE_NAME)
                                        .cloned()
                                        .collect::<Vec<_>>();
                                    if ntargets.is_empty() {
                                        ActionPlan::Sail
                                    } else {
                                        ActionPlan::Shoot {
                                            target: ntargets,
                                            id: *id,
                                        }
                                    }
                                }
                                ActionPlan::Catch { source, id: _ } => {
                                    if source == COMPARE_NODE_NAME {
                                        ActionPlan::Sail
                                    } else {
                                        data.clone()
                                    }
                                }
                            };
                        }
                    }

                    let lock_next = lock_next && ship_name != COMPARE_NODE_NAME;

                    let is_be = coordinator
                        .be_subscriptions
                        .read()
                        .unwrap()
                        .contains(&(ship_name.clone(), var_name.clone()))
                        || best_effort_clients.read().unwrap().contains(&ship_name);

                    if is_be {
                        let coord_inner = coordinator.clone();
                        let var_name_inner = var_name.clone();
                        let ship_name_inner = ship_name.clone();
                        tokio::spawn(async move {
                            let _ = coord_inner
                                .rat_action_send(
                                    ship_name_inner,
                                    var_name_inner,
                                    data,
                                    lock_next,
                                    true,
                                )
                                .await;
                        });
                    } else if let Err(e) = coordinator
                        .rat_action_send(ship_name.clone(), var_name, data, lock_next, false)
                        .await
                    {
                        error!("Error while sending action to Rat {}: {}", ship_name, e);
                        std::process::exit(1);
                    }
                }
                MinotTask::RegisterShipAtVar {
                    ship,
                    var,
                    kind,
                    node_mode,
                } => {
                    // Track BE publishers
                    if kind == net::RatPubRegisterKind::Publish && node_mode == Qos::BestEffort {
                        coordinator
                            .be_publisher_vars
                            .write()
                            .unwrap()
                            .insert(var.clone());

                        // Notify any existing reliable subscribers that this var now has a BE publisher.
                        let reliable_subs: Vec<String> = {
                            let current_rules = rules_changer.read().unwrap();
                            let be_subs = coordinator.be_subscriptions.read().unwrap();
                            current_rules
                                .subscriber_ships(&var)
                                .into_iter()
                                .filter(|s| !be_subs.contains(&(s.clone(), var.clone())))
                                .collect()
                        };
                        if !reliable_subs.is_empty() {
                            let error_msg = format!(
                                "Topic '{}' has a best-effort publisher. A reliable subscriber cannot connect to a best-effort publisher. \
                                 Pass Qos::BestEffort to create_subscriber to opt in to best-effort delivery.",
                                var
                            );
                            for sub_ship in reliable_subs {
                                error!(
                                    "Notifying existing reliable subscriber '{}' of BE publisher on '{}'",
                                    sub_ship, var
                                );
                                let _ = coordinator
                                    .rat_send(
                                        sub_ship,
                                        net::PacketKind::RegistrationError(error_msg.clone()),
                                    )
                                    .await;
                            }
                        }
                    }

                    // Reject reliable subscribers connecting to BE-published topics
                    if (kind == net::RatPubRegisterKind::Subscribe
                        || kind == net::RatPubRegisterKind::Scope)
                        && node_mode == Qos::Reliable
                        && coordinator.be_publisher_vars.read().unwrap().contains(&var)
                    {
                        let msg = format!(
                            "Topic '{}' has a best-effort publisher. A reliable subscriber cannot connect to a best-effort publisher. \
                             Pass Qos::BestEffort to create_subscriber to opt in to best-effort delivery.",
                            var
                        );
                        error!("{}", &msg);
                        let _ = coordinator
                            .rat_send(ship, net::PacketKind::RegistrationError(msg))
                            .await;
                        continue;
                    }

                    if node_mode == Qos::BestEffort && kind == net::RatPubRegisterKind::Subscribe {
                        coordinator
                            .be_subscriptions
                            .write()
                            .unwrap()
                            .insert((ship.clone(), var.clone()));
                    }
                    let mt_net_kind = match kind {
                        net::RatPubRegisterKind::Publish => RatPubRegisterKind::Publish,
                        net::RatPubRegisterKind::Subscribe | net::RatPubRegisterKind::Scope => {
                            RatPubRegisterKind::Subscribe
                        }
                    };
                    let already_registered = {
                        let current_rules = rules_changer.read().unwrap();
                        current_rules
                            .cache
                            .get(&var)
                            .map(|s| s.contains(&(ship.clone(), mt_net_kind.clone())))
                            .unwrap_or(false)
                    };
                    // Single-subscription prevention
                    if already_registered {
                        debug!(
                            "Ship {} already registered for var {} as {:?}, skipping",
                            ship, var, mt_net_kind
                        );
                        // Still send Acknowledge for duplicate registrations
                        let _ = coordinator
                            .rat_send(ship, net::PacketKind::Acknowledge)
                            .await;
                        continue;
                    }
                    info!(
                        "Registering for topics ({:?}, {:?}, {:?})",
                        &ship, &var, &mt_net_kind
                    );
                    {
                        let mut current_rules = rules_changer.write().unwrap();
                        current_rules.register(var.clone(), ship.clone(), mt_net_kind);
                    }
                    // Wait for the registering ship to be present in rat_qs before
                    // pushing routes, so convert_action can resolve its address.
                    coordinator.wait_for_client(&ship).await;
                    // Push updated routes to all affected ships after registration.
                    let rules_snapshot = rules_changer.read().unwrap().clone();
                    coordinator
                        .push_routes_for_var(&var, &rules_snapshot)
                        .await
                        .ok();
                    let _ = coordinator
                        .rat_send(ship, net::PacketKind::Acknowledge)
                        .await;
                }
                MinotTask::PushRoutesForVar { var } => {
                    let rules_snapshot = rules_changer.read().unwrap().clone();
                    if let Err(e) = coordinator.push_routes_for_var(&var, &rules_snapshot).await {
                        debug!("PushRoutesForVar for {}: {}", var, e);
                    }
                }
                MinotTask::BroadcastTorpedo { dead_clients } => {
                    info!("Broadcasting Torpedo: {:?}", dead_clients);
                    let targets: Vec<(String, tokio::sync::mpsc::Sender<Packet>)> = {
                        let clients = coordinator.rat_qs.read().await;
                        clients
                            .iter()
                            .map(|(name, info)| (name.clone(), info.sender.clone()))
                            .collect()
                    };

                    for (name, sender) in targets {
                        if let Err(e) = sender
                            .send(Packet {
                                header: net::Header::default(),
                                data: PacketKind::Torpedo(dead_clients.clone()),
                            })
                            .await
                        {
                            debug!("Could not forward Torpedo to {}: {}", name, e);
                        }
                    }

                    coordinator_shutting_down_in_coord
                        .store(true, std::sync::atomic::Ordering::SeqCst);
                    if let Some(tx) = &torpedo_tx_for_coord {
                        let _ = tx.send(()).await;
                    }
                }
                MinotTask::PeerDead { ship } => {
                    info!("PeerDead: {}", ship);
                    let is_reliable = !best_effort_clients.read().unwrap().contains(&ship);
                    let affected_vars = rules_changer.read().unwrap().all_vars_for_ship(&ship);
                    rules_changer.write().unwrap().remove_client(&ship);
                    let rules_snapshot = rules_changer.read().unwrap().clone();
                    for var in &affected_vars {
                        coordinator
                            .push_routes_for_var(var, &rules_snapshot)
                            .await
                            .ok();
                    }
                    connected_clients.write().unwrap().remove(&ship);
                    best_effort_clients.write().unwrap().remove(&ship);
                    if is_reliable {
                        let dead_clients = vec![ship.clone()];
                        info!("Reliable peer lost, firing Torpedo: {:?}", dead_clients);
                        let targets: Vec<(String, tokio::sync::mpsc::Sender<Packet>)> = {
                            let clients = coordinator.rat_qs.read().await;
                            clients
                                .iter()
                                .map(|(name, info)| (name.clone(), info.sender.clone()))
                                .collect()
                        };

                        for (name, sender) in targets {
                            if let Err(e) = sender
                                .send(Packet {
                                    header: net::Header::default(),
                                    data: PacketKind::Torpedo(dead_clients.clone()),
                                })
                                .await
                            {
                                debug!("Could not forward Torpedo to {}: {}", name, e);
                            }
                        }

                        coordinator_shutting_down_in_coord
                            .store(true, std::sync::atomic::Ordering::SeqCst);
                        if let Some(tx) = &torpedo_tx_for_coord {
                            let _ = tx.send(()).await;
                        }
                    }
                }
            }
        }
        debug!("coord_rx closed");
    });

    #[cfg(feature = "embed-scope")]
    tokio::spawn(async move {
        if let Err(e) = mt_scope::Scope::create(mt_scope::ScopeConfig {
            name: "minot_scope".to_string(),
            mode: mt_scope::Qos::Reliable,
        })
        .await
        {
            if coordinator_shutting_down_in_scope.load(std::sync::atomic::Ordering::SeqCst) {
                debug!("Embedded scope stopped during coordinator shutdown: {}", e);
            } else if e.to_string().contains("Receiver task failed to start") {
                debug!(
                    "Embedded scope stopped (likely due to program shutdown): {}",
                    e
                );
            } else {
                error!("Embedded scope exited with error: {}", e);
            }
        }
    });
}

/// Attempt to start a coordinator with the given configuration.
///
/// Acquires an exclusive lock file at `/tmp/minot-coord.lock`.
/// Returns `true` if the coordinator was started, `false` if one was already running.
/// Idempotent: safe to call concurrently — only one will win the lock.
pub fn try_start_with_rules(
    locked_start: bool,
    clients: HashSet<String>,
    rules: Rules,
    torpedo_tx: Option<tokio::sync::mpsc::Sender<()>>,
) -> bool {
    let lock_file_path =
        std::env::temp_dir().join(format!("minot-coord_{}.lock", users::get_current_uid()));
    let lock_file = match std::fs::File::create(&lock_file_path) {
        Ok(f) => f,
        Err(e) => {
            log::error!("Failed to create coordinator lock file: {}", e);
            return false;
        }
    };
    if fs2::FileExt::try_lock_exclusive(&lock_file).is_err() {
        log::debug!("Coordinator already running, skipping embedded start.");
        return false;
    }
    log::info!("Starting embedded coordinator...");
    run_coordinator(locked_start, clients, rules, torpedo_tx);
    // Keep lock file alive until the tokio runtime exits
    tokio::spawn(async move {
        let _lock = lock_file;
        std::future::pending::<()>().await
    });
    true
}

/// Start a coordinator with empty rules.
///
/// Idempotent: does nothing if a coordinator is already running.
/// Safe to call from any async context — spawns background tasks and returns immediately.
pub fn start_default() {
    try_start_with_rules(false, HashSet::new(), Rules::new(), None);
}

pub fn start_default_with_torpedo(torpedo_tx: Option<tokio::sync::mpsc::Sender<()>>) {
    try_start_with_rules(false, HashSet::new(), Rules::new(), torpedo_tx);
}
