#!/usr/bin/env bash

set -x
set -euo pipefail

ROOT=$(cd $(dirname ${BASH_SOURCE[0]}) && pwd)
source ${ROOT}/../lib.sh

remote_config=${CONFIG_PATH}
local_config=/tmp/config.yaml
download_blob "${remote_config}" "${local_config}"

o11y_repo=tidbcloud/observability
git_ref="main"

region=$(yq '.region' "${local_config}")
logical_region_name=$(yq '.name' "${local_config}")
env=$(yq '.env' "${local_config}")
vendor=$(yq '.cloudProvider' "${local_config}")
cluster_name=$(yq '.k8s.name' "${local_config}")
image_registry=$(yq '.imageRegistry' "${local_config}")
subscription_id=$(yq '.azure.controlplaneSubscriptionId' "${local_config}")
jobId=$(yq '.jobId' "${local_config}")
resource_group="$cluster_name-infra"
aks_resource_group="$cluster_name"

vnet_name="${cluster_name}-vnet"
appgw_subnet_name="${cluster_name}-appgw-subnet"

hosted_zone_name="observability-$env.pingcap.cloud"
if [ "$env" == "prod" ]; then
    hosted_zone_name="observability.tidbcloud.com"
fi
echo "hosted_zone_name: $hosted_zone_name"

private_link_service_name="nginx-write-$cluster_name"
waf_rs_name="$cluster_name-rs-api"
waf_ds_name="$cluster_name-ds-api"
application_gateway_name="appgw-$cluster_name"
public_ip_name="appgw-pip-$cluster_name"


# create WAF
az network application-gateway waf-policy create \
  --name $waf_rs_name \
  --resource-group $resource_group \
  --type OWASP \
  --version 3.2 \
  --custom-rules ${ROOT}/waf/custom-rs.json \
  --policy-settings state=Enabled mode=Prevention request-body-check=no

rs_waf_id=$(az network application-gateway waf-policy show --name $waf_rs_name --resource-group $resource_group| jq -r ".id")

az network application-gateway waf-policy create \
  --name $waf_ds_name \
  --resource-group $resource_group \
  --type OWASP \
  --version 3.2 \
  --custom-rules ${ROOT}/waf/custom-ds.json \
  --policy-settings state=Enabled mode=Prevention

ds_waf_id=$(az network application-gateway waf-policy show --name $waf_ds_name --resource-group $resource_group| jq -r ".id")

# enable AGIC for aks
az network public-ip create \
  --name $public_ip_name \
  --resource-group $resource_group \
  --allocation-method Static \
  --sku Standard

appgw_subnet_id=$(az network vnet subnet show \
  --name $appgw_subnet_name \
  --vnet-name $vnet_name \
  --resource-group $aks_resource_group \
  -o tsv --query "id")

az network application-gateway create \
  --name $application_gateway_name \
  --resource-group $resource_group \
  --sku WAF_v2 \
  --public-ip-address $public_ip_name \
  --waf-policy $rs_waf_id \
  --subnet $appgw_subnet_id \
  --priority 100

appgwId=$(az network application-gateway show \
  --name $application_gateway_name \
  --resource-group $resource_group \
  -o tsv --query "id")
enableAppGw $cluster_name $aks_resource_group $appgwId

deploy_tenant_id=$(yq '.azure.controlplaneTenantId' "${local_config}")
deploy_client_id=$(yq '.azure.controlplaneClientId' "${local_config}")
azureLogin $deploy_tenant_id $deploy_client_id
appgw_addon_identity_principal_id=$(az aks show --name $cluster_name \
  --resource-group $cluster_name \
  --query "addonProfiles.ingressApplicationGateway.identity.objectId" \
  -o tsv)
az role assignment create --role "Network Contributor" \
  --assignee-object-id "${appgw_addon_identity_principal_id}" --assignee-principal-type ServicePrincipal \
  --scope "/subscriptions/${subscription_id}/resourceGroups/${aks_resource_group}"

public_ip=$(az network public-ip show --name $public_ip_name --resource-group $resource_group -o tsv --query "ipAddress")
hosted_zone_id=$(yq '.jobOptions.o11y.route53ZoneId' "${local_config}")
echo "add route53 A record for public_ip: $public_ip"
change_batch=$(cat <<EOF
{
  "Comment": "Update A records for azure ${cluster_name}",
  "Changes": [
    {
      "Action": "UPSERT",
      "ResourceRecordSet": {
        "Name": "www.rs.${logical_region_name}.azure.${hosted_zone_name}",
        "Type": "A",
        "TTL": 300,
        "ResourceRecords": [{ "Value": "$public_ip" }]
      }
    },
    {
      "Action": "UPSERT",
      "ResourceRecordSet": {
        "Name": "www.ds.${logical_region_name}.azure.${hosted_zone_name}",
        "Type": "A",
        "TTL": 300,
        "ResourceRecords": [{ "Value": "$public_ip" }]
      }
    }
  ]
}
EOF
)

