#!/usr/bin/env bash
set -euo pipefail

# --- Configuration ---
readonly DEVELOPERS=("alice" "bob" "charlie")
readonly DEV_NAMESPACES=("dev-alice" "dev-bob" "dev-charlie")
readonly ALL_NAMESPACES=("${DEV_NAMESPACES[@]}" "dev-shared" "staging" "prod")
readonly TEST_LABEL="app=verification-test"

# --- Cleanup Function ---
cleanup() {
  echo "Cleaning up test resources..."
  for ns in "${ALL_NAMESPACES[@]}"; do
    kubectl delete namespace "$ns" --ignore-not-found=true --grace-period=0 --force &
  done
  wait # Wait for all background delete jobs to finish
  echo "Cleanup complete."
}
trap cleanup EXIT

# --- Verification Functions ---

# 1. Verify all namespaces exist in a single API call
verify_namespaces() {
  echo "Checking namespaces..."
  local existing_ns
  existing_ns=$(kubectl get namespace -o jsonpath='{.items[*].metadata.name}')
  for ns in "${ALL_NAMESPACES[@]}"; do
    if ! [[ "$existing_ns" =~ $ns ]]; then
      echo "Namespace '$ns' does not exist"
      exit 1
    fi
  done
  echo "All namespaces exist."
}

# 2. Verify service accounts exist
verify_service_accounts() {
  echo "Checking service accounts..."
  for dev in "${DEVELOPERS[@]}"; do
    local sa_name="${dev}-sa"
    local ns_name="dev-${dev}"
    if ! kubectl get serviceaccount "$sa_name" -n "$ns_name" &>/dev/null; then
      echo "ServiceAccount '$sa_name' does not exist in namespace '$ns_name'"
      exit 1
    fi
  done
  echo "All developer ServiceAccounts exist."
}

# 3. Verify RBAC permissions
verify_rbac() {
  echo "Testing RBAC permissions..."
  for dev in "${DEVELOPERS[@]}"; do
    local sa_user="system:serviceaccount:dev-${dev}:${dev}-sa"
    local own_ns="dev-${dev}"

    # Should have full access to own namespace
    if ! kubectl auth can-i create pods --as="$sa_user" -n "$own_ns" --quiet; then
      echo "$dev cannot create pods in their own namespace '$own_ns'"
      exit 1
    fi
    echo "  - $dev has write access to their own namespace"

    # Should have read access to dev-shared
    if ! kubectl auth can-i get pods --as="$sa_user" -n "dev-shared" --quiet; then
      echo "$dev cannot read pods in 'dev-shared' namespace"
      exit 1
    fi
    echo "  - $dev has read access to 'dev-shared'"

    # Should NOT have access to restricted namespaces
    for other_ns in "${ALL_NAMESPACES[@]}"; do
        # Skip check for their own namespace and the shared one
        if [[ "$other_ns" == "$own_ns" || "$other_ns" == "dev-shared" ]]; then
            continue
        fi

        if kubectl auth can-i get pods --as="$sa_user" -n "$other_ns" --quiet; then
            echo "$dev has unauthorized read access to '$other_ns' namespace"
            exit 1
        fi
    done
    echo "  - $dev is properly isolated from other dev, staging, and prod namespaces"
  done
  echo "RBAC permissions are correctly configured."
}

