text: #! /usr/bin/env python
  # -*- coding: utf-8 -*-
  # vim:fenc=utf-8
  
  """
  Functions for deleting elements.
  """
  
  from __future__ import annotations
  from meerschaum.utils.typing import Any, SuccessTuple, Union, Optional, List
  
  def delete(
          action : Optional[List[str]] = None,
          **kw : Any
      ) -> SuccessTuple:
      """
      Delete an element.
      """
      from meerschaum.utils.misc import choose_subaction
      from meerschaum.utils.debug import dprint
      options = {
          'config'     : _delete_config, 
          'pipes'      : _delete_pipes,
          'plugins'    : _delete_plugins,
          'users'      : _delete_users,
          'connectors' : _delete_connectors,
          'jobs'       : _delete_jobs,
      }
      return choose_subaction(action, options, **kw)
  
  def _complete_delete(
          action : Optional[List[str]] = None,
          **kw : Any
      ) -> List[str]:
      """
      Override the default Meerschaum `complete_` function.
      """
      from meerschaum.actions.start import _complete_start_jobs
      from meerschaum.actions.edit import _complete_edit_config
      if action is None:
          action = []
      options = {
          'connector': _complete_delete_connectors,
          'connectors': _complete_delete_connectors,
          'config' : _complete_edit_config,
          'job' : _complete_start_jobs,
          'jobs' : _complete_start_jobs,
      }
  
      if (
          len(action) > 0 and action[0] in options
              and kw.get('line', '').split(' ')[-1] != action[0]
      ):
          sub = action[0]
          del action[0]
          return options[sub](action=action, **kw)
  
      from meerschaum.actions.shell import default_action_completer
      return default_action_completer(action=(['delete'] + action), **kw)
  
  def _delete_pipes(
          debug : bool = False,
          yes : bool = False,
          force : bool = False,
          noask : bool = False,
          **kw : Any
      ) -> SuccessTuple:
      """
      Drop pipes and delete their registrations.
      """
      from meerschaum import get_pipes
      from meerschaum.utils.prompt import yes_no
      from meerschaum.utils.formatting import pprint
      from meerschaum.utils.warnings import warn
      pipes = get_pipes(as_list=True, debug=debug, **kw)
      if len(pipes) == 0:
          return False, "No pipes to delete."
      question = "Are you sure you want to delete these pipes? This can't be undone!\n"
      for p in pipes:
          question += f" - {p}" + "\n"
      answer = force
      if force:
          answer = True
      else:
          answer = yes_no(question, default='n', noask=noask, yes=yes)
      if not answer:
          return False, "No pipes deleted."
  
      successes, fails = 0, 0
      success_dict = {}
  
      for p in pipes:
          success_tuple = p.delete(debug=debug)
          success_dict[p] = success_tuple[1]
          if success_tuple[0]:
              successes += 1
          else:
              fails += 1
              warn(success_tuple[1], stack=False)
  
      msg = (
          f"Finished deleting {len(pipes)} pipes.\n" +
          f"    ({successes} succeeded, {fails} failed)"
      )
      
      return successes > 0, msg
  
  def _delete_config(
          action : Optional[List[str]] = None,
          yes : bool = False,
          force : bool = False,
          noask : bool = False,
          debug : bool = False,
          **kw : Any
      ) -> SuccessTuple:
      """
      Delete configuration files.
      """
      import os, shutil
      from meerschaum.utils.prompt import yes_no
      from meerschaum.config._paths import CONFIG_DIR_PATH, STACK_COMPOSE_PATH, DEFAULT_CONFIG_DIR_PATH
      from meerschaum.config._read_config import get_possible_keys, get_keyfile_path
      from meerschaum.utils.debug import dprint
      paths = [p for p in [STACK_COMPOSE_PATH, DEFAULT_CONFIG_DIR_PATH] if p.exists()]
      if action is None:
          action = []
      keys = get_possible_keys() if len(action) == 0 else action
      for k in keys:
          _p = get_keyfile_path(k, create_new=False)
          if _p is not None:
              paths.append(_p)
  
      if force or not paths:
          answer = True
      else:
          sep = '\n' + '  - '
          answer = yes_no(
              f"Are you sure you want to delete the following configuration files?" +
              f"{sep + sep.join([str(p) for p in paths])}\n",
              default='n', noask=noask, yes=yes
          )
  
      if answer or force:
          for path in paths:
              if debug:
                  dprint(f"Removing {path}...")
              if os.path.isfile(path):
                  os.remove(path)
              elif os.path.isdir(path):
                  shutil.rmtree(path)
          success, msg = True, "Success"
      else:
          success, msg = False, "Nothing deleted."
      
      return success, msg
  
  def _delete_plugins(
          action : Optional[List[str]] = None,
          repository : Optional[str] = None,
          yes : bool = False,
          force : bool = False,
          noask : bool = False,
          debug : bool = False,
          **kw : Any
      ) -> SuccessTuple:
      """
      Delete plugins from a Meerschaum repository.
      """
      from meerschaum.utils.warnings import info
      from meerschaum.plugins import reload_plugins
      from meerschaum.connectors.parse import parse_repo_keys
      from meerschaum.utils.prompt import yes_no
      from meerschaum.utils.formatting import print_tuple
      repo_connector = parse_repo_keys(repository)
  
      sep = '\n' + '  - '
      answer = yes_no(
          "Are you sure you want to delete the following plugins " +
          f"from the repository '{repo_connector}'?\n" +
          "THIS CANNOT BE UNDONE!\n" +
          f"{sep + sep.join([str(p) for p in action])}\n",
          default='n', noask=noask, yes=yes
      ) if not force else True
  
      if not answer:
          return False, f"No plugins deleted."
  
      successes = dict()
      for name in action:
          info(f"Deleting plugin '{name}' from Meerschaum repository '{repo_connector}'...")
          success, msg = repo_connector.delete_plugin(name, debug=debug)
          successes[name] = (success, msg)
          print_tuple((success, msg))
  
      reload_plugins(debug=debug)
      return True, "Success"
  
  def _delete_users(
          action : Optional[List[str]] = None,
          mrsm_instance : Optional[str] = None,
          yes : bool = False,
          force : bool = False,
          noask : bool = False,
          shell : bool = False,
          debug : bool = False,
          **kw
      ) -> SuccessTuple:
      """
      Delete users from a Meerschaum instance. Adequate permissions are required.
      """
      from meerschaum import get_connector
      from meerschaum.connectors.parse import parse_instance_keys
      from meerschaum.utils.prompt import yes_no, prompt
      from meerschaum.utils.debug import dprint
      from meerschaum.utils.warnings import warn, error, info
      from meerschaum._internal.User import User
      from meerschaum.connectors.api import APIConnector
      from meerschaum.utils.formatting import print_tuple
      instance_connector = parse_instance_keys(mrsm_instance)
  
      if action is None:
          action = []
  
      registered_users = []
      for username in action:
          user = User(username=username)
          user_id = instance_connector.get_user_id(user, debug=debug)
          if user_id is None:
              info(f"User '{user}' does not exist. Skipping...")
              continue
          user.user_id = user_id
          registered_users.append(user)
  
      if len(registered_users) == 0:
          return False, "No users to delete."
  
      ### verify that the user absolutely wants to do this (skips on --force)
      question = (
          "Are you sure you want to delete these users from Meerschaum instance "
          + f"'{instance_connector}'?\n"
      )
      for username in registered_users:
          question += f" - {username}" + "\n"
      if force:
          answer = True
      else:
          answer = yes_no(question, default='n', noask=noask, yes=yes)
      if not answer:
          return False, "No users deleted."
  
      success = dict()
      for user in registered_users:
          info(f"Deleting user '{user}' from Meerschaum instance '{instance_connector}'...")
          result_tuple = instance_connector.delete_user(user, debug=debug)
          print_tuple(result_tuple)
          success[username] = result_tuple[0]
  
      succeeded, failed = 0, 0
      for username, r in success.items():
          if r:
              succeeded += 1
          else:
              failed += 1
  
      msg = (
          f"Finished deleting {len(action)} users." + '\n' +
          f"    ({succeeded} succeeded, {failed} failed)"
      )
      return True, msg
  
  def _delete_connectors(
          action : Optional[List[str]] = None,
          connector_keys : Optional[List[str]] = None,
          yes : bool = False,
          force : bool = False,
          noask : bool = False,
          debug : bool = False,
          **kw : Any
      ) -> SuccessTuple:
      """
      Delete configured connectors.
  
      Example:
          `delete connectors sql:test`
      """
      import os, pathlib
      from meerschaum.utils.prompt import yes_no, prompt
      from meerschaum.connectors.parse import parse_connector_keys
      from meerschaum.config import _config
      from meerschaum.config._edit import write_config
      from meerschaum.utils.warnings import info, warn
      cf = _config()
      if action is None:
          action = []
      if connector_keys is None:
          connector_keys = []
  
      _keys = list(set(action + connector_keys))
  
      if not _keys:
          return False, "No connectors to delete."
  
      to_delete = []
      for ck in _keys:
          try:
              conn = parse_connector_keys(ck, debug=debug)
          except Exception as e:
              warn(f"Could not parse connector '{ck}'. Skipping...", stack=False)
              continue
  
          if not force:
              if not yes_no(
                  f"Are you sure you want to delete connector '{conn}' from the configuration file?",
                  default = 'n',
                  yes = yes,
                  noask = noask,
              ):
                  info(f"Skipping connector '{conn}'...")
                  continue
          to_delete.append(conn)
  
      if len(to_delete) == 0:
          return False, "No changes made to the configuration file."
      for c in to_delete:
          try:
              ### Remove database files.
              if c.flavor in ('sqlite', 'duckdb'):
                  if ':memory:' not in c.database and pathlib.Path(c.database).exists():
                      if force or yes_no(
                          f"Detected '{c.flavor}' database '{c.database}'. "
                          + "Delete this file?", default='n', noask=noask, yes=yes
                      ):
                          try:
                              os.remove(c.database)
                          except Exception as e:
                              warn(
                                  "Failed to delete database file for connector "
                                  + f"'{c}'. Ignoring...", stack=False
                              )
          except Exception as e:
              pass
          try:
              del cf['meerschaum']['connectors'][c.type][c.label]
          except Exception as e:
              warn(f"Failed to delete connector '{c}' from configuration. Skipping...", stack=False)
  
      write_config(cf, debug=debug)
      return True, "Success"
  
  def _complete_delete_connectors(
          action : Optional[List[str]] = None,
          line : str = '',
          **kw : Any
      ) -> List[str]:
      from meerschaum.config import get_config
      from meerschaum.utils.misc import get_connector_labels
      types = list(get_config('meerschaum', 'connectors').keys())
      if line.split(' ')[-1] == '' or not action:
          search_term = ''
      else:
          search_term = action[-1]
  
      #  search_term = action[-1] if action or line.split(' ')[-1] == '' else ''
      return get_connector_labels(*types, search_term=search_term)
  
  def _delete_jobs(
          action : Optional[List[str]] = None,
          noask : bool = False,
          nopretty : bool = False,
          force : bool = False,
          yes : bool = False,
          debug : bool = False,
          **kw
      ) -> SuccessTuple:
      """
      Remove a job's log files and delete the job's ID.
  
      If the job is running, ask to kill the job first.
      """
      from meerschaum.utils.daemon import (
          Daemon, get_running_daemons, get_stopped_daemons, get_filtered_daemons,
      )
      from meerschaum.utils.prompt import yes_no
      from meerschaum.utils.formatting._jobs import pprint_jobs
      from meerschaum.utils.formatting._shell import clear_screen
      from meerschaum.utils.warnings import warn
      from meerschaum.utils.misc import items_str
      from meerschaum.actions import actions
      daemons = get_filtered_daemons(action, warn=(not nopretty))
      if not daemons:
          return False, "No jobs to delete."
      _delete_all_jobs = False
      if not action:
          if not force:
              pprint_jobs(daemons)
              if not yes_no(
                  "Delete all jobs? This cannot be undone!",
                  noask=noask, yes=yes, default='n'
              ):
                  return False, "No jobs were deleted."
              _delete_all_jobs = True
      _running_daemons = get_running_daemons(daemons)
      _stopped_daemons = get_stopped_daemons(daemons, _running_daemons)
      _to_delete = _stopped_daemons
      if _running_daemons:
          clear_screen(debug=debug)
          if not force:
              pprint_jobs(_running_daemons, nopretty=nopretty)
          if force or yes_no(
              "The above jobs are still running. Stop these jobs?",
              default='n', yes=yes, noask=noask
          ):
              actions['stop'](
                  action = (['jobs'] + [d.daemon_id for d in _running_daemons]),
                  nopretty = nopretty,
                  yes = yes,
                  force = force,
                  noask = noask,
                  debug = debug,
              )
              #  import time
              #  time.sleep(1)
              ### Ensure the running jobs are dead.
              if get_running_daemons(daemons):
                  return False, (
                      f"Failed to kill running jobs. Please stop these jobs before deleting."
                  )
              _to_delete += _running_daemons
          ### User decided not to kill running jobs.
          else:
              pass
  
      if not _to_delete:
          return False, "No jobs to delete."
  
      if not force and not _delete_all_jobs:
          pprint_jobs(_to_delete, nopretty=nopretty)
          if not yes_no(
              "Are you sure you want to delete these jobs?",
              yes=yes, noask=noask, default='y',
          ):
              return False, "No jobs were deleted."
  
      _deleted = []
      for d in _to_delete:
          d.cleanup()
          if d.path.exists() and not nopretty:
              warn("Failed to delete job '{d.daemon_id}'.", stack=False)
              continue
          _deleted.append(d)
  
      return (
          len(_deleted) > 0,
          ("Deleted job" + ("s" if len(_deleted) != 1 else '')
              + f" {items_str([d.daemon_id for d in _deleted])}."),
      )
  
  
  
  ### NOTE: This must be the final statement of the module.
  ###       Any subactions added below these lines will not
  ###       be added to the `help` docstring.
  from meerschaum.utils.misc import choices_docstring as _choices_docstring
  delete.__doc__ += _choices_docstring('delete')
  