# login to AWS
export AWS_REGION=us-west-2
id_token=$(curl -H "Authorization: Bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" "${ACTIONS_ID_TOKEN_REQUEST_URL}" | jq -r '.value')
role=$(yq '.jobOptions.awsAccountRole' "${local_config}")
resp=$(aws sts assume-role-with-web-identity --web-identity-token "${id_token}" \
  --role-arn "${role}" \
  --role-session-name "azure-iam-${env}-${logical_region_name}")
export AWS_ACCESS_KEY_ID=$(echo $resp | jq -r '.Credentials.AccessKeyId')
export AWS_SECRET_ACCESS_KEY=$(echo $resp | jq -r '.Credentials.SecretAccessKey')
export AWS_SESSION_TOKEN=$(echo $resp | jq -r '.Credentials.SessionToken')
aws_account_id=$(aws sts get-caller-identity --query "Account" --output text --no-cli-pager)
echo "Login to AWS with account ${aws_account_id}"

AWS_MAX_ATTEMPTS=20 aws route53 change-resource-record-sets \
  --hosted-zone-id "$hosted_zone_id" \
  --change-batch "$change_batch"

# init and deploy cloud-addon components managed in o11y repo
args=$(cat <<EOF
{
  "hostedZoneName": "$hosted_zone_name",
  "dsWAFID": "$ds_waf_id",
  "privateLinkServiceName": "$private_link_service_name",
  "imageRegistry": "$image_registry"
}
EOF
)
echo "args: $args"

common_params="-f vendor=${vendor} -f region=${region} -f logicalRegionName=${logical_region_name} -f env=${env} -f cluster=${cluster_name} -f jobId=${jobId}"
# Create pull request by invoking new-region workflow.
gh workflow run new-region-cloud-addon.yaml --repo ${o11y_repo} --ref ${git_ref} ${common_params} -f args="${args}"
wait_workflow_run_to_merge new-region-cloud-addon.yaml ${o11y_repo}
if [[ $? -eq 1 ]]; then
    exit 1
fi

gh workflow run cloud-addon-cd.yaml --repo ${o11y_repo} --ref ${git_ref} -f env=${env} -f region=${region} -f vendor=${vendor} -f git-ref=${git_ref}

# Wait services ready
declare -A ss
ss["deploy/ingress-nginx-read-controller -n observability"]="false"
ss["deploy/ingress-nginx-write-controller -n kube-system"]="false"

wait_for_services_ready ss
if [[ $? -eq 1 ]]; then
    echo "Services are not ready"
    exit 1
fi

echo "Waiting for kubernetes service and private link service to be ready"
start_time=$(date +%s)
timeout=600  # 10 minutes
interval=10
while [[ $(($(date +%s) - start_time)) -lt $timeout ]]; do
    set +e
    k8s_service_ready=false
    pls_ready=false

    # Check Kubernetes service
    ip=$(kubectl get svc ingress-nginx-write-controller-internal -n kube-system \
      -ojsonpath='{.status.loadBalancer.ingress[*].ip}' 2>/dev/null || echo "")
    if [[ -n "$ip" ]]; then
        echo "Internal service status is ready with IP: $ip"
        k8s_service_ready=true
    fi

    # Check Azure Private Link Service
    if az network private-link-service show \
        --name "${private_link_service_name}" \
        --resource-group "${resource_group}" > /dev/null 2>&1; then
        echo "Private link service found"
        pls_ready=true
    fi

    if [[ "$k8s_service_ready" == "true" && "$pls_ready" == "true" ]]; then
        set -e
        break
    fi

    set -e
    echo "Services not fully ready yet. Retrying in $interval seconds..."
    sleep "$interval"
done

# change the tag "k8s-azure-owner-service" of private link service to o11y,
# thus to avoid configs being overwritten latter by controller.
az network private-link-service update \
    --name "${private_link_service_name}" \
    --resource-group "${resource_group}" \
    --tags "k8s-azure-owner-service=o11y"

local_output=/tmp/output.yaml
remote_output=$(yq '.outputPath' "${local_config}")
cat <<EOF > "${local_output}"
addon:
  rsWAFID: $rs_waf_id
  dsWAFID: $ds_waf_id
  privateLinkServiceName: $private_link_service_name
EOF

echo "Write output to storage..."
upload_blob "${local_output}" "${remote_output}" --overwrite
