#!/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/base-config.yaml
bucket_name=${remote_config#s3://}
bucket_name=${bucket_name%%/*}
output_bucket_region=$(aws s3api get-bucket-location --bucket=$bucket_name --query="LocationConstraint" \
  --region=us-west-2 --output=text --no-cli-pager)
aws s3 cp ${remote_config} ${local_config}  --region=$output_bucket_region

o11y_repo=tidbcloud/observability
git_ref="main"
cloud_provider=$(yq '.cloudProvider' "${local_config}")
region=$(yq '.region' "${local_config}")
logical_region_name=$(yq '.name' "${local_config}")
vendor=$(yq '.cloudProvider' "${local_config}")
jobId=$(yq '.jobId' "${local_config}")
env=$(yq '.env' "${local_config}")
image_registry=$(yq '.imageRegistry' "${local_config}")
cluster_name=$(yq '.k8s.name' "${local_config}")
account_id=$(yq '.aws.controlplaneAccount' "${local_config}")

# common variables
echo "cloud_provider: $cloud_provider"
echo "region: $region"
echo "logical_region_name: $logical_region_name"
echo "env: $env"
echo "cluster_name: $cluster_name"
echo "account_id: $account_id"

remote_iam_config=$(yq '.jobOutput.basic-iam' "${local_config}")
local_iam_config=/tmp/iam-config.yaml
aws s3 cp ${remote_iam_config} ${local_iam_config}  --region=$output_bucket_region
alb_role_arn=$(yq '.loadBalancerController.roleArn' "${local_iam_config}")
ca_role_arn=$(yq '.awsClusterAutoscaler.roleArn' "${local_iam_config}")
external_dns_role_arn=$(yq '.externalDns.roleArn' "${local_iam_config}")

zone=$(yq '.jobOptions.o11y.zone' "${local_config}")
zone_suffix=${zone#$region}
elb_account_id_for_s3_bucket_policy=$(yq '.jobOptions.o11y.elbAccountIdForS3BucketPolicy' "${local_config}")
nlb_allowed_principals=$(yq '.jobOptions.o11y.nlbAllowedPrincipals[]' "${local_config}" | tr '\n' ',' | sed 's/,$//')

hosted_zone_name=$(yq '.jobOptions.o11y.dns.hostedZoneName // ""' "${local_config}")
if [ -z "$hosted_zone_name" ]; then
  if [ "$env" == "prod" ]; then
      hosted_zone_name="observability.tidbcloud.com"
  else
      hosted_zone_name="observability-$env.pingcap.cloud"
  fi
fi
echo "hosted_zone_name: $hosted_zone_name"

hosted_zone_id=$(aws route53 list-hosted-zones \
  --query "HostedZones[?Name=='$hosted_zone_name.'].Id" \
  --output text)
hosted_zone_id_suffix=${hosted_zone_id#/hostedzone/}
echo "hosted_zone_id: $hosted_zone_id"

ds_logs_bucket_name="$cluster_name-ds-logs"
if ! aws s3api head-bucket --bucket "$ds_logs_bucket_name" 2>/dev/null; then
    echo "ds logs bucket $ds_logs_bucket_name does not exist, creating..."
    if [[ "$region" == "us-east-1" ]]; then
      aws s3api create-bucket \
        --bucket $ds_logs_bucket_name \
        --region $region
    else
      aws s3api create-bucket \
        --bucket $ds_logs_bucket_name \
        --region $region \
        --create-bucket-configuration LocationConstraint=$region
    fi
    aws s3api put-public-access-block \
      --bucket $ds_logs_bucket_name \
      --public-access-block-configuration \
      "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"

else
	echo "ds logs bucket $ds_logs_bucket_name already exists"
fi

# Configure bucket policy (idempotent - runs whether bucket was just created or already existed)
echo "Configuring bucket policy for $ds_logs_bucket_name..."
cat >/tmp/bucket-policy.json <<EOF
{
  "Version": "2012-10-17",
  "Id": "AWSConsole-AccessLogs-Policy-1628794925198",
  "Statement": [
    {
      "Sid": "AWSConsoleStmt-regions-after-202208",
      "Effect": "Allow",
      "Principal": {
        "Service": "logdelivery.elasticloadbalancing.amazonaws.com"
      },
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::$ds_logs_bucket_name/*"
    },
    {
      "Sid": "AWSLogDeliveryWrite",
      "Effect": "Allow",
      "Principal": {
        "Service": "delivery.logs.amazonaws.com"
      },
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::$ds_logs_bucket_name/*",
      "Condition": {
        "StringEquals": {
          "s3:x-amz-acl": "bucket-owner-full-control"
        }
      }
    },
    {
      "Sid": "AWSLogDeliveryAclCheck",
      "Effect": "Allow",
      "Principal": {
        "Service": "delivery.logs.amazonaws.com"
      },
      "Action": "s3:GetBucketAcl",
      "Resource": "arn:aws:s3:::$ds_logs_bucket_name"
    }
  ]
}
EOF
if [ ! -z "$elb_account_id_for_s3_bucket_policy" ]; then
	cp /tmp/bucket-policy.json /tmp/bucket-policy-temp.json
	jq --arg account_id "$elb_account_id_for_s3_bucket_policy" --arg bucket "$ds_logs_bucket_name" '.Statement = ([{
        "Sid": "AWSConsoleStmt-1628794925198",
        "Effect": "Allow",
        "Principal": {
          "AWS": ("arn:aws:iam::" + $account_id + ":root")
        },
        "Action": "s3:PutObject",
        "Resource": ("arn:aws:s3:::" + $bucket + "/*")
      }] + .Statement)' /tmp/bucket-policy-temp.json > /tmp/bucket-policy.json
	rm /tmp/bucket-policy-temp.json
fi
aws s3api put-bucket-policy \
	--bucket $ds_logs_bucket_name \
	--policy file:///tmp/bucket-policy.json
echo "Bucket policy configured for $ds_logs_bucket_name"


outputs=$(aws cloudformation describe-stacks \
  --stack-name auto-deploy-$cluster_name-ingress \
  --query 'Stacks[0].Outputs' \
  --output json)
ds_cert_arn=$(echo $outputs | jq -r '.[] | select(.OutputKey=="DsCertificateArn") | .OutputValue')
ds_waf_acl_arn=$(echo $outputs | jq -r '.[] | select(.OutputKey=="DsWebAclArn") | .OutputValue')
ds_logs_bucket_name="$cluster_name-ds-logs"

vpc_id=$(aws eks describe-cluster \
  --name $cluster_name \
  --query "cluster.resourcesVpcConfig.vpcId" \
  --output text)
echo "vpc_id: $vpc_id"

subnet_info=$(aws ec2 describe-subnets \
  --filters "Name=vpc-id,Values=$vpc_id" "Name=availability-zone,Values=$zone" "Name=tag:Name,Values=*Private*" \
  --query "Subnets[0].{SubnetId:SubnetId, Name:Tags[?Key=='Name'].Value | [0]}" \
  --output json)
subnet_id=$(echo $subnet_info | jq -r '.SubnetId')
subnet_name=$(echo $subnet_info | jq -r '.Name')
echo "subnet_id: $subnet_id"
echo "subnet_name: $subnet_name"

# init and deploy cloud-addon components managed in o11y repo
args=$(cat <<EOF
{
  "hostedZoneName": "$hosted_zone_name",
  "hostedZoneId": "$hosted_zone_id",
  "imageRegistry": "$image_registry",
  "clusterAutoscalerRoleArn": "$ca_role_arn",
  "albControllerRoleArn": "$alb_role_arn",
  "externalDNSRoleArn": "$external_dns_role_arn",
  "dsWAFID": "$ds_waf_acl_arn",
  "dsCertID": "$ds_cert_arn",
  "dsLogsBucketName": "$ds_logs_bucket_name",
  "subnetName": "$subnet_name"
}
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=${logical_region_name} -f vendor=${vendor} -f git-ref=${git_ref}

# Wait services ready
declare -A ss
ss["deploy/ingress-nginx-read-controller -n kube-system"]="false"
ss["deploy/ingress-nginx-write-controller -n kube-system"]="false"
ss["deploy/external-dns -n kube-system"]="false"
ss["deploy/cluster-autoscaler -n kube-system"]="false"
ss["deploy/aws-load-balancer-controller -n kube-system"]="false"

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

# TODO: move following resources to a standalone job
echo "waiting for nlb aws state..."
nlb_name="ngx-w-$logical_region_name"
nlb_state=""
while [ "$nlb_state" != "active" ]; do
  nlb_state=$(aws elbv2 describe-load-balancers --names "$nlb_name" --query 'LoadBalancers[0].State.Code' --output text)
  if [ "$nlb_state" != "active" ]; then
    echo "nlb_state: $nlb_state, waiting..."
    sleep 30
  fi
done
echo "nlb ready: $nlb_name"

nlb_arn=$(aws elbv2 describe-load-balancers --names "$nlb_name" --query 'LoadBalancers[0].LoadBalancerArn' --output text)
echo "nlb_arn: $nlb_arn"

aws cloudformation deploy \
  --region $region \
  --stack-name auto-deploy-$cluster_name-vpc-endpoint-service \
  --template-file $ROOT/aws/cloudformation/vpc-endpoint-service.yaml \
  --parameter-overrides \
    NLBArn=$nlb_arn \
    NLBAllowedPrincipals=$nlb_allowed_principals

outputs=$(aws cloudformation describe-stacks \
  --stack-name auto-deploy-$cluster_name-vpc-endpoint-service \
  --query 'Stacks[0].Outputs' \
  --output json)
vpc_endpoint_service_id=$(echo $outputs | jq -r '.[] | select(.OutputKey=="VpcEndpointServiceId") | .OutputValue')
echo "vpc_endpoint_service_id: $vpc_endpoint_service_id"

private_dns_name="prometheus-endpoint-$zone_suffix.$hosted_zone_name"
echo "private_dns_name: $private_dns_name"

# We currently cannot configure private dns name using cloudformation.
# See: https://github.com/aws-cloudformation/cloudformation-coverage-roadmap/issues/367
aws ec2 modify-vpc-endpoint-service-configuration \
  --service-id $vpc_endpoint_service_id \
  --private-dns-name $private_dns_name

dns_config=$(aws ec2 describe-vpc-endpoint-service-configurations \
  --service-ids $vpc_endpoint_service_id \
  --query 'ServiceConfigurations[0].PrivateDnsNameConfiguration' \
  --output json)
dns_name=$(echo $dns_config | jq -r '.Name')
dns_type=$(echo $dns_config | jq -r '.Type')
dns_value=$(echo $dns_config | jq -r '.Value')
echo "dns_name: $dns_name"
echo "dns_type: $dns_type"
echo "dns_value: $dns_value"

endpoint_service_dns_name=$(aws ec2 describe-vpc-endpoint-service-configurations \
  --service-ids $vpc_endpoint_service_id \
  --query 'ServiceConfigurations[0].ServiceName' \
  --output text)
aws route53 change-resource-record-sets \
  --hosted-zone-id $hosted_zone_id \
  --change-batch '{
    "Changes": [
      {
        "Action": "UPSERT",
        "ResourceRecordSet": {
          "Name": "'"$dns_name.$hosted_zone_name"'",
          "Type": "'"$dns_type"'",
          "TTL": 300,
          "ResourceRecords": [
            {
              "Value": "\"'"$dns_value"'\""
            }
          ]
        }
      }
    ]
  }'

# Create S3 Gateway Endpoint
echo "Creating S3 Gateway Endpoint..."
vpc_id=$(aws eks describe-cluster \
  --region $region \
  --name $cluster_name \
  --query "cluster.resourcesVpcConfig.vpcId" \
  --output text)
echo "vpc_id: $vpc_id"

# Get route table IDs for the VPC
route_table_ids=$(aws ec2 describe-route-tables \
  --region $region \
  --filters "Name=vpc-id,Values=$vpc_id" \
  --query "RouteTables[].RouteTableId" \
  --output text)
echo "route_table_ids: $route_table_ids"

# Check if S3 gateway endpoint already exists
existing_endpoint=$(aws ec2 describe-vpc-endpoints \
  --region $region \
  --filters "Name=vpc-id,Values=$vpc_id" "Name=service-name,Values=com.amazonaws.$region.s3" "Name=vpc-endpoint-type,Values=Gateway" \
  --query "VpcEndpoints[0].VpcEndpointId" \
  --output text)

if [ "$existing_endpoint" == "None" ] || [ -z "$existing_endpoint" ]; then
  echo "Creating new S3 Gateway Endpoint..."
  endpoint_id=$(aws ec2 create-vpc-endpoint \
    --region $region \
    --vpc-id $vpc_id \
    --service-name com.amazonaws.$region.s3 \
    --vpc-endpoint-type Gateway \
    --route-table-ids $route_table_ids \
    --query "VpcEndpoint.VpcEndpointId" \
    --output text)
  echo "Created S3 Gateway Endpoint: $endpoint_id"
else
  echo "S3 Gateway Endpoint already exists: $existing_endpoint"
fi
