use formatting::format_serror;
use komodo_client::{
  api::execute::*,
  entities::{permission::PermissionLevel, swarm::Swarm},
};
use mogh_resolver::Resolve;

use crate::{
  api::execute::ExecuteArgs,
  helpers::{swarm::swarm_request, update::update_update},
  monitor::refresh_swarm_cache,
  permission::get_check_permissions,
};

impl Resolve<ExecuteArgs> for RemoveSwarmNodes {
  #[instrument(
    "RemoveSwarmNodes",
    skip_all,
    fields(
      task_id = task_id.to_string(),
      operator = user.id,
      update_id = update.id,
      swarm = self.swarm,
      nodes = serde_json::to_string(&self.nodes).unwrap_or_else(|e| e.to_string()),
      force = self.force,
    )
  )]
  async fn resolve(
    self,
    ExecuteArgs {
      user,
      update,
      task_id,
    }: &ExecuteArgs,
  ) -> Result<Self::Response, Self::Error> {
    let swarm = get_check_permissions::<Swarm>(
      &self.swarm,
      user,
      PermissionLevel::Execute.into(),
    )
    .await?;

    update_update(update.clone()).await?;

    let mut update = update.clone();

    match swarm_request(
      &swarm.config.server_ids,
      periphery_client::api::swarm::RemoveSwarmNodes {
        nodes: self.nodes,
        force: self.force,
      },
    )
    .await
    {
      Ok(log) => {
        update.logs.push(log);
        refresh_swarm_cache(&swarm, true).await;
      }
      Err(e) => update.push_error_log(
        "Remove Swarm Nodes",
        format_serror(
          &e.context("Failed to remove swarm nodes").into(),
        ),
      ),
    };

    update.finalize();
    update_update(update.clone()).await?;

    Ok(update)
  }
}

impl Resolve<ExecuteArgs> for UpdateSwarmNode {
  #[instrument(
    "UpdateSwarmNode",
    skip_all,
    fields(
      task_id = task_id.to_string(),
      operator = user.id,
      update_id = update.id,
      swarm = self.swarm,
      node = self.node,
      availability = format!("{:?}", self.availability),
      label_add = format!("{:?}", self.label_add),
      label_rm = format!("{:?}", self.label_rm),
      role = format!("{:?}", self.role),
    )
  )]
  async fn resolve(
    self,
    ExecuteArgs {
      user,
      update,
      task_id,
    }: &ExecuteArgs,
  ) -> Result<Self::Response, Self::Error> {
    let swarm = get_check_permissions::<Swarm>(
      &self.swarm,
      user,
      PermissionLevel::Execute.into(),
    )
    .await?;

    update_update(update.clone()).await?;

    let mut update = update.clone();

    match swarm_request(
      &swarm.config.server_ids,
      periphery_client::api::swarm::UpdateSwarmNode {
        node: self.node,
        availability: self.availability,
        label_add: self.label_add,
        label_rm: self.label_rm,
        role: self.role,
      },
    )
    .await
    {
      Ok(log) => {
        update.logs.push(log);
        refresh_swarm_cache(&swarm, true).await;
      }
      Err(e) => update.push_error_log(
        "Update Swarm Node",
        format_serror(
          &e.context("Failed to update swarm node").into(),
        ),
      ),
    };

    update.finalize();
    update_update(update.clone()).await?;

    Ok(update)
  }
}

impl Resolve<ExecuteArgs> for RemoveSwarmStacks {
  #[instrument(
    "RemoveSwarmStacks",
    skip_all,
    fields(
      task_id = task_id.to_string(),
      operator = user.id,
      update_id = update.id,
      swarm = self.swarm,
      stacks = serde_json::to_string(&self.stacks).unwrap_or_else(|e| e.to_string()),
      detach = self.detach,
    )
  )]
  async fn resolve(
    self,
    ExecuteArgs {
      user,
      update,
      task_id,
    }: &ExecuteArgs,
  ) -> Result<Self::Response, Self::Error> {
    let swarm = get_check_permissions::<Swarm>(
      &self.swarm,
      user,
      PermissionLevel::Execute.into(),
    )
    .await?;

    update_update(update.clone()).await?;

    let mut update = update.clone();

    match swarm_request(
      &swarm.config.server_ids,
      periphery_client::api::swarm::RemoveSwarmStacks {
        stacks: self.stacks,
        detach: self.detach,
      },
    )
    .await
    {
      Ok(log) => {
        update.logs.push(log);
        refresh_swarm_cache(&swarm, true).await;
      }
      Err(e) => update.push_error_log(
        "Remove Swarm Stacks",
        format_serror(
          &e.context("Failed to remove swarm stacks").into(),
        ),
      ),
    };

    update.finalize();
    update_update(update.clone()).await?;

    Ok(update)
  }
}