# 4. Verify Resource Quotas using precise jsonpath
verify_quotas() {
  echo "Checking resource quotas..."
  declare -A expected_quotas=(
      ["dev-alice"]="pods=10:services=5"
      ["dev-bob"]="pods=10:services=5"
      ["dev-charlie"]="pods=10:services=5"
      ["dev-shared"]="pods=20:services=10"
      ["staging"]="pods=50:services=20"
      ["prod"]="pods=50:services=20"
  )

  for ns in "${!expected_quotas[@]}"; do
    # First, find the name of the ResourceQuota object in the namespace.
    local quota_name
    quota_name=$(kubectl get resourcequota -n "$ns" -o jsonpath='{.items[0].metadata.name}')

    if [[ -z "$quota_name" ]]; then
      echo "No ResourceQuota object found in namespace '$ns'"
      exit 1
    fi

    # Parse expected values from the array
    local expected_values="${expected_quotas[$ns]}"
    local expected_pods=$(echo "$expected_values" | cut -d: -f1 | cut -d= -f2)
    local expected_services=$(echo "$expected_values" | cut -d: -f2 | cut -d= -f2)

    # Get both actual values in a single API call, separated by a space
    local actual_values
    actual_values=$(kubectl get resourcequota "$quota_name" -n "$ns" -o=jsonpath='{.spec.hard.pods}{" "}{.spec.hard.services}')
    
    # Read the space-separated output into variables
    local actual_pods actual_services
    read -r actual_pods actual_services <<< "$actual_values"

    # Check if either value does not match
    if [[ "$actual_pods" != "$expected_pods" || "$actual_services" != "$expected_services" ]]; then
      echo "ResourceQuota mismatch in namespace '$ns'."
      echo "  - Expected: pods=${expected_pods}, services=${expected_services}"
      echo "  - Found:    pods=${actual_pods}, services=${actual_services}"
      exit 1
    fi
  done
  echo "Resource quotas are correctly configured."
}

# 5. Verify Network Policies exist (without assuming a name)
verify_network_policies() {
  echo "Checking for existence of Network Policies..."
  for ns in "${ALL_NAMESPACES[@]}"; do
    # Get a count of NetworkPolicy objects in the namespace
    local policy_count
    policy_count=$(kubectl get networkpolicy -n "$ns" -o name | wc -l)
    
    if [[ "$policy_count" -eq 0 ]]; then
      echo "No NetworkPolicy objects found in namespace '$ns'. A default deny policy is likely missing."
      exit 1
    fi
  done
  echo "At least one NetworkPolicy exists in all namespaces."
}

# 6. Test network isolation with a functional test
test_network_isolation() {
  echo "Testing network isolation..."
  local manifest_yaml=""

  # Generate a single YAML manifest for all test resources
  for dev in "${DEVELOPERS[@]}"; do
    local ns="dev-${dev}"
    # The selector needs key: value format
    local selector_key="app"
    local selector_value="verification-test"

    manifest_yaml+=$(cat <<EOF
---
apiVersion: v1
kind: Pod
metadata:
  name: test-pod-${dev}
  namespace: $ns
  labels:
    ${selector_key}: ${selector_value}
spec:
  containers:
  - name: curl
    image: curlimages/curl:latest
    command: ["sleep", "3600"]
    resources:
        limits:
            cpu: "100m"
            memory: "128Mi"
        requests:
            cpu: "100m"
            memory: "128Mi"
---
apiVersion: v1
kind: Service
metadata:
  name: test-service-${dev}
  namespace: $ns
  labels:
    ${selector_key}: ${selector_value}
spec:
  selector:
    ${selector_key}: ${selector_value} # Use same labels for selector
  ports:
  - port: 80
EOF
)
  done

  echo "  - Creating test pods and services..."
  echo "$manifest_yaml" | kubectl apply -f -

  echo "  - Waiting for test pods to be ready..."
  for dev in "${DEVELOPERS[@]}"; do
    kubectl wait --for=condition=Ready pod/test-pod-${dev} -n "dev-${dev}" --timeout=60s
  done
  
  # Test that alice cannot reach bob's service
  echo "  - Testing cross-namespace isolation (alice -> bob)..."
  if kubectl exec -n dev-alice test-pod-alice -- curl -s --max-time 3 http://test-service-bob.dev-bob.svc.cluster.local &>/dev/null; then
    echo "Network policy FAILED: dev-alice can reach dev-bob's service"
    exit 1
  fi
  echo "  - Cross-namespace access is properly blocked."

  # Test that DNS is working
  echo "  - Testing DNS access..."
  if ! kubectl exec -n dev-alice test-pod-alice -- nslookup -timeout=3 kubernetes.default.svc.cluster.local &>/dev/null; then
    echo "DNS resolution FAILED (it should be allowed by network policies)"
    exit 1
  fi
  echo "  - DNS access is working correctly."
  echo "Network policies are functioning correctly."
}


# --- Main Execution ---
main() {
  echo "Starting comprehensive verification of dev cluster setup..."
  verify_namespaces
  verify_service_accounts
  verify_rbac
  verify_quotas
  verify_network_policies
  test_network_isolation
  echo "All verifications passed! Cluster setup is correctly configured."
}

main