#!/usr/bin/bash
# shellcheck disable=SC2034
dns_volcengine_info='Volcano Engine DNS API
Site: https://www.volcengine.com/docs/6758/155086
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_volcengine
Options:
 Volcengine_ACCESS_KEY_ID API Key ID
 Volcengine_SECRET_ACCESS_KEY API Secret
 Volcengine_SESSION_TOKEN Session Token. Optional, only needed when using temporary STS credentials.
Issues: github.com/acmesh-official/acme.sh/issues/7064
'

Volcengine_HOST="dns.volcengineapi.com"
Volcengine_URL="https://$Volcengine_HOST"

########  Public functions #####################

#fulldomain txtvalue
dns_volcengine_add() {
  fulldomain=$1
  txtvalue=$2
  _record_id=""

  Volcengine_ACCESS_KEY_ID="${Volcengine_ACCESS_KEY_ID:-$(_readaccountconf_mutable Volcengine_ACCESS_KEY_ID)}"
  Volcengine_SECRET_ACCESS_KEY="${Volcengine_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable Volcengine_SECRET_ACCESS_KEY)}"

  if [ -z "$Volcengine_ACCESS_KEY_ID" ] || [ -z "$Volcengine_SECRET_ACCESS_KEY" ]; then
    Volcengine_ACCESS_KEY_ID=""
    Volcengine_SECRET_ACCESS_KEY=""
    _err "You haven't specified the volcengine dns api key id and api key secret yet."
    return 1
  fi

  #save the api key and email to the account conf file.
  _saveaccountconf_mutable Volcengine_ACCESS_KEY_ID "$Volcengine_ACCESS_KEY_ID"
  _saveaccountconf_mutable Volcengine_SECRET_ACCESS_KEY "$Volcengine_SECRET_ACCESS_KEY"

  _debug "First detect the root zone"
  if ! _get_root "$fulldomain"; then
    _err "invalid domain"
    _sleep 1
    return 1
  fi
  _debug _domain_id "$_domain_id"
  _debug _sub_domain "$_sub_domain"
  _debug _domain "$_domain"

  # _info "Getting existing records for $fulldomain"
  if ! volcengine_rest POST "" "Action=ListRecords&Version=2018-08-01" "{\"ZID\":$_domain_id,\"Host\":\"$_sub_domain\",\"Type\":\"TXT\",\"Value\":\"$txtvalue\",\"SearchMode\":\"exact\"}"; then
    _sleep 1
    return 1
  fi

  # ListRecords already filtered by ZID + Host + Value + SearchMode:exact,
  # so any returned record is our target. Don't match on FQDN: Volcengine
  # lowercases the Host/FQDN in the response, which would break a
  # case-sensitive string compare against $fulldomain.
  _record_id="$(echo "$response" | _egrep_o "\"RecordID\":\"[0-9]+\"," | cut -d: -f2 | cut -d, -f1 | tr -d '"')"
  _debug "_record_id" "$_record_id"

  if [ "$_record_id" ] && _contains "$response" "$txtvalue"; then
    _info "The TXT record already exists. Skipping."
    _sleep 1
    return 0
  fi

  _debug "Adding records"

  if volcengine_rest POST "" "Action=CreateRecord&Version=2018-08-01" "{\"ZID\":$_domain_id,\"Host\":\"$_sub_domain\",\"Type\":\"TXT\",\"Value\":\"$txtvalue\",\"Remark\":\"acme.sh\"}"; then
    _info "TXT record updated successfully."
    _sleep 1
    return 0
  fi

  _sleep 1
  return 1
}

