#!/usr/bin/env nu

# Multi-Provider Web App Deployment Script
# Orchestrates deployment across DigitalOcean, AWS, and Hetzner

def main [--debug: bool = false] {
  print "🚀 Multi-Provider Web App Deployment"
  print "────────────────────────────────────────"

  # Enable debug output if requested
  if $debug {
    print "✓ Debug mode enabled"
  }

  # Step 1: Validate configuration
  print "\n📋 Step 1: Validating configuration..."
  validate_environment

  # Step 2: Create networks first
  print "\n🌐 Step 2: Creating private networks..."
  create_digitalocean_vpc
  create_aws_vpc
  create_hetzner_network

  # Step 3: Create provider resources
  print "\n☁️  Step 3: Creating compute and database resources..."
  create_hetzner_resources
  create_aws_resources
  create_digitalocean_resources

  # Step 4: Setup VPN tunnels
  print "\n🔐 Step 4: Setting up VPN tunnels for secure communication..."
  setup_vpn_do_to_aws
  setup_vpn_aws_to_hetzner

  # Step 5: Configure security
  print "\n🔒 Step 5: Configuring security..."
  setup_firewall_rules
  setup_security_groups

  # Step 6: Deploy application
  print "\n📦 Step 6: Deploying application..."
  deploy_application

  # Step 7: Verify deployment
  print "\n✅ Step 7: Verifying deployment..."
  verify_deployment

  print "\n🎉 Deployment complete!"
  print "Your application is now live on three cloud providers."
}

def validate_environment [] {
  # Check required environment variables
  let required = [
    "DIGITALOCEAN_TOKEN",
    "AWS_ACCESS_KEY_ID",
    "AWS_SECRET_ACCESS_KEY",
    "HCLOUD_TOKEN"
  ]

  $required | each {|var|
    if ($env | has $var) {
      print $"  ✓ ($var) is set"
    } else {
      print $"  ✗ ($var) is not set"
      error make {msg: $"Missing required environment variable: ($var)"}
    }
  }

  # Verify CLI tools
  let tools = ["doctl", "aws", "hcloud", "nickel"]
  $tools | each {|tool|
    if (which $tool | is-not-empty) {
      print $"  ✓ ($tool) is installed"
    } else {
      print $"  ✗ ($tool) is not installed"
      error make {msg: $"Missing required tool: ($tool)"}
    }
  }

  # Validate Nickel configuration
  print "  Validating Nickel configuration..."
  let nickel_result = (do { nickel export workspace.ncl | from json } | complete)
  if $nickel_result.exit_code == 0 {
    print "  ✓ Nickel configuration is valid"
  } else {
    error make {msg: $"Nickel validation failed: ($nickel_result.stderr)"}
  }

  # Validate config.toml
  print "  Validating config.toml..."
  let config_result = (do { open config.toml } | complete)
  if $config_result.exit_code == 0 {
    print "  ✓ config.toml is valid"
  } else {
    error make {msg: $"config.toml validation failed: ($config_result.stderr)"}
  }
}

def create_hetzner_network [] {
  print "  Creating Hetzner private network..."

  let network = (hcloud network create \
    --name "backup-network" \
    --ip-range "10.2.0.0/16" \
    --format "json" | from json)

  print $"    ✓ Created network: ($network.network.id)"

  # Create subnet
  hcloud network add-subnet backup-network \
    --ip-range "10.2.1.0/24" \
    --network-zone "eu-central"

  print "    ✓ Created subnet: 10.2.1.0/24"
}

def create_hetzner_resources [] {
  print "  Creating Hetzner backup volume..."

  # Create volume attached to network
  let volume = (hcloud volume create \
    --name "webapp-backups" \
    --size 500 \
    --location "nbg1" \
    --format "ext4" | from json)

  print $"    ✓ Created volume: ($volume.id)"

  # Wait for volume to be ready
  let max_wait = 60
  mut attempts = 0

  while $attempts < $max_wait {
    let status = (hcloud volume describe "webapp-backups" --output json | from json)

    if $status.status == "available" {
      print "    ✓ Volume is available"
      break
    }

    sleep 1sec
    $attempts = ($attempts + 1)
  }

  if $attempts >= $max_wait {
    error make {msg: "Hetzner volume failed to become available"}
  }
}