impl Resolve<ExecuteArgs> for RemoveSwarmServices {
  #[instrument(
    "RemoveSwarmServices",
    skip_all,
    fields(
      task_id = task_id.to_string(),
      operator = user.id,
      update_id = update.id,
      swarm = self.swarm,
      services = serde_json::to_string(&self.services).unwrap_or_else(|e| e.to_string()),
    )
  )]
  async fn resolve(
    self,
    ExecuteArgs {
      user,
      update,
      task_id,
    }: &ExecuteArgs,
  ) -> Result<Self::Response, Self::Error> {
    let swarm = get_check_permissions::<Swarm>(
      &self.swarm,
      user,
      PermissionLevel::Execute.into(),
    )
    .await?;

    update_update(update.clone()).await?;

    let mut update = update.clone();

    match swarm_request(
      &swarm.config.server_ids,
      periphery_client::api::swarm::RemoveSwarmServices {
        services: self.services,
      },
    )
    .await
    {
      Ok(log) => {
        update.logs.push(log);
        refresh_swarm_cache(&swarm, true).await;
      }
      Err(e) => update.push_error_log(
        "Remove Swarm Services",
        format_serror(
          &e.context("Failed to remove swarm services").into(),
        ),
      ),
    };

    update.finalize();
    update_update(update.clone()).await?;

    Ok(update)
  }
}

impl Resolve<ExecuteArgs> for CreateSwarmConfig {
  #[instrument(
    "CreateSwarmConfig",
    skip_all,
    fields(
      task_id = task_id.to_string(),
      operator = user.id,
      update_id = update.id,
      swarm = self.swarm,
      config = self.name,
    )
  )]
  async fn resolve(
    self,
    ExecuteArgs {
      user,
      update,
      task_id,
    }: &ExecuteArgs,
  ) -> Result<Self::Response, Self::Error> {
    let swarm = get_check_permissions::<Swarm>(
      &self.swarm,
      user,
      PermissionLevel::Execute.into(),
    )
    .await?;

    update_update(update.clone()).await?;

    let mut update = update.clone();

    match swarm_request(
      &swarm.config.server_ids,
      periphery_client::api::swarm::CreateSwarmConfig {
        name: self.name,
        data: self.data,
        labels: self.labels,
        template_driver: self.template_driver,
      },
    )
    .await
    {
      Ok(log) => {
        update.logs.push(log);
        refresh_swarm_cache(&swarm, true).await;
      }
      Err(e) => update.push_error_log(
        "Create Swarm Config",
        format_serror(
          &e.context("Failed to create swarm config").into(),
        ),
      ),
    };

    update.finalize();
    update_update(update.clone()).await?;

    Ok(update)
  }
}

impl Resolve<ExecuteArgs> for RotateSwarmConfig {
  #[instrument(
    "RotateSwarmConfig",
    skip_all,
    fields(
      task_id = task_id.to_string(),
      operator = user.id,
      update_id = update.id,
      swarm = self.swarm,
      config = self.config,
    )
  )]
  async fn resolve(
    self,
    ExecuteArgs {
      user,
      update,
      task_id,
    }: &ExecuteArgs,
  ) -> Result<Self::Response, Self::Error> {
    let swarm = get_check_permissions::<Swarm>(
      &self.swarm,
      user,
      PermissionLevel::Execute.into(),
    )
    .await?;

    update_update(update.clone()).await?;

    let mut update = update.clone();

    match swarm_request(
      &swarm.config.server_ids,
      periphery_client::api::swarm::RotateSwarmConfig {
        config: self.config,
        data: self.data,
      },
    )
    .await
    {
      Ok(logs) => {
        update.logs.extend(logs);
        refresh_swarm_cache(&swarm, true).await;
      }
      Err(e) => update.push_error_log(
        "Rotate Swarm Config",
        format_serror(
          &e.context("Failed to rotate swarm config").into(),
        ),
      ),
    };

    update.finalize();
    update_update(update.clone()).await?;

    Ok(update)
  }
}

impl Resolve<ExecuteArgs> for RemoveSwarmConfigs {
  #[instrument(
    "RemoveSwarmConfigs",
    skip_all,
    fields(
      task_id = task_id.to_string(),
      operator = user.id,
      update_id = update.id,
      swarm = self.swarm,
      configs = serde_json::to_string(&self.configs).unwrap_or_else(|e| e.to_string()),
    )
  )]
  async fn resolve(
    self,
    ExecuteArgs {
      user,
      update,
      task_id,
    }: &ExecuteArgs,
  ) -> Result<Self::Response, Self::Error> {
    let swarm = get_check_permissions::<Swarm>(
      &self.swarm,
      user,
      PermissionLevel::Execute.into(),
    )
    .await?;

    update_update(update.clone()).await?;

    let mut update = update.clone();

    match swarm_request(
      &swarm.config.server_ids,
      periphery_client::api::swarm::RemoveSwarmConfigs {
        configs: self.configs,
      },
    )
    .await
    {
      Ok(log) => {
        update.logs.push(log);
        refresh_swarm_cache(&swarm, true).await;
      }
      Err(e) => update.push_error_log(
        "Remove Swarm Configs",
        format_serror(
          &e.context("Failed to remove swarm configs").into(),
        ),
      ),
    };

    update.finalize();
    update_update(update.clone()).await?;

    Ok(update)
  }
}

