#!/usr/bin/env bash

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.

# Allowed values for the commands & flags used
# The values here should be synchronized with the ones in
# the ./scripts/ci/libraries/_initialization.sh and it is verified
# by the BATS tests automatically during pre-commit and CI
# Those cannot be made read-only as the breeze-complete must be re-sourceable

_breeze_allowed_python_major_minor_versions="3.6 3.7 3.8 3.9"
_breeze_allowed_backends="sqlite mysql postgres mssql"
_breeze_allowed_integrations="cassandra kerberos mongo openldap pinot rabbitmq redis statsd trino all"
_breeze_allowed_generate_constraints_modes="source-providers pypi-providers no-providers"
_breeze_allowed_kubernetes_modes="image"
_breeze_allowed_kubernetes_versions="v1.20.2 v1.19.7 v1.18.15"
_breeze_allowed_helm_versions="v3.6.3"
_breeze_allowed_kind_versions="v0.11.1"
_breeze_allowed_mysql_versions="5.7 8"
_breeze_allowed_mssql_versions="2017-latest 2019-latest"
_breeze_allowed_postgres_versions="9.6 10 11 12 13"
_breeze_allowed_kind_operations="start stop restart status deploy test shell k9s"
_breeze_allowed_executors="KubernetesExecutor CeleryExecutor LocalExecutor CeleryKubernetesExecutor"
_breeze_allowed_test_types="All Always Core Providers API CLI Integration Other WWW Postgres MySQL Helm Quarantined"
_breeze_allowed_package_formats="both sdist wheel"
_breeze_allowed_installation_methods=". apache-airflow"

# shellcheck disable=SC2034
{
    # Default values for the commands & flags used
    _breeze_default_backend=$(echo "${_breeze_allowed_backends}" | awk '{print $1}')
    _breeze_default_generate_providers_mode=$(echo "${_breeze_allowed_generate_constraints_modes}" | awk '{print $1}')
    _breeze_default_kubernetes_mode=$(echo "${_breeze_allowed_kubernetes_modes}" | awk '{print $1}')
    _breeze_default_kubernetes_version=$(echo "${_breeze_allowed_kubernetes_versions}" | awk '{print $1}')
    _breeze_default_helm_version=$(echo "${_breeze_allowed_helm_versions}" | awk '{print $1}')
    _breeze_default_kind_version=$(echo "${_breeze_allowed_kind_versions}" | awk '{print $1}')
    _breeze_default_executor=$(echo "${_breeze_allowed_executors}" | awk '{print $1}')
    _breeze_default_postgres_version=$(echo "${_breeze_allowed_postgres_versions}" | awk '{print $1}')
    _breeze_default_mysql_version=$(echo "${_breeze_allowed_mysql_versions}" | awk '{print $1}')
    _breeze_default_mssql_version=$(echo "${_breeze_allowed_mssql_versions}" | awk '{print $1}')
    _breeze_default_test_type=$(echo "${_breeze_allowed_test_types}" | awk '{print $1}')
    _breeze_default_package_format=$(echo "${_breeze_allowed_package_formats}" | awk '{print $1}')
}

_breeze_allowed_install_airflow_versions=$(cat <<-EOF
2.0.2
2.0.1
2.0.0
wheel
sdist
EOF
)

_breeze_allowed_USE_AIRFLOW_VERSION=$(cat <<-EOF
${_breeze_allowed_install_airflow_versions}
none
EOF
)

_breeze_allowed_static_checks=$(cat <<-EOF
all
airflow-config-yaml
airflow-providers-available
airflow-provider-yaml-files-ok
base-operator
bats-tests
bats-in-container-tests
black
blacken-docs
boring-cyborg
build
build-providers-dependencies
check-apache-license
check-builtin-literals
check-executables-have-shebangs
check-extras-order
check-hooks-apply
check-integrations
check-merge-conflict
check-xml
daysago-import-check
debug-statements
detect-private-key
doctoc
dont-use-safe-filter
end-of-file-fixer
fix-encoding-pragma
flake8
flynt
forbid-tabs
helm-lint
identity
incorrect-use-of-LoggingMixin
insert-license
isort
json-schema
language-matters
lint-dockerfile
lint-openapi
markdownlint
mermaid
mixed-line-ending
mypy
mypy-helm
no-providers-in-core-examples
no-relative-imports
pre-commit-descriptions
pre-commit-hook-names
pretty-format-json
provide-create-sessions
providers-changelogs
providers-init-file
providers-subpackages-init-file
provider-yamls
pydevd
pydocstyle
python-no-log-warn
pyupgrade
restrict-start_date
rst-backticks
setup-order
setup-extra-packages
shellcheck
sort-in-the-wild
sort-spelling-wordlist
stylelint
trailing-whitespace
ui-lint
update-breeze-file
update-extras
update-local-yml-file
update-setup-cfg-file
update-versions
verify-db-migrations-documented
version-sync
www-lint
yamllint
yesqa
EOF
)