def create_aws_vpc [] {
  print "  Creating AWS VPC..."

  let vpc = (aws ec2 create-vpc \
    --cidr-block "10.1.0.0/16" \
    --tag-specifications "ResourceType=vpc,Tags=[{Key=Name,Value=webapp-vpc}]" | from json)

  print $"    ✓ Created VPC: ($vpc.Vpc.VpcId)"

  # Create private subnet for database
  let subnet = (aws ec2 create-subnet \
    --vpc-id $vpc.Vpc.VpcId \
    --cidr-block "10.1.1.0/24" \
    --availability-zone "us-east-1a" | from json)

  print $"    ✓ Created private subnet: ($subnet.Subnet.SubnetId)"

  # Create security group for RDS
  let sg = (aws ec2 create-security-group \
    --group-name "webapp-db-sg" \
    --description "Security group for RDS database" \
    --vpc-id $vpc.Vpc.VpcId | from json)

  print $"    ✓ Created security group: ($sg.GroupId)"

  # Allow PostgreSQL from DO VPC (10.0.0.0/16)
  aws ec2 authorize-security-group-ingress \
    --group-id $sg.GroupId \
    --protocol tcp \
    --port 5432 \
    --cidr "10.0.0.0/16"

  print "    ✓ Allowed PostgreSQL from DO network (10.0.0.0/16)"
}

def create_aws_resources [] {
  print "  Creating AWS RDS PostgreSQL database..."

  # Create RDS instance in private subnet
  let db_response = (aws rds create-db-instance \
    --db-instance-identifier "webapp-db" \
    --db-instance-class "db.t3.medium" \
    --engine "postgres" \
    --engine-version "14.6" \
    --master-username "admin" \
    --master-user-password "YourSecurePassword123!" \
    --allocated-storage 100 \
    --storage-type "gp3" \
    --multi-az \
    --db-subnet-group-name "default" | from json)

  print $"    ✓ Created RDS instance: ($db_response.DBInstance.DBInstanceIdentifier)"

  # Wait for database to be available
  print "    Waiting for database to be available (this may take 10-15 minutes)..."
  let max_wait = 900  # 15 minutes

  mut attempts = 0
  while $attempts < $max_wait {
    let db_check = (do {
      aws rds describe-db-instances \
        --db-instance-identifier "webapp-db" | from json
    } | complete)

    if $db_check.exit_code == 0 {
      let db_info = ($db_check.stdout | from json)

      if ($db_info.DBInstances | first).DBInstanceStatus == "available" {
        print "    ✓ Database is available"
        let endpoint = ($db_info.DBInstances | first).Endpoint.Address
        print $"    ✓ Database endpoint: ($endpoint)"
        break
      }
    }

    sleep 10sec
    $attempts = ($attempts + 10)
  }

  if $attempts >= $max_wait {
    error make {msg: "AWS RDS failed to become available"}
  }
}

def create_digitalocean_vpc [] {
  print "  Creating DigitalOcean VPC..."

  let vpc = (doctl compute vpc create \
    --name "web-app-vpc" \
    --region "nyc3" \
    --ip-range "10.0.0.0/16" \
    --format ID \
    --no-header)

  print $"    ✓ Created VPC: ($vpc)"
}

def create_digitalocean_resources [] {
  print "  Creating DigitalOcean droplets and load balancer..."

  # Create droplets
  let ssh_keys = (doctl compute ssh-key list --no-header --format ID)

  if ($ssh_keys | is-empty) {
    error make {msg: "No SSH keys found in DigitalOcean. Please upload one first."}
  }

  let ssh_key_id = ($ssh_keys | first)

  # Create 3 web server droplets
  let droplet_ids = (
    1..3 | each {|i|
      let response = (doctl compute droplet create \
        $"web-server-($i)" \
        --region "nyc3" \
        --size "s-2vcpu-4gb" \
        --image "ubuntu-22-04-x64" \
        --ssh-keys $ssh_key_id \
        --enable-monitoring \
        --enable-backups \
        --format ID \
        --no-header | into string)

      print $"    ✓ Created droplet: web-server-($i)"
      $response
    }
  )

  # Wait for droplets to be ready
  print "    Waiting for droplets to be ready..."
  sleep 30sec

  # Verify droplets are running
  $droplet_ids | each {|id|
    let droplet = (doctl compute droplet get $id --format Status --no-header)
    if $droplet != "active" {
      error make {msg: $"Droplet ($id) failed to start"}
    }
  }

  print "    ✓ All droplets are active"

  # Create load balancer
  print "  Creating DigitalOcean load balancer..."
  let lb = (doctl compute load-balancer create \
    --name "web-lb" \
    --region "nyc3" \
    --forwarding-rules "entry_protocol:http,entry_port:80,target_protocol:http,target_port:80" \
    --format ID \
    --no-header | into string)

  print $"    ✓ Created load balancer: ($lb)"
}

