#compdef dcsctl

# --- helpers ---------------------------------------------------------------

_dcsctl__docker_contexts() {
  local -a ctxs
  ctxs=(${(f)"$(command docker context ls --format '{{.Name}}' 2>/dev/null)"})
  _describe -t docker-contexts 'docker contexts' ctxs
}

_dcsctl__current_context() {
  command docker context show 2>/dev/null
}

_dcsctl__control_plane_base() {
  # Match dcsctl logic: base is $DCS_ROOT if set, else ~/.dcs
  print -r -- "${DCS_ROOT:-$HOME/.dcs}"
}

_dcsctl__projects() {
  local ctx base
  ctx="$(_dcsctl__current_context)"
  [[ -z "$ctx" ]] && return 0

  base="$(_dcsctl__control_plane_base)/$ctx/compose"
  [[ -d "$base" ]] || return 0

  local -a ignored projects
  ignored=(shared '@Recycle' '@Recently-Snapshot' '.DS_Store')

  projects=()
  local dir name
  for dir in "$base"/*; do
    [[ -d "$dir" ]] || continue
    name="${dir:t}"
    (( ${ignored[(I)$name]} )) && continue
    projects+=("$name")
  done

  _describe -t dcs-projects 'projects' projects
}

_dcsctl__secret_names_from_dir() {
  local ctx proj base projdir secretdir
  ctx="$(_dcsctl__current_context)"
  [[ -z "$ctx" ]] && return 1

  proj="${words[4]}"   # dcsctl secret add <project> <name>
  [[ -z "$proj" ]] && return 1

  base="$(_dcsctl__control_plane_base)/$ctx/compose"
  projdir="$base/$proj"
  secretdir="$projdir/secrets"
  [[ -d "$secretdir" ]] || return 1

  local -a names
  names=()
  local f n
  for f in "$secretdir"/*.txt; do
    [[ -f "$f" ]] || continue
    n="${f:t}"
    n="${n%.txt}"
    names+=("$n")
  done

  (( ${#names} == 0 )) && return 1
  _describe -t dcs-secrets 'secret names' names
}

# List service subdirectories in a swarm-mode project.
# Takes one argument: the project name.
_dcsctl__swarm_services() {
  local ctx proj base projdir svcdir
  ctx="$(_dcsctl__current_context)"
  [[ -z "$ctx" ]] && return 1

  proj="$1"
  [[ -z "$proj" ]] && return 1

  base="$(_dcsctl__control_plane_base)/$ctx/compose"
  projdir="$base/$proj"
  svcdir="$projdir/services"
  [[ -d "$svcdir" ]] || return 1

  local -a svcs
  svcs=()
  local dir name
  for dir in "$svcdir"/*/; do
    [[ -d "$dir" ]] || continue
    name="${dir:t}"
    svcs+=("$name")
  done

  (( ${#svcs} == 0 )) && return 1
  _describe -t dcs-swarm-services 'services' svcs
}

# Detect whether a project is in swarm mode (has services/ dir).
_dcsctl__is_swarm() {
  local ctx proj base projdir
  ctx="$(_dcsctl__current_context)"
  [[ -z "$ctx" ]] && return 1

  proj="$1"
  [[ -z "$proj" ]] && return 1

  base="$(_dcsctl__control_plane_base)/$ctx/compose"
  projdir="$base/$proj"
  [[ -d "$projdir/services" ]]
}

# Fetch services for a given project by asking docker compose for the resolved config.
_dcsctl__services() {
  local ctx proj base projdir
  ctx="$(_dcsctl__current_context)"
  [[ -z "$ctx" ]] && return 1

  proj="${words[3]}"   # dcsctl run <project> ...
  [[ -z "$proj" ]] && return 1

  base="$(_dcsctl__control_plane_base)/$ctx/compose"
  projdir="$base/$proj"
  [[ -d "$projdir" ]] || return 1

  local env1 env2 env3
  env1="$projdir/.env"
  env2="$projdir/service.env"
  env3="$projdir/service.secrets.env"

  [[ -f "$env1" && -f "$env2" && -f "$env3" && -f "$projdir/docker-compose.yml" ]] || return 1

  local -a svcs
  svcs=(${(f)"$(
    command docker --context "$ctx" compose \
      --project-directory "$projdir" \
      --project-name "$proj" \
      --env-file "$env1" \
      --env-file "$env2" \
      --env-file "$env3" \
      config --services 2>/dev/null
  )"})

  (( ${#svcs} == 0 )) && return 1
  _describe -t dcs-services 'services' svcs
}

# Heuristic: decide if we are in a position where a service name is expected.
_dcsctl__maybe_complete_service() {
  local subcmd cur
  subcmd="${words[4]}"   # dcsctl run <project> <subcmd> ...
  cur="${words[CURRENT]}"

  [[ "$cur" == -* ]] && return 1

  case "$subcmd" in
    logs|ps|stop|start|restart|up|pull|build|rm|kill|pause|unpause|top|exec)
      _dcsctl__services
      return $?
      ;;
  esac

  return 1
}

# Delegate completion to docker after we reconstruct words as docker compose ...
_dcsctl__docker_compose_delegate() {
  local ctx proj base projdir
  ctx="$(_dcsctl__current_context)"
  [[ -z "$ctx" ]] && return 1

  proj="${words[3]}"
  [[ -z "$proj" ]] && return 1

  base="$(_dcsctl__control_plane_base)/$ctx/compose"
  projdir="$base/$proj"

  local -a dc_words
  dc_words=(docker --context "$ctx" compose --project-directory "$projdir" --project-name "$proj")

  if (( ${#words} >= 4 )); then
    dc_words+=("${words[@]:3}")  # from 4th element
  fi

  local -a save_words
  local save_cword
  save_words=("${words[@]}")
  save_cword=$CURRENT

  words=("${dc_words[@]}")
  CURRENT=${#words}

  autoload -Uz _docker
  _docker

  words=("${save_words[@]}")
  CURRENT=$save_cword
}

# Helper for edit --service completion: extract the project arg from the command
# line and complete swarm service names.
_dcsctl__swarm_services_for_edit() {
  local proj=""
  local i
  for (( i=3; i <= ${#words}; i++ )); do
    local w="${words[$i]}"
    case "$w" in
      -e|--env|-r|--runtime|-s|--secret) ;;
      --service) (( i++ )) ;;  # skip the next arg (the service value)
      -*) ;;
      *)
        if [[ -z "$proj" ]]; then
          proj="$w"
        fi
        ;;
    esac
  done

  [[ -z "$proj" ]] && return 1
  _dcsctl__swarm_services "$proj"
}

# --- main completion -------------------------------------------------------

_dcsctl() {
  local -a subcmds
  subcmds=(
    'new:Create a new project scaffold'
    'verify:Verify and enforce host permissions for appdata bind mounts'
    'run:Run docker compose for a project (compose mode)'
    'up:Alias for run <project> up -d (compose mode)'
    'down:Bring down a project (compose or swarm)'
    'stop:Scale all services to 0 replicas (swarm projects only)'
    'deploy:Merge service fragments and deploy as Docker Swarm stack'
    'ls:Alias for docker compose ls'
    'dir:Print the project directory, or a service directory in a swarm project'
    'edit:Edit project files in $EDITOR'
    'secret:Manage project secrets'
    'service:Manage services within a swarm project'
    'import:Import an existing docker-compose.yml into a DCS project'
    'context:Manage DCS control-plane contexts'
    'reload:Down, rebuild and up all containers within a DCS project'
    'rename:Rename and update a DCS project'
    'help:Show help for a command'
  )

  if (( CURRENT == 2 )); then
    _describe -t commands 'dcsctl commands' subcmds
    return
  fi

  local cmd
  cmd="${words[2]}"

  case "$cmd" in
    new)
      _arguments -C \
        '(-v --verify)'{-v,--verify}'[Run verification after creating the project]' \
        '--verbose[Verbose output for verification (used with --verify)]' \
        '--compose[Create flat single-compose layout (legacy)]' \
        '--service[Initial service name for swarm layout]:service name:' \
        '1:project name:' \
        && return
      ;;

    verify)
      _arguments -C \
        '--verbose[Show current vs expected ownership/mode while verifying (QNAP only)]' \
        '1:project name:_dcsctl__projects' \
        && return
      ;;

    run)
      if (( CURRENT == 3 )); then
        _dcsctl__projects
        return
      fi

      if (( CURRENT >= 5 )); then
        _dcsctl__maybe_complete_service && return
      fi

      _dcsctl__docker_compose_delegate
      return
      ;;

    up)
      _arguments -C \
        '(-v --verify)'{-v,--verify}'[Run `dcsctl verify` before running docker compose]' \
        '1:project name:_dcsctl__projects' \
        && return
      ;;

    down)
      _arguments -C \
        '1:project name:_dcsctl__projects' \
        && return
      ;;

    stop)
      _arguments -C \
        '1:project name:_dcsctl__projects' \
        && return
      ;;

    deploy)
      _arguments -C \
        '(-v --verify)'{-v,--verify}'[Run verification before deploying]' \
        '--verbose[Verbose verification output]' \
        '1:project name:_dcsctl__projects' \
        && return
      ;;

    ls)
      return
      ;;

    dir)
      if (( CURRENT == 3 )); then
        _dcsctl__projects
        return
      fi
      if (( CURRENT == 4 )); then
        local proj="${words[3]}"
        _dcsctl__swarm_services "$proj" && return
        return 0
      fi
      ;;

    edit)
      _arguments -C \
        '(-e --env -r --runtime -s --secret)'{-e,--env}'[Edit .env]' \
        '(-e --env -r --runtime -s --secret)'{-r,--runtime}'[Edit service.env]' \
        '(-e --env -r --runtime -s --secret)'{-s,--secret}'[Edit service.secrets.env]' \
        '--service[Target a specific service (swarm mode)]:service name:_dcsctl__swarm_services_for_edit' \
        '1:project name:_dcsctl__projects' \
        && return
      ;;

    secret)
      if (( CURRENT == 3 )); then
        _describe -t secret-subcmds 'secret commands' \
          'add:Create/open secrets/<name>.txt in $EDITOR'
        return
      fi

      local sub
      sub="${words[3]}"
      case "$sub" in
        add)
          if (( CURRENT == 4 )); then
            _dcsctl__projects
            return
          fi
          if (( CURRENT == 5 )); then
            _dcsctl__secret_names_from_dir && return
            return 0
          fi
          ;;
      esac
      ;;

    service)
      if (( CURRENT == 3 )); then
        local -a service_subcmds
        service_subcmds=(
          'add:Add a new service to a swarm project'
          'ls:List services in a swarm project'
        )
        _describe -t service-subcmds 'service commands' service_subcmds
        return
      fi

      local sub
      sub="${words[3]}"
      case "$sub" in
        add)
          if (( CURRENT == 4 )); then
            _dcsctl__projects
            return
          fi
          # CURRENT == 5: free-form service name, no completions needed
          ;;
        ls)
          if (( CURRENT == 4 )); then
            _dcsctl__projects
            return
          fi
          ;;
      esac
      ;;

    import)
      _arguments -C \
        '--env-file[Additional env file to ingest]:env file:_files' \
        '--strict[Fail if compose violates template contract after import]' \
        '--swarm[Import as swarm project, splitting services into per-service fragments]' \
        '1:project name:_dcsctl__projects' \
        '2:compose path:_files' \
        && return
      ;;

    context)
      if (( CURRENT == 3 )); then
        _describe -t context-subcmds 'context commands' \
          'init:Initialize ~/.dcs/<context> with env.system and compose/'
        return
      fi

      local sub
      sub="${words[3]}"
      case "$sub" in
        init)
          if (( CURRENT == 4 )); then
            _dcsctl__docker_contexts
            return
          fi
          ;;
      esac
      ;;

    reload)
      _arguments -C \
        '1:project name:_dcsctl__projects' \
        && return
      ;;

    rename)
      _arguments -C \
        '1:project name:_dcsctl__projects' \
        '2:new project name:' \
        && return
      ;;

    help)
      if (( CURRENT == 3 )); then
        _describe -t commands 'dcsctl commands' subcmds
        return
      fi
      ;;
  esac

  _describe -t commands 'dcsctl commands' subcmds
}

_dcsctl "$@"