_breeze_default_github_repository="apache/airflow"
_breeze_default_github_image_id="latest"

_breeze_short_options="
h p: b: i:
K: V:
l a: t: d:
v y n q f
F P I E: C r
L U X
g: s:
S: N:
"

_breeze_long_options="
help python: backend: integration:
kubernetes-mode: kubernetes-version: helm-version: kind-version:
skip-mounting-local-sources mount-all-local-sources install-airflow-version: install-airflow-reference: db-reset
verbose assume-yes assume-no assume-quit forward-credentials init-script:
force-build-images force-pull-images check-if-base-python-image-updated production-image extras: force-clean-images skip-rebuild-check
build-cache-local build-cache-pulled build-cache-disabled disable-pip-cache
github-repository: github-image-id: generate-constraints-mode:
postgres-version: mysql-version: mssql-version:
version-suffix-for-pypi: version-suffix-for-svn:
additional-extras: additional-python-deps: additional-dev-deps: additional-runtime-deps: image-tag:
disable-mysql-client-installation constraints-location: disable-pip-cache install-from-docker-context-files
additional-extras: additional-python-deps: disable-pypi-when-building skip-installing-airflow-providers-from-sources
dev-apt-deps: additional-dev-apt-deps: dev-apt-command: additional-dev-apt-command: additional-dev-apt-env:
runtime-apt-deps: additional-runtime-apt-deps: runtime-apt-command: additional-runtime-apt-command: additional-runtime-apt-env:
load-default-connections load-example-dags
use-packages-from-dist no-rbac-ui package-format: upgrade-to-newer-dependencies installation-method: continue-on-pip-check-failure non-interactive generate-providers-issue
use-airflow-version:
cleanup-docker-context-files
test-type: preserve-volumes dry-run-docker
executor:
"

_breeze_commands="
shell
build-docs
build-image
cleanup-image
exec
generate-constraints
push-image
initialize-local-virtualenv
prepare-airflow-packages
setup-autocomplete
start-airflow
stop
restart
toggle-suppress-cheatsheet
toggle-suppress-asciiart"

_breeze_extra_arg_commands="
docker-compose
kind-cluster
prepare-provider-documentation
prepare-provider-packages
static-check
tests
"

_breeze_help_commands="
flags
help
help-all"

_breeze_all_commands="${_breeze_commands} ${_breeze_extra_arg_commands} ${_breeze_help_commands}"

# Note on OSX bash has no associative arrays (Bash 3.2) so we have to fake it

_breeze_known_values=""

#######################################################################################################
#
# Dynamically set list of values in _breeze_known_values variable depending on the command used
#
# Used variables:
#   _breeze_allowed_*  for allowed values for the commands
#
# Arguments:
#   $1 - command to get known values for
#
# Set variables:
#   _breeze_known_values is set to list of known values for the command, separated with space
#
#######################################################################################################

function breeze_complete::get_known_values_breeze() {
    case "$1" in
    -p | --python)
        _breeze_known_values=${_breeze_allowed_python_major_minor_versions}
        ;;
    -b | --backend)
        _breeze_known_values=${_breeze_allowed_backends}
        ;;
    -i | --integration)
        _breeze_known_values=${_breeze_allowed_integrations}
        ;;
    -K | --kubernetes-mode)
        _breeze_known_values=${_breeze_allowed_kubernetes_modes}
        ;;
    -V | --kubernetes-version)
        _breeze_known_values=${_breeze_allowed_kubernetes_versions}
        ;;
    --kind-version)
        _breeze_known_values=${_breeze_allowed_kind_versions}
        ;;
    --helm-version)
        _breeze_known_values=${_breeze_allowed_helm_versions}
        ;;
    static-check)
        _breeze_known_values=${_breeze_allowed_static_checks}
        ;;
    -a | --install-airflow-version)
        _breeze_known_values=${_breeze_allowed_install_airflow_versions}
        ;;
    --use-airflow-version)
        _breeze_known_values=${_breeze_allowed_USE_AIRFLOW_VERSION}
        ;;
    docker-compose)
        # shellcheck disable=SC2034
        if typeset -f "_docker_compose" >/dev/null; then
            _docker_compose
        fi
        _breeze_known_values=""
        ;;
    --postgres-version)
        _breeze_known_values=${_breeze_allowed_postgres_versions}
        ;;
    --mysql-version)
        _breeze_known_values=${_breeze_allowed_mysql_versions}
        ;;
    --mssql-version)
        _breeze_known_values=${_breeze_allowed_mssql_versions}
        ;;
    -g | --github-repository)
        _breeze_known_values="${_breeze_default_github_repository}"
        ;;
    -s | --github-image-id)
        _breeze_known_values="${_breeze_default_github_image_id}"
        ;;
    kind-cluster)
        _breeze_known_values="${_breeze_allowed_kind_operations}"
        ;;
    tests)
        _breeze_known_values="$(find tests -name '*.py')"
        ;;
    --test-type)
        _breeze_known_values="${_breeze_allowed_test_types}"
        ;;
    --executor)
      _breeze_known_values="${_breeze_allowed_executors}"
      ;;
    --package-format)
        _breeze_known_values="${_breeze_allowed_package_formats}"
        ;;
    --installation-method)
        _breeze_known_values="${_breeze_allowed_installation_methods}"
        ;;
    --generate-constraints-mode)
        _breeze_known_values="${_breeze_allowed_generate_constraints_modes}"
        ;;
    *)
        _breeze_known_values=""
        ;;
    esac
}

