#!/usr/bin/env bash
# Controlled school-account deployment helper.
#
# Read-only commands:
#   ./scripts/school-deploy.sh plan
#   ./scripts/school-deploy.sh status
#
# Write commands require EG334S_EXECUTE=DEPLOY and an exact account-ID match:
#   ./scripts/school-deploy.sh deploy-onprem
#   ./scripts/school-deploy.sh deploy-hub
#   ./scripts/school-deploy.sh configure-vpn1-firewall
#   ./scripts/school-deploy.sh deploy-alb
#   ./scripts/school-deploy.sh deploy-recovery

set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
ACTION="${1:-plan}"

PROFILE="${EG334S_AWS_PROFILE:-}"
EXPECTED_ACCOUNT="${EG334S_EXPECTED_ACCOUNT_ID:-}"
ADMIN_CIDR="${EG334S_ADMIN_CIDR:-}"
USE1_KEYPAIR="${EG334S_USE1_KEYPAIR:-}"
EUW1_KEYPAIR="${EG334S_EUW1_KEYPAIR:-}"
MONITORING_EMAIL="${EG334S_MONITORING_EMAIL:-}"
STACK_PREFIX="${EG334S_STACK_PREFIX:-eg334s-school}"

ONPREM_STACK="${STACK_PREFIX}-onprem"
HUB_STACK="${STACK_PREFIX}-hub"
ALB_STACK="${STACK_PREFIX}-alb"
RECOVERY_STACK="${STACK_PREFIX}-recovery"

die() {
  echo "ERROR: $*" >&2
  exit 2
}

need() {
  command -v "$1" >/dev/null 2>&1 || die "$1 is required"
}

need aws
need jq

[[ -n "$PROFILE" ]] || die "set EG334S_AWS_PROFILE"
[[ "$EXPECTED_ACCOUNT" =~ ^[0-9]{12}$ ]] ||
  die "EG334S_EXPECTED_ACCOUNT_ID must be the confirmed 12-digit school account ID"
[[ "$ADMIN_CIDR" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/32$ ]] ||
  die "EG334S_ADMIN_CIDR must be an explicit IPv4 /32"

identity="$(AWS_PROFILE="$PROFILE" aws sts get-caller-identity --output json)"
actual_account="$(jq -r '.Account // empty' <<<"$identity")"
caller_arn="$(jq -r '.Arn // empty' <<<"$identity")"
[[ "$actual_account" == "$EXPECTED_ACCOUNT" ]] ||
  die "authenticated account $actual_account does not match expected school account $EXPECTED_ACCOUNT"

echo "Account gate passed: $actual_account"
echo "Caller: $caller_arn"
echo "Stack prefix: $STACK_PREFIX"

output() {
  local region="$1" stack="$2" key="$3"
  AWS_PROFILE="$PROFILE" aws cloudformation describe-stacks \
    --region "$region" \
    --stack-name "$stack" \
    --query "Stacks[0].Outputs[?OutputKey=='$key'].OutputValue | [0]" \
    --output text
}

require_write_gate() {
  [[ "${EG334S_EXECUTE:-NO}" == "DEPLOY" ]] ||
    die "write blocked; set EG334S_EXECUTE=DEPLOY only during the attended deployment window"
  [[ -n "$USE1_KEYPAIR" ]] || die "set EG334S_USE1_KEYPAIR"
  [[ -n "$EUW1_KEYPAIR" ]] || die "set EG334S_EUW1_KEYPAIR"
}

deploy_onprem() {
  require_write_gate
  AWS_PROFILE="$PROFILE" aws cloudformation deploy \
    --region us-east-1 \
    --stack-name "$ONPREM_STACK" \
    --template-file "$ROOT/docs/iac/vpn1-onprem-us-east-1.yaml" \
    --parameter-overrides \
      KeyPairName="$USE1_KEYPAIR" \
      AdminCidr="$ADMIN_CIDR" \
      CreateCentralLogBucket=true \
    --tags Project=EG334S Environment=SCHOOL-DEMO ManagedBy=CloudFormation \
    --no-fail-on-empty-changeset
  AWS_PROFILE="$PROFILE" aws cloudformation describe-stacks \
    --region us-east-1 --stack-name "$ONPREM_STACK" \
    --query 'Stacks[0].Outputs' --output table
}