def setup_vpn_do_to_aws [] {
  print "  Setting up VPN tunnel: DigitalOcean → AWS..."

  # Create VPN Gateway in AWS
  let vgw = (aws ec2 create-vpn-gateway \
    --type "ipsec.1" \
    --tag-specifications "ResourceType=vpn-gateway,Tags=[{Key=Name,Value=webapp-vpn-gw}]" | from json)

  print $"    ✓ Created VPN Gateway: ($vgw.VpnGateway.VpnGatewayId)"

  # Create Customer Gateway (DigitalOcean endpoint - use placeholder)
  let cgw = (aws ec2 create-customer-gateway \
    --type "ipsec.1" \
    --public-ip "203.0.113.12" \
    --tag-specifications "ResourceType=customer-gateway,Tags=[{Key=Name,Value=do-vpn-endpoint}]" | from json)

  print $"    ✓ Created Customer Gateway: ($cgw.CustomerGateway.CustomerGatewayId)"

  # Create VPN Connection
  let vpn = (aws ec2 create-vpn-connection \
    --type "ipsec.1" \
    --customer-gateway-id $cgw.CustomerGateway.CustomerGatewayId \
    --vpn-gateway-id $vgw.VpnGateway.VpnGatewayId | from json)

  print $"    ✓ Created VPN Connection: ($vpn.VpnConnection.VpnConnectionId)"

  print "    Note: Download VPN configuration and apply to DigitalOcean endpoint manually"
}

def setup_vpn_aws_to_hetzner [] {
  print "  Setting up VPN tunnel: AWS → Hetzner..."

  # For Hetzner, VPN is typically set up via StrongSwan or Wireguard on their servers
  print "    VPN tunnel to Hetzner requires manual configuration on Hetzner server"
  print "    Use SSH to configure StrongSwan or Wireguard based on network requirements"

  print "    Example configuration steps:"
  print "      1. SSH into Hetzner server"
  print "      2. Install StrongSwan: apt-get install strongswan"
  print "      3. Configure IPSec with AWS VPN gateway"
  print "      4. Enable IP forwarding: sysctl -w net.ipv4.ip_forward=1"
  print "      5. Verify connectivity: ping 10.1.1.10 (AWS RDS)"
}

def setup_firewall_rules [] {
  print "  Configuring firewall rules..."

  # Allow SSH, HTTP, HTTPS on DigitalOcean droplets
  print "    Configuring DigitalOcean firewall..."
  let fw = (doctl compute firewall create \
    --name "web-firewall" \
    --inbound-rules "protocol:tcp,ports:22,sources:addresses:0.0.0.0/0;sources:addresses::/0" \
    --inbound-rules "protocol:tcp,ports:80,sources:addresses:0.0.0.0/0;sources:addresses::/0" \
    --inbound-rules "protocol:tcp,ports:443,sources:addresses:0.0.0.0/0;sources:addresses::/0" \
    --format ID \
    --no-header | into string)

  print $"    ✓ Created firewall: ($fw)"
}

def setup_security_groups [] {
  print "  Configuring AWS security groups..."

  # Create security group for RDS
  let sg = (aws ec2 create-security-group \
    --group-name "webapp-db-sg" \
    --description "Security group for webapp RDS database" | from json)

  print $"    ✓ Created security group: ($sg.GroupId)"

  # Allow PostgreSQL from web tier CIDR
  aws ec2 authorize-security-group-ingress \
    --group-id $sg.GroupId \
    --protocol tcp \
    --port 5432 \
    --cidr 10.0.0.0/8

  print "    ✓ Configured PostgreSQL access"
}

def deploy_application [] {
  # Get web server IPs
  let droplets = (doctl compute droplet list --format Name,PublicIPv4 --no-header)

  $droplets | lines | each {|line|
    if ($line | is-not-empty) {
      let parts = ($line | split column --max-columns 2 " " | get column1)
      let ip = ($parts | last)
      let name = ($parts | first)

      print $"  Deploying to ($name) at ($ip)..."

      # Note: In production, you would:
      # 1. SSH into the server
      # 2. Clone application repository
      # 3. Install dependencies
      # 4. Configure environment variables
      # 5. Start application

      print $"    ✓ Ready for application deployment at ssh://root@($ip)"
    }
  }
}

def verify_deployment [] {
  print "  Verifying DigitalOcean droplets..."

  let droplets = (doctl compute droplet list --no-header --format ID,Name,Status)
  print $"    Found ($droplets | lines | length) droplets"

  print "  Verifying AWS RDS database..."
  let db_check = (do {
    aws rds describe-db-instances \
      --db-instance-identifier "webapp-db" | from json
  } | complete)

  if $db_check.exit_code == 0 {
    let db = ($db_check.stdout | from json)
    let endpoint = ($db.DBInstances | first).Endpoint.Address
    print $"    Database endpoint: ($endpoint)"
  } else {
    print "    Note: Database may still be initializing"
  }

  print "  Verifying Hetzner volumes..."
  let volumes = (hcloud volume list --format "ID Name Status" --no-header)
  print $"    Found ($volumes | lines | length) volumes"

  print "\n  Summary:"
  print "    ✓ DigitalOcean: 3 web servers + load balancer"
  print "    ✓ AWS: PostgreSQL database (Multi-AZ)"
  print "    ✓ Hetzner: Backup storage volume"
}

# Run main function
main --debug=$nu.env.DEBUG?
