//! The RunCommand plugin allows defining and executing remote commands.

use hashbrown::HashMap;
use serde::{Deserialize, Serialize};

#[cfg(not(feature = "std"))]
use alloc::string::String;

use crate::packet::{NetworkPacket, NetworkPacketBody};

/// A value of the map in the list of commands of the [`RunCommandPacket::command_list`] field.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RunCommandValue {
    /// A displayed name for the command.
    pub name: String,

    /// The command to execute.
    pub command: String,
}

/// This packet is a list of available commands.
///
/// <https://invent.kde.org/network/kdeconnect-meta/blob/master/protocol.md#kdeconnectruncommand>
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct RunCommandPacket {
    /// A JSON-serialized dictionary of commands that the device offers. The key is sent in a
    /// kdeconnect.runcommand.request packet to execute its corresponding command. Each value has
    /// a name field and a command field, e.g
    /// `{ "command-id1": { "name": "Command", "command": "/path/to/command1" } }`.
    pub command_list: String,
}

/// This packet is a runcommand status update.
///
/// <https://invent.kde.org/network/kdeconnect-meta/blob/master/protocol.md#kdeconnectruncommandrequest>
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct RunCommandRequestPacket {
    /// If the packet body contains this field it is a request to execute the command with the ID key.
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub key: Option<String>,

    /// If the packet body contains this field it is a request for the command list.
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub request_command_list: Option<bool>,
}

impl NetworkPacket {
    /// Make a [`NetworkPacket`] with a [`RunCommandPacket`] payload listing all available commands.
    pub fn run_command_list_response(command_list: &HashMap<String, RunCommandValue>) -> Self {
        NetworkPacket::new(NetworkPacketBody::RunCommand(RunCommandPacket {
            command_list: serde_json::to_string(command_list).expect("malformed command list JSON"),
        }))
    }

    /// Make a [`NetworkPacket`] with a [`RunCommandRequestPacket`] payload ready to request the
    /// list of commands.
    pub fn run_command_request_list() -> Self {
        NetworkPacket::new(NetworkPacketBody::RunCommandRequest(RunCommandRequestPacket {
            key: None,
            request_command_list: Some(true),
        }))
    }

    /// Make a [`NetworkPacket`] with a [`RunCommandRequestPacket`] payload asking to execute the
    /// provided command.
    pub fn run_command_exec<T: Into<String>>(key: T) -> Self {
        NetworkPacket::new(NetworkPacketBody::RunCommandRequest(RunCommandRequestPacket {
            key: Some(key.into()),
            request_command_list: None,
        }))
    }
}
