#!/bin/bash
# Install Dex OIDC Identity Provider for Citadel
# Provides SSO/OIDC authentication for K8s API server
#
# What this installs:
#   - Dex OIDC provider (HA: 2 replicas, CRD storage)
#   - TLS certificate via cert-manager (internal-ca)
#   - RBAC bindings for OIDC groups
#   - K3s API server OIDC configuration
#
# Prerequisites:
#   - K3s running (scripts/01-install-k3s.sh)
#   - Helm installed (scripts/02-install-cilium.sh)
#   - cert-manager installed (scripts/09-install-cert-manager.sh)

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_DIR="$(dirname "${SCRIPT_DIR}")"

# shellcheck source=scripts/common.sh
source "${SCRIPT_DIR}/common.sh"

DEX_VERSION="0.18.0"
DEX_NAMESPACE="dex"
DEX_ISSUER_URL="https://dex.citadel.local"

log_info "Installing Dex OIDC Identity Provider for Citadel..."

# ----- Prerequisites -----
require_kubectl || exit 1
require_helm || exit 1

# Check cert-manager is installed
if ! kubectl get deployment cert-manager -n cert-manager &>/dev/null; then
    log_error "cert-manager not found. Run scripts/09-install-cert-manager.sh first."
    exit 1
fi

# ----- Step 1: Add Dex Helm repository -----
log_info "Step 1: Adding Dex Helm repository..."

if helm repo list 2>/dev/null | grep -q dex; then
    log_info "Dex repo already added, updating..."
    helm repo update dex
else
    helm repo add dex https://charts.dexidp.io
    helm repo update
fi

log_success "Helm repository ready"

# ----- Step 2: Create namespace -----
log_info "Step 2: Creating namespace ${DEX_NAMESPACE}..."

kubectl create namespace "${DEX_NAMESPACE}" --dry-run=client -o yaml | kubectl apply -f -

log_success "Namespace ${DEX_NAMESPACE} ready"

# ----- Step 3: Issue TLS certificate for Dex -----
log_info "Step 3: Issuing TLS certificate for Dex..."

kubectl apply -f - <<'CERT_EOF'
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: dex-tls
  namespace: dex
  labels:
    app.kubernetes.io/managed-by: citadel-operator
    app.kubernetes.io/part-of: citadel-sso
spec:
  secretName: dex-tls
  duration: 2160h    # 90 days
  renewBefore: 720h  # 30 days
  commonName: dex.citadel.local
  dnsNames:
    - dex.citadel.local
    - dex.dex.svc.cluster.local
    - dex.dex.svc
  issuerRef:
    name: internal-ca
    kind: ClusterIssuer
    group: cert-manager.io
  privateKey:
    algorithm: ECDSA
    size: 256
    rotationPolicy: Always
CERT_EOF

# Wait for certificate
log_info "Waiting for Dex TLS certificate to be ready..."
kubectl wait --for=condition=Ready certificate/dex-tls \
    -n "${DEX_NAMESPACE}" --timeout=120s 2>/dev/null || {
    log_warn "Dex TLS certificate not yet ready (may take a moment)"
    log_warn "Check status: kubectl describe certificate dex-tls -n ${DEX_NAMESPACE}"
}

log_success "TLS certificate issued"

# ----- Step 4: Install Dex -----
log_info "Step 4: Installing Dex via Helm..."

if helm list -n "${DEX_NAMESPACE}" 2>/dev/null | grep -q dex; then
    log_warn "Dex already installed. Upgrading..."
    helm upgrade dex dex/dex \
        --namespace "${DEX_NAMESPACE}" \
        --version "${DEX_VERSION}" \
        --values "${REPO_DIR}/security/sso/dex-install.yaml" \
        --wait --timeout 300s
else
    helm install dex dex/dex \
        --namespace "${DEX_NAMESPACE}" \
        --version "${DEX_VERSION}" \
        --values "${REPO_DIR}/security/sso/dex-install.yaml" \
        --wait --timeout 300s
fi

log_success "Dex Helm chart installed"

# ----- Step 5: Verify Dex pods -----
log_info "Step 5: Verifying Dex pods..."

kubectl rollout status deployment/dex -n "${DEX_NAMESPACE}" --timeout=120s

READY_PODS=$(kubectl get pods -n "${DEX_NAMESPACE}" -l app.kubernetes.io/name=dex \
    --field-selector=status.phase=Running -o name 2>/dev/null | wc -l | tr -d ' ')

if [ "${READY_PODS}" -ge 1 ]; then
    log_success "Dex is running (${READY_PODS} pod(s) ready)"
else
    log_error "No Dex pods are ready"
    log_error "  kubectl logs -n ${DEX_NAMESPACE} -l app.kubernetes.io/name=dex"
    exit 1
fi

# ----- Step 6: Apply RBAC bindings -----
log_info "Step 6: Applying OIDC RBAC bindings..."

kubectl apply -f "${REPO_DIR}/security/sso/rbac-oidc.yaml"