#fulldomain txtvalue
dns_volcengine_rm() {
  fulldomain=$1
  txtvalue=$2
  _record_id=""

  Volcengine_ACCESS_KEY_ID="${Volcengine_ACCESS_KEY_ID:-$(_readaccountconf_mutable Volcengine_ACCESS_KEY_ID)}"
  Volcengine_SECRET_ACCESS_KEY="${Volcengine_SECRET_ACCESS_KEY:-$(_readaccountconf_mutable Volcengine_SECRET_ACCESS_KEY)}"

  _debug "First detect the root zone"
  if ! _get_root "$fulldomain"; then
    _err "invalid domain"
    _sleep 1
    return 1
  fi
  _debug _domain_id "$_domain_id"
  _debug _sub_domain "$_sub_domain"
  _debug _domain "$_domain"

  _info "Getting existing records for $fulldomain"

  if ! volcengine_rest POST "" "Action=ListRecords&Version=2018-08-01" "{\"ZID\":$_domain_id,\"Host\":\"$_sub_domain\",\"Type\":\"TXT\",\"Value\":\"$txtvalue\",\"SearchMode\":\"exact\"}"; then
    _sleep 1
    return 1
  fi

  # ListRecords already filtered by ZID + Host + Value + SearchMode:exact,
  # so any returned record is our target. Don't match on FQDN: Volcengine
  # lowercases the Host/FQDN in the response, which would break a
  # case-sensitive string compare against $fulldomain.
  _record_id="$(echo "$response" | _egrep_o "\"RecordID\":\"[0-9]+\"," | cut -d: -f2 | cut -d, -f1 | tr -d '"')"
  _debug "_record_id" "$_record_id"

  if [ -z "$_record_id" ]; then
    _debug "no records exist, skip"
    _sleep 1
    return 0
  fi

  if volcengine_rest POST "" "Action=DeleteRecord&Version=2018-08-01" "{\"RecordID\":\"$_record_id\"}"; then
    _info "TXT record deleted successfully."
    _sleep 1
    return 0
  fi
  _sleep 1
  return 1
}

####################  Private functions below ##################################

_get_root() {
  domain=$1
  i=1
  p=1

  # iterate over names (a.b.c.d -> b.c.d -> c.d -> d)
  while true; do
    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
    _debug "Checking domain: $h"
    if [ -z "$h" ]; then
      _err "invalid domain"
      return 1
    fi

    # iterate over paginated result for list_hosted_zones
    if ! volcengine_rest POST "" "Action=ListZones&Version=2018-08-01" "{\"Key\":\"$h\",\"SearchMode\":\"exact\"}"; then
      return 1
    fi
    if _contains "$response" "\"ZoneName\":\"$h\""; then
      _domain_id=$(printf "%s" "$response" | _egrep_o "\"ZID\":[0-9]+," | cut -d: -f2 | cut -d, -f1)
      if [ "$_domain_id" ]; then
        _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
        _domain=$h
        return 0
      fi
      _err "Can't find domain with id: $h"
      return 1
    fi
    p=$i
    i=$(_math "$i" + 1)
  done
  return 1
}