deploy_hub() {
  require_write_gate
  local strongswan_eip log_bucket
  strongswan_eip="$(output us-east-1 "$ONPREM_STACK" StrongSwanElasticIP)"
  log_bucket="$(output us-east-1 "$ONPREM_STACK" CentralLogBucketName)"
  [[ "$strongswan_eip" != "None" && -n "$strongswan_eip" ]] ||
    die "missing StrongSwanElasticIP from $ONPREM_STACK"
  [[ "$log_bucket" != "None" && -n "$log_bucket" ]] ||
    die "missing CentralLogBucketName from $ONPREM_STACK"

  AWS_PROFILE="$PROFILE" aws cloudformation deploy \
    --region eu-west-1 \
    --stack-name "$HUB_STACK" \
    --template-file "$ROOT/docs/iac/vpn1-awspublic-eu-west-1.yaml" \
    --parameter-overrides \
      KeyPairName="$EUW1_KEYPAIR" \
      AdminCidr="$ADMIN_CIDR" \
      OnPremStrongSwanEIP="$strongswan_eip" \
      CentralLogBucketName="$log_bucket" \
      MonitoringEmail="$MONITORING_EMAIL" \
      CreateAzureVpn=false \
    --tags Project=EG334S Environment=SCHOOL-DEMO ManagedBy=CloudFormation \
    --no-fail-on-empty-changeset
  AWS_PROFILE="$PROFILE" aws cloudformation describe-stacks \
    --region eu-west-1 --stack-name "$HUB_STACK" \
    --query 'Stacks[0].Outputs' --output table
}

configure_vpn1_firewall() {
  require_write_gate
  local vpn_id tunnel1 tunnel2
  vpn_id="$(output eu-west-1 "$HUB_STACK" VpnConnectionId)"
  read -r tunnel1 tunnel2 < <(
    AWS_PROFILE="$PROFILE" aws ec2 describe-vpn-connections \
      --region eu-west-1 \
      --vpn-connection-ids "$vpn_id" \
      --query 'VpnConnections[0].VgwTelemetry[].OutsideIpAddress' \
      --output text
  )
  [[ -n "${tunnel1:-}" && -n "${tunnel2:-}" ]] ||
    die "VPN #1 did not return two AWS tunnel endpoints"

  AWS_PROFILE="$PROFILE" aws cloudformation deploy \
    --region us-east-1 \
    --stack-name "$ONPREM_STACK" \
    --template-file "$ROOT/docs/iac/vpn1-onprem-us-east-1.yaml" \
    --parameter-overrides \
      KeyPairName="$USE1_KEYPAIR" \
      AdminCidr="$ADMIN_CIDR" \
      Vpn1Tunnel1Cidr="${tunnel1}/32" \
      Vpn1Tunnel2Cidr="${tunnel2}/32" \
      CreateCentralLogBucket=true \
    --tags Project=EG334S Environment=SCHOOL-DEMO ManagedBy=CloudFormation \
    --no-fail-on-empty-changeset

  echo "VPN #1 peer firewall updated for $tunnel1 and $tunnel2."
  echo "Next: retrieve the vendor configuration into a protected local file and configure strongSwan."
}

deploy_alb() {
  require_write_gate
  local vpc subnet route_table instance security_group
  vpc="$(output eu-west-1 "$HUB_STACK" Vpc2Id)"
  subnet="$(output eu-west-1 "$HUB_STACK" PublicSubnetId)"
  route_table="$(output eu-west-1 "$HUB_STACK" RouteTableId)"
  instance="$(output eu-west-1 "$HUB_STACK" PublicServerInstanceId)"
  security_group="$(output eu-west-1 "$HUB_STACK" PublicServerSecurityGroupId)"

  AWS_PROFILE="$PROFILE" aws cloudformation deploy \
    --region eu-west-1 \
    --stack-name "$ALB_STACK" \
    --template-file "$ROOT/docs/iac/hub-alb-eu-west-1.yaml" \
    --parameter-overrides \
      VpcId="$vpc" \
      PublicSubnetA="$subnet" \
      HubRouteTableId="$route_table" \
      PrimaryInstanceId="$instance" \
      HubSecurityGroupId="$security_group" \
      EnvironmentLabel=SCHOOL-DEMO \
    --tags Project=EG334S Environment=SCHOOL-DEMO ManagedBy=CloudFormation \
    --no-fail-on-empty-changeset
  echo "ALB URL: $(output eu-west-1 "$ALB_STACK" SiteUrl)"
}