log_success "RBAC bindings applied"

# ----- Step 7: Configure K3s API server OIDC flags -----
log_info "Step 7: Configuring K3s API server OIDC flags..."

K3S_CONFIG="/etc/rancher/k3s/config.yaml"

if [ -f "${K3S_CONFIG}" ]; then
    # Check if OIDC is already configured
    if grep -q "oidc-issuer-url" "${K3S_CONFIG}" 2>/dev/null; then
        log_warn "OIDC flags already present in ${K3S_CONFIG}"
        log_warn "Verify they match the expected configuration:"
        log_warn "  oidc-issuer-url: ${DEX_ISSUER_URL}"
        log_warn "  oidc-client-id: kubernetes"
    else
        log_info "Adding OIDC flags to ${K3S_CONFIG}..."
        cat >> "${K3S_CONFIG}" <<OIDC_EOF

# OIDC Authentication via Dex (added by 10-install-dex.sh)
kube-apiserver-arg:
  - "oidc-issuer-url=${DEX_ISSUER_URL}"
  - "oidc-client-id=kubernetes"
  - "oidc-username-claim=email"
  - "oidc-groups-claim=groups"
  - "oidc-groups-prefix=oidc:"
  - "oidc-username-prefix=oidc:"
OIDC_EOF
        log_success "OIDC flags added to ${K3S_CONFIG}"
        log_warn "K3s server restart required for OIDC changes to take effect:"
        log_warn "  sudo systemctl restart k3s"
    fi
else
    log_warn "K3s config file not found at ${K3S_CONFIG}"
    log_warn "Add the following flags manually to your K3s server configuration:"
    log_warn "  --kube-apiserver-arg=oidc-issuer-url=${DEX_ISSUER_URL}"
    log_warn "  --kube-apiserver-arg=oidc-client-id=kubernetes"
    log_warn "  --kube-apiserver-arg=oidc-username-claim=email"
    log_warn "  --kube-apiserver-arg=oidc-groups-claim=groups"
    log_warn "  --kube-apiserver-arg=oidc-groups-prefix=oidc:"
    log_warn "  --kube-apiserver-arg=oidc-username-prefix=oidc:"
fi

# ----- Step 8: Verify setup -----
log_info "Step 8: Verifying SSO setup..."

# Verify Dex is responding
DEX_POD=$(kubectl get pods -n "${DEX_NAMESPACE}" -l app.kubernetes.io/name=dex \
    -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)

if [ -n "${DEX_POD}" ]; then
    # Check the Dex health endpoint from within the cluster
    if kubectl exec -n "${DEX_NAMESPACE}" "${DEX_POD}" -- \
        wget -q --spider --no-check-certificate \
        "https://localhost:5556/healthz/live" 2>/dev/null; then
        log_success "Dex health check passed"
    else
        log_warn "Dex health check via wget failed (may need curl or different check)"
    fi
fi

# Verify RBAC bindings
RBAC_COUNT=$(kubectl get clusterrolebindings -l app.kubernetes.io/managed-by=citadel-operator \
    -l citadel.dev/oidc-group -o name 2>/dev/null | wc -l | tr -d ' ')
log_info "OIDC ClusterRoleBindings: ${RBAC_COUNT}"

# Verify custom ClusterRoles
for role in citadel-operator citadel-developer; do
    if kubectl get clusterrole "${role}" &>/dev/null; then
        log_success "ClusterRole '${role}' exists"
    else
        log_error "ClusterRole '${role}' not found"
    fi
done

# ----- Summary -----
log_info ""
log_info "=========================================="
log_info "Dex SSO Installation Complete"
log_info "=========================================="
log_info ""
log_info "Components:"
log_info "  Dex OIDC Provider:   Running (HA: 2 replicas)"
log_info "  TLS Certificate:     Issued by internal-ca"
log_info "  Issuer URL:          ${DEX_ISSUER_URL}"
log_info ""
log_info "RBAC Bindings:"
log_info "  platform-admins  →  cluster-admin (cluster-wide)"
log_info "  platform-ops     →  citadel-operator (cluster-wide)"
log_info "  developers       →  citadel-developer (namespace: default)"
log_info "  viewers          →  view (cluster-wide)"
log_info ""
log_info "Clients:"
log_info "  kubernetes         Public client (API server token validation)"
log_info "  citadel-dashboard    Authorization code flow + PKCE"
log_info "  citadel-cli          Device code flow (headless/SSH)"
log_info ""
log_info "Next steps:"
log_info "  1. Configure upstream connectors in security/sso/dex-connectors.yaml"
log_info "  2. Replace placeholder secrets in security/sso/dex-clients.yaml"
log_info "  3. Restart K3s to apply OIDC flags: sudo systemctl restart k3s"
log_info "  4. Test with: kubectl --token=<oidc-token> get pods"
log_info ""
log_info "Documentation: security/sso/README.md"
log_info "=========================================="