#method uri qstr data
volcengine_rest() {
  mtd="$1"
  ep="$2"
  qsr="$3"
  data="$4"

  _debug mtd "$mtd"
  _debug ep "$ep"
  _debug qsr "$qsr"
  _debug data "$data"

  # clear any header state left over from a previous request so that
  # conditionally-set headers (e.g. x-content-sha256, x-security-token)
  # can't leak into the next request
  _H1=""
  _H2=""
  _H3=""
  _H4=""
  _H5=""

  CanonicalURI="/$ep"
  _debug2 CanonicalURI "$CanonicalURI"

  CanonicalQueryString="$qsr"
  _debug2 CanonicalQueryString "$CanonicalQueryString"

  RequestDate="$(date -u +"%Y%m%dT%H%M%SZ")"
  _debug2 RequestDate "$RequestDate"

  Hash="sha256"

  _H1="X-Date: $RequestDate"
  _debug2 _H1 "$_H1"

  volcengine_host="$Volcengine_HOST"
  CanonicalHeaders="host:$volcengine_host\n"
  SignedHeaders="host"

  if [ -n "$data" ]; then
    XContentSha256="$(printf "%s" "$data" | _digest "$Hash" hex)"
    _H4="x-content-sha256: $XContentSha256"
    _debug2 _H4 "$_H4"

    CanonicalHeaders="${CanonicalHeaders}x-content-sha256:$XContentSha256\n"
    SignedHeaders="${SignedHeaders};x-content-sha256"
  fi

  CanonicalHeaders="${CanonicalHeaders}x-date:$RequestDate\n"
  SignedHeaders="${SignedHeaders};x-date"

  if [ -n "$Volcengine_SESSION_TOKEN" ]; then
    _H3="x-security-token: $Volcengine_SESSION_TOKEN"
    CanonicalHeaders="${CanonicalHeaders}x-security-token:$Volcengine_SESSION_TOKEN\n"
    SignedHeaders="${SignedHeaders};x-security-token"
  fi

  _debug2 CanonicalHeaders "$CanonicalHeaders"
  _debug2 SignedHeaders "$SignedHeaders"

  RequestPayload="$data"
  _debug2 RequestPayload "$RequestPayload"

  CanonicalRequest="$mtd\n$CanonicalURI\n$CanonicalQueryString\n$CanonicalHeaders\n$SignedHeaders\n$(printf "%s" "$RequestPayload" | _digest "$Hash" hex)"
  _debug2 CanonicalRequest "$CanonicalRequest"

  HashedCanonicalRequest="$(printf '%b' "$CanonicalRequest" | _digest "$Hash" hex)"
  _debug2 HashedCanonicalRequest "$HashedCanonicalRequest"

  Algorithm="HMAC-SHA256"
  _debug2 Algorithm "$Algorithm"

  RequestDateOnly="$(echo "$RequestDate" | cut -c 1-8)"
  _debug2 RequestDateOnly "$RequestDateOnly"

  Region="cn-beijing"
  Service="dns"

  CredentialScope="$RequestDateOnly/$Region/$Service/request"
  _debug2 CredentialScope "$CredentialScope"

  StringToSign="$Algorithm\n$RequestDate\n$CredentialScope\n$HashedCanonicalRequest"

  _debug2 StringToSign "$StringToSign"

  kSecret="$Volcengine_SECRET_ACCESS_KEY"

  _secure_debug2 kSecret "$kSecret"

  kSecretH="$(printf "%s" "$kSecret" | _hex_dump | tr -d " ")"
  _secure_debug2 kSecretH "$kSecretH"

  kDateH="$(printf "%s" "$RequestDateOnly" | _hmac "$Hash" "$kSecretH" hex)"
  _debug2 kDateH "$kDateH"

  kRegionH="$(printf "%s" "$Region" | _hmac "$Hash" "$kDateH" hex)"
  _debug2 kRegionH "$kRegionH"

  kServiceH="$(printf "%s" "$Service" | _hmac "$Hash" "$kRegionH" hex)"
  _debug2 kServiceH "$kServiceH"

  kSigningH="$(printf "%s" "request" | _hmac "$Hash" "$kServiceH" hex)"
  _debug2 kSigningH "$kSigningH"

  signature="$(printf '%b' "$StringToSign" | _hmac "$Hash" "$kSigningH" hex)"
  _debug2 signature "$signature"

  Authorization="$Algorithm Credential=$Volcengine_ACCESS_KEY_ID/$CredentialScope, SignedHeaders=$SignedHeaders, Signature=$signature"
  _debug2 Authorization "$Authorization"

  _H2="Authorization: $Authorization"
  _debug2 _H2 "$_H2"

  url="$Volcengine_URL/$ep"
  if [ "$qsr" ]; then
    url="$Volcengine_URL/$ep?$qsr"
  fi

  if [ "$mtd" = "GET" ]; then
    response="$(_get "$url")"
  else
    response="$(_post "$data" "$url" "" "POST" "application/json")"
  fi

  _ret="$?"
  _debug response "$response"
  if [ "$_ret" = "0" ]; then
    if _contains "$response" "\"Error\":{"; then
      _err "Response error:$response"
      return 1
    fi
  fi

  return "$_ret"
}