deploy_recovery() {
  require_write_gate
  local hub_vpc primary_subnet recovery_subnet hub_sg primary_instance
  local primary_ip onprem_vpc topic target_group
  hub_vpc="$(output eu-west-1 "$HUB_STACK" Vpc2Id)"
  primary_subnet="$(output eu-west-1 "$HUB_STACK" PublicSubnetId)"
  recovery_subnet="$(output eu-west-1 "$ALB_STACK" SecondaryPublicSubnetId)"
  hub_sg="$(output eu-west-1 "$HUB_STACK" PublicServerSecurityGroupId)"
  primary_instance="$(output eu-west-1 "$HUB_STACK" PublicServerInstanceId)"
  primary_ip="$(output eu-west-1 "$HUB_STACK" PublicServerPrivateIp)"
  onprem_vpc="$(output us-east-1 "$ONPREM_STACK" Vpc1Id)"
  topic="$(output eu-west-1 "$HUB_STACK" MonitoringTopicArn)"
  target_group="$(output eu-west-1 "$ALB_STACK" TargetGroupArn)"

  AWS_PROFILE="$PROFILE" aws cloudformation deploy \
    --region eu-west-1 \
    --stack-name "$RECOVERY_STACK" \
    --template-file "$ROOT/docs/iac/hub-recovery-eu-west-1.yaml" \
    --parameter-overrides \
      HubVpcId="$hub_vpc" \
      PrimarySubnetId="$primary_subnet" \
      RecoverySubnetId="$recovery_subnet" \
      HubSecurityGroupId="$hub_sg" \
      PrimaryInstanceId="$primary_instance" \
      PrimaryPrivateIp="$primary_ip" \
      OnPremVpcId="$onprem_vpc" \
      KeyPairName="$EUW1_KEYPAIR" \
      MonitoringTopicArn="$topic" \
      AlbTargetGroupArn="$target_group" \
    --capabilities CAPABILITY_NAMED_IAM \
    --tags Project=EG334S Environment=SCHOOL-DEMO ManagedBy=CloudFormation \
    --no-fail-on-empty-changeset
}

status() {
  for item in \
    "us-east-1:$ONPREM_STACK" \
    "eu-west-1:$HUB_STACK" \
    "eu-west-1:$ALB_STACK" \
    "eu-west-1:$RECOVERY_STACK"
  do
    region="${item%%:*}"
    stack="${item#*:}"
    AWS_PROFILE="$PROFILE" aws cloudformation describe-stacks \
      --region "$region" --stack-name "$stack" \
      --query 'Stacks[0].[StackName,StackStatus,LastUpdatedTime]' \
      --output text 2>/dev/null || echo "$stack	NOT DEPLOYED"
  done
}

case "$ACTION" in
  plan)
    "$ROOT/scripts/validate-iac.sh"
    EG334S_AWS_PROFILE="$PROFILE" EG334S_ADMIN_CIDR="$ADMIN_CIDR" \
      "$ROOT/scripts/preflight-school-account.sh"
    echo
    echo "READ-ONLY PLAN COMPLETE. No cloud resources were changed."
    echo "Deployment order: onprem -> hub -> VPN1 firewall/strongSwan -> ALB -> optional recovery -> Azure VPN2 -> acceptance."
    ;;
  status) status ;;
  deploy-onprem) deploy_onprem ;;
  deploy-hub) deploy_hub ;;
  configure-vpn1-firewall) configure_vpn1_firewall ;;
  deploy-alb) deploy_alb ;;
  deploy-recovery) deploy_recovery ;;
  *)
    die "unknown action '$ACTION'"
    ;;
esac
