#!/usr/bin/env bash

agentteam_tidb_fail() {
  echo "$*" >&2
  exit 1
}

agentteam_tidb_require_command() {
  local command_name="$1"
  if ! command -v "$command_name" >/dev/null 2>&1; then
    agentteam_tidb_fail "required command not found: $command_name"
  fi
}

agentteam_tidb_url_decode() {
  local value="$1"
  printf '%b' "${value//%/\\x}"
}

agentteam_tidb_query_param() {
  local query_string="$1"
  local target_key="$2"
  local pair=""

  IFS='&' read -r -a query_pairs <<<"$query_string"
  for pair in "${query_pairs[@]}"; do
    if [[ -z "$pair" ]]; then
      continue
    fi
    if [[ "$pair" == "$target_key="* ]]; then
      agentteam_tidb_url_decode "${pair#*=}"
      return 0
    fi
    if [[ "$pair" == "$target_key" ]]; then
      printf ''
      return 0
    fi
  done

  return 1
}

agentteam_tidb_parse_addr() {
  local addr="$1"

  agentteam_tidb_host=""
  agentteam_tidb_port=""

  if [[ "$addr" =~ ^(\[[^]]+\])(:(.+))?$ ]]; then
    agentteam_tidb_host="${BASH_REMATCH[1]}"
    agentteam_tidb_port="${BASH_REMATCH[3]:-4000}"
    return 0
  fi
  if [[ "$addr" == *:* ]]; then
    agentteam_tidb_host="${addr%:*}"
    agentteam_tidb_port="${addr##*:}"
    return 0
  fi

  agentteam_tidb_host="$addr"
  agentteam_tidb_port="4000"
}