_breeze_getopt_short_options=""
_breeze_getopt_long_options=""

#######################################################################################################
#
# Retrieves list of short and long options from the constants in the form of EOL separated values
# which is easy to maintain into comma separated list - usable for getopt.
#
# Used variables:
#   _breeze_short_options
#   _breeze_long_options
#
# Set variables:
#   _breeze_getopt_short_options
#   _breeze_getopt_long_options
#######################################################################################################
function breeze_complete::_build_options_breeze {
    local separator=""
    local option

    for option in ${_breeze_short_options}
    do
        _breeze_getopt_short_options="${_breeze_getopt_short_options}${separator}${option}"
        separator=","
    done

    separator=""
    for option in ${_breeze_long_options}
    do
        _breeze_getopt_long_options="${_breeze_getopt_long_options}${separator}${option}"
        separator=","
    done
}

#######################################################################################################
#
# Returns 0 if word to check is in the list of words
# which is easy to maintain into comma separated list - usable for getopt.
#
# Arguments:
#   $1 - space separated list of words
#   $2 - word to look for
#
# Return:
#   0 - if the word is found in the list
#   1 - otherwise
#######################################################################################################
function breeze_complete::_listcontains_breeze {
    local word
    for word in $1; do
        [[ ${word} = "$2" ]] && return 0
    done
    return 1
}


#######################################################################################################
#
# Convert options from the lists passed and add the options to appropriate variables.
# The options are stripped of the trailing colon and if they do have a colon, they are added
# to the options that require an argument. Options are prefixed with the prefix.
#
#   Arguments:
#      $1 - prefix for the option
#      $2 - list of options
#
#   Variables modified:
#       all_options - space-separated all options
#       options_with extra_args - space separate list of options with an extra argument
#######################################################################################################
function breeze_complete::_convert_options {
    local option_with_prefix_without_colon
    local option
    local prefix=$1
    for option in $2
    do
        last_character="${option:$((${#option} - 1)):1}"
        option_with_prefix_without_colon=${prefix}${option//:/}
        if [[ "${last_character}" == ":" ]]; then
            options_with_extra_arguments="${options_with_extra_arguments} ${option_with_prefix_without_colon}"
        fi
        all_options="${all_options} ${option_with_prefix_without_colon}"
    done
}


#######################################################################################################
#
# A completion function for breeze
#
# See: https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html#Programmable-Completion
#
#######################################################################################################
function breeze_complete::_comp_breeze {
    local all_options=""
    local options_with_extra_arguments=""
    local currently_typed_string
    local previous_command_in_the_command_line
    local last_character

    breeze_complete::_convert_options "-" "${_breeze_short_options}"
    breeze_complete::_convert_options "--" "${_breeze_long_options}"

    # Add all Breeze commands to list of all options
    all_options="${all_options} ${_breeze_all_commands}"

    # Add all Breeze commands with extra args to list of options with extra args
    options_with_extra_arguments="${options_with_extra_arguments} ${_breeze_extra_arg_commands}"

    # Retrieve typed beginning of current commands used so that we can use it as filter in the compgen utility
    currently_typed_string="${COMP_WORDS[${COMP_CWORD}]}"

    # if we have already typed more words, then we check what was the last previous command so that
    # we can retrieve list of known values matching it.
    if [[ ${#COMP_WORDS[@]} -gt 1 ]]; then
        previous_command_in_the_command_line="${COMP_WORDS[${COMP_CWORD} - 1]}"
    else
        previous_command_in_the_command_line=""
    fi

    # if the previous command is in the list of options with extra args - check what are the known values
    if breeze_complete::_listcontains_breeze \
            "${options_with_extra_arguments}" "${previous_command_in_the_command_line}"; then
        # Set COMPREPLY containing list of valid values matching currently typed string from known values
        COMPREPLY=()
        breeze_complete::get_known_values_breeze "${previous_command_in_the_command_line}"
        while IFS='' read -r line; do COMPREPLY+=("${line}"); done \
            < <(compgen -W "${_breeze_known_values}" -- "${currently_typed_string}")
    else
        # Set COMPREPLY containing list of valid commands/options matching currently typed string
        COMPREPLY=()
        while IFS='' read -r line; do COMPREPLY+=("${line}"); done \
            < <(compgen -W "${all_options}" -- "${currently_typed_string}")
    fi
}

breeze_complete::_build_options_breeze

# set autocompletion function for breeze
complete -F breeze_complete::_comp_breeze breeze
complete -F breeze_complete::_comp_breeze ./breeze
