use std::collections::HashMap;

use komodo_client::entities::{
  SearchCombinator,
  docker::{
    SwarmLists,
    config::SwarmConfig,
    node::{NodeSpecAvailabilityEnum, NodeSpecRoleEnum, SwarmNode},
    secret::SwarmSecret,
    service::SwarmService,
    stack::SwarmStackLists,
    swarm::SwarmInspectInfo,
    task::SwarmTask,
  },
  update::Log,
};
use resolver_api::Resolve;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize, Resolve)]
#[response(PollSwarmStatusResponse)]
#[error(anyhow::Error)]
pub struct PollSwarmStatus {}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PollSwarmStatusResponse {
  /// Inspect swarm response
  pub inspect: Option<SwarmInspectInfo>,
  pub lists: SwarmLists,
}

// ======
//  Node
// ======

#[derive(Debug, Clone, Serialize, Deserialize, Resolve)]
#[response(SwarmNode)]
#[error(anyhow::Error)]
pub struct InspectSwarmNode {
  pub node: String,
}

/// `docker node rm [OPTIONS] NODE [NODE...]`
///
/// https://docs.docker.com/reference/cli/docker/node/rm/
#[derive(Debug, Clone, Serialize, Deserialize, Resolve)]
#[response(Log)]
#[error(anyhow::Error)]
pub struct RmSwarmNodes {
  pub nodes: Vec<String>,
  pub force: bool,
}

/// `docker node update [OPTIONS] NODE`
///
/// https://docs.docker.com/reference/cli/docker/node/update/
#[derive(Debug, Clone, Serialize, Deserialize, Resolve)]
#[response(Log)]
#[error(anyhow::Error)]
pub struct UpdateSwarmNode {
  pub node: String,
  pub availability: Option<NodeSpecAvailabilityEnum>,
  /// Add metadata to a swarm node using node labels (`key=value`).
  /// You can specify a node label as a key with an empty value.
  pub label_add: Option<HashMap<String, Option<String>>>,
  /// Remove labels by the label key.
  pub label_rm: Option<Vec<String>>,
  /// Update the node role (`worker`, `manager`)
  pub role: Option<NodeSpecRoleEnum>,
}

// =========
//  Service
// =========

#[derive(Debug, Clone, Serialize, Deserialize, Resolve)]
#[response(SwarmService)]
#[error(anyhow::Error)]
pub struct InspectSwarmService {
  pub service: String,
}

/// Get a swarm service's logs.
///
/// https://docs.docker.com/reference/cli/docker/service/logs/
#[derive(Debug, Clone, Serialize, Deserialize, Resolve)]
#[response(Log)]
#[error(anyhow::Error)]
pub struct GetSwarmServiceLog {
  /// The name or id of service.
  /// Also accepts task id.
  pub service: String,
  /// Pass `--tail` for only recent log contents. Max of 5000
  #[serde(default = "default_tail")]
  pub tail: u64,
  /// Enable `--timestamps`
  #[serde(default)]
  pub timestamps: bool,
  /// Enable `--no-task-ids`
  #[serde(default)]
  pub no_task_ids: bool,
  /// Enable `--no-resolve`
  #[serde(default)]
  pub no_resolve: bool,
  /// Enable `--details`
  #[serde(default)]
  pub details: bool,
}

fn default_tail() -> u64 {
  50
}

//

/// Search a swarm service's logs.
///
/// https://docs.docker.com/reference/cli/docker/service/logs/
#[derive(Debug, Clone, Serialize, Deserialize, Resolve)]
#[response(Log)]
#[error(anyhow::Error)]
pub struct GetSwarmServiceLogSearch {
  /// The name or id of service.
  /// Also accepts task id.
  pub service: String,
  /// The search terms.
  pub terms: Vec<String>,
  /// And: Only lines matching all terms
  /// Or: Lines matching any one of the terms
  #[serde(default)]
  pub combinator: SearchCombinator,
  /// Invert the search (search for everything not matching terms)
  #[serde(default)]
  pub invert: bool,
  /// Enable `--timestamps`
  #[serde(default)]
  pub timestamps: bool,
  /// Enable `--no-task-ids`
  #[serde(default)]
  pub no_task_ids: bool,
  /// Enable `--no-resolve`
  #[serde(default)]
  pub no_resolve: bool,
  /// Enable `--details`
  #[serde(default)]
  pub details: bool,
}

/// `docker service rm SERVICE [SERVICE...]`
///
/// https://docs.docker.com/reference/cli/docker/service/rm/
#[derive(Debug, Clone, Serialize, Deserialize, Resolve)]
#[response(Log)]
#[error(anyhow::Error)]
pub struct RmSwarmServices {
  pub services: Vec<String>,
}

// ======
//  Task
// ======

#[derive(Debug, Clone, Serialize, Deserialize, Resolve)]
#[response(SwarmTask)]
#[error(anyhow::Error)]
pub struct InspectSwarmTask {
  pub task: String,
}

// ========
//  Secret
// ========

#[derive(Debug, Clone, Serialize, Deserialize, Resolve)]
#[response(SwarmSecret)]
#[error(anyhow::Error)]
pub struct InspectSwarmSecret {
  pub secret: String,
}

// ========
//  Config
// ========

#[derive(Debug, Clone, Serialize, Deserialize, Resolve)]
#[response(Vec<SwarmConfig>)]
#[error(anyhow::Error)]
pub struct InspectSwarmConfig {
  pub config: String,
}

// =======
//  Stack
// =======

#[derive(Debug, Clone, Serialize, Deserialize, Resolve)]
#[response(SwarmStackLists)]
#[error(anyhow::Error)]
pub struct InspectSwarmStack {
  /// The swarm stack name
  pub stack: String,
}

/// `docker stack rm [OPTIONS] STACK [STACK...]`
///
/// https://docs.docker.com/reference/cli/docker/stack/rm/
#[derive(Debug, Clone, Serialize, Deserialize, Resolve)]
#[response(Log)]
#[error(anyhow::Error)]
pub struct RmSwarmStacks {
  pub stacks: Vec<String>,
  /// Do not wait for stack removal
  pub detach: bool,
}