agentteam_tidb_parse_core_storage_dsn() {
  local dsn="$1"
  local userinfo_without_db=""
  local addr_and_db=""
  local userinfo=""
  local addr=""
  local database_and_query=""
  local database=""
  local query_string=""

  dsn="${dsn#"${dsn%%[![:space:]]*}"}"
  dsn="${dsn%"${dsn##*[![:space:]]}"}"
  if [[ -z "$dsn" ]]; then
    agentteam_tidb_fail "AGENTTEAM_DATABASE_DSN is required"
  fi

  if [[ "$dsn" != *@tcp\(*\)/* ]]; then
    agentteam_tidb_fail "AGENTTEAM_DATABASE_DSN must use mysql tcp(host:port) form"
  fi

  userinfo_without_db="${dsn%%@tcp(*}"
  addr_and_db="${dsn#*@tcp(}"
  addr="${addr_and_db%%)/*}"
  database_and_query="${addr_and_db#*)/}"

  if [[ "$database_and_query" == *\?* ]]; then
    database="${database_and_query%%\?*}"
    query_string="${database_and_query#*\?}"
  else
    database="$database_and_query"
    query_string=""
  fi

  userinfo="$userinfo_without_db"
  if [[ "$userinfo" == *:* ]]; then
    agentteam_tidb_user_encoded="${userinfo%%:*}"
    agentteam_tidb_password_encoded="${userinfo#*:}"
  else
    agentteam_tidb_user_encoded="$userinfo"
    agentteam_tidb_password_encoded=""
  fi

  agentteam_tidb_user="$(agentteam_tidb_url_decode "$agentteam_tidb_user_encoded")"
  agentteam_tidb_password="$(agentteam_tidb_url_decode "$agentteam_tidb_password_encoded")"
  agentteam_tidb_database="$(agentteam_tidb_url_decode "$database")"
  agentteam_tidb_query_string="$query_string"

  if [[ -z "$agentteam_tidb_user" ]]; then
    agentteam_tidb_fail "AGENTTEAM_DATABASE_DSN must include a mysql user"
  fi
  if [[ -z "$agentteam_tidb_database" ]]; then
    agentteam_tidb_fail "AGENTTEAM_DATABASE_DSN must include a database name"
  fi

  agentteam_tidb_parse_addr "$addr"
  if [[ -z "$agentteam_tidb_host" ]] || [[ -z "$agentteam_tidb_port" ]]; then
    agentteam_tidb_fail "AGENTTEAM_DATABASE_DSN must include a tcp host and port"
  fi

  agentteam_tidb_tls="false"
  if agentteam_tidb_tls_value="$(agentteam_tidb_query_param "$query_string" "tls" 2>/dev/null)"; then
    if [[ -n "$agentteam_tidb_tls_value" ]]; then
      agentteam_tidb_tls="$agentteam_tidb_tls_value"
    fi
  fi
}

agentteam_tidb_prepare_empty_dir() {
  local dir_path="$1"

  if [[ -e "$dir_path" ]] && [[ ! -d "$dir_path" ]]; then
    agentteam_tidb_fail "path is not a directory: $dir_path"
  fi

  mkdir -p "$dir_path"

  if find "$dir_path" -mindepth 1 -print -quit | grep -q .; then
    agentteam_tidb_fail "output directory must be empty: $dir_path"
  fi
}

agentteam_tidb_require_input_dir() {
  local dir_path="$1"

  if [[ ! -d "$dir_path" ]]; then
    agentteam_tidb_fail "input directory does not exist: $dir_path"
  fi

  if ! find "$dir_path" -type f \( -name '*.sql' -o -name '*.sql.gz' -o -name '*.csv' -o -name '*.csv.gz' \) -print -quit | grep -q .; then
    agentteam_tidb_fail "input directory does not contain dumpling data files: $dir_path"
  fi
}

agentteam_tidb_resolve_tiup_bin() {
  local tiup_bin="${AGENTTEAM_TIUP_BIN:-tiup}"
  if [[ "$tiup_bin" == */* ]]; then
    [[ -x "$tiup_bin" ]] || agentteam_tidb_fail "tiup binary is not executable: $tiup_bin"
  else
    agentteam_tidb_require_command "$tiup_bin"
  fi
  printf '%s' "$tiup_bin"
}

agentteam_tidb_append_tls_file_flags() {
  local -n target_args_ref=$1
  local ca_path="${AGENTTEAM_TIDB_TLS_CA_PATH:-}"
  local cert_path="${AGENTTEAM_TIDB_TLS_CERT_PATH:-}"
  local key_path="${AGENTTEAM_TIDB_TLS_KEY_PATH:-}"

  if [[ -n "$ca_path" ]]; then
    target_args_ref+=("--ca" "$ca_path")
  fi
  if [[ -n "$cert_path" ]]; then
    target_args_ref+=("--cert" "$cert_path")
  fi
  if [[ -n "$key_path" ]]; then
    target_args_ref+=("--key" "$key_path")
  fi
}

agentteam_tidb_validate_export_tls_support() {
  case "$agentteam_tidb_tls" in
    ""|false)
      return 0
      ;;
    *)
      if [[ -n "${AGENTTEAM_TIDB_TLS_CA_PATH:-}" ]]; then
        return 0
      fi
      agentteam_tidb_fail "tidb-export currently supports tls=false or cert-based TLS via AGENTTEAM_TIDB_TLS_CA_PATH/AGENTTEAM_TIDB_TLS_CERT_PATH/AGENTTEAM_TIDB_TLS_KEY_PATH; got tls=$agentteam_tidb_tls"
      ;;
  esac
}

agentteam_tidb_validate_import_tls_support() {
  case "$agentteam_tidb_tls" in
    ""|false|true|skip-verify|preferred)
      return 0
      ;;
    *)
      if [[ -n "${AGENTTEAM_TIDB_TLS_CA_PATH:-}" ]]; then
        return 0
      fi
      agentteam_tidb_fail "tidb-import does not recognize tls mode $agentteam_tidb_tls without explicit AGENTTEAM_TIDB_TLS_CA_PATH configuration"
      ;;
  esac
}