impl Resolve<ExecuteArgs> for CreateSwarmSecret {
  #[instrument(
    "CreateSwarmSecret",
    skip_all,
    fields(
      task_id = task_id.to_string(),
      operator = user.id,
      update_id = update.id,
      swarm = self.swarm,
      secret = self.name,
    )
  )]
  async fn resolve(
    self,
    ExecuteArgs {
      user,
      update,
      task_id,
    }: &ExecuteArgs,
  ) -> Result<Self::Response, Self::Error> {
    let swarm = get_check_permissions::<Swarm>(
      &self.swarm,
      user,
      PermissionLevel::Execute.into(),
    )
    .await?;

    update_update(update.clone()).await?;

    let mut update = update.clone();

    match swarm_request(
      &swarm.config.server_ids,
      periphery_client::api::swarm::CreateSwarmSecret {
        name: self.name,
        data: self.data,
        driver: self.driver,
        labels: self.labels,
        template_driver: self.template_driver,
      },
    )
    .await
    {
      Ok(log) => {
        update.logs.push(log);
        refresh_swarm_cache(&swarm, true).await;
      }
      Err(e) => update.push_error_log(
        "Create Swarm Secret",
        format_serror(
          &e.context("Failed to create swarm secret").into(),
        ),
      ),
    };

    update.finalize();
    update_update(update.clone()).await?;

    Ok(update)
  }
}

impl Resolve<ExecuteArgs> for RotateSwarmSecret {
  #[instrument(
    "RotateSwarmSecret",
    skip_all,
    fields(
      task_id = task_id.to_string(),
      operator = user.id,
      update_id = update.id,
      swarm = self.swarm,
      secret = self.secret,
    )
  )]
  async fn resolve(
    self,
    ExecuteArgs {
      user,
      update,
      task_id,
    }: &ExecuteArgs,
  ) -> Result<Self::Response, Self::Error> {
    let swarm = get_check_permissions::<Swarm>(
      &self.swarm,
      user,
      PermissionLevel::Execute.into(),
    )
    .await?;

    update_update(update.clone()).await?;

    let mut update = update.clone();

    match swarm_request(
      &swarm.config.server_ids,
      periphery_client::api::swarm::RotateSwarmSecret {
        secret: self.secret,
        data: self.data,
      },
    )
    .await
    {
      Ok(logs) => {
        update.logs.extend(logs);
        refresh_swarm_cache(&swarm, true).await;
      }
      Err(e) => update.push_error_log(
        "Rotate Swarm Secret",
        format_serror(
          &e.context("Failed to rotate swarm secret").into(),
        ),
      ),
    };

    update.finalize();
    update_update(update.clone()).await?;

    Ok(update)
  }
}

impl Resolve<ExecuteArgs> for RemoveSwarmSecrets {
  #[instrument(
    "RemoveSwarmSecrets",
    skip_all,
    fields(
      task_id = task_id.to_string(),
      operator = user.id,
      update_id = update.id,
      swarm = self.swarm,
      secrets = serde_json::to_string(&self.secrets).unwrap_or_else(|e| e.to_string()),
    )
  )]
  async fn resolve(
    self,
    ExecuteArgs {
      user,
      update,
      task_id,
    }: &ExecuteArgs,
  ) -> Result<Self::Response, Self::Error> {
    let swarm = get_check_permissions::<Swarm>(
      &self.swarm,
      user,
      PermissionLevel::Execute.into(),
    )
    .await?;

    update_update(update.clone()).await?;

    let mut update = update.clone();

    match swarm_request(
      &swarm.config.server_ids,
      periphery_client::api::swarm::RemoveSwarmSecrets {
        secrets: self.secrets,
      },
    )
    .await
    {
      Ok(log) => {
        update.logs.push(log);
        refresh_swarm_cache(&swarm, true).await;
      }
      Err(e) => update.push_error_log(
        "Remove Swarm Secrets",
        format_serror(
          &e.context("Failed to remove swarm secrets").into(),
        ),
      ),
    };

    update.finalize();
    update_update(update.clone()).await?;

    Ok(update)
  }
}